CalendarAppWidgetService.java revision 210642aa1f8bec18f7275cddb9a3b50fad3f4218
1bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang/* 2bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * Copyright (C) 2009 The Android Open Source Project 3bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * 4bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * Licensed under the Apache License, Version 2.0 (the "License"); 5bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * you may not use this file except in compliance with the License. 6bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * You may obtain a copy of the License at 7bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * 8bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * http://www.apache.org/licenses/LICENSE-2.0 9bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * 10bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * Unless required by applicable law or agreed to in writing, software 11bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * distributed under the License is distributed on an "AS IS" BASIS, 12bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * See the License for the specific language governing permissions and 14bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * limitations under the License. 15bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang */ 16bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 179a3cb14e28536e4133dddbe952f47189fe344ec1Mason Tangpackage com.android.calendar.widget; 18bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 194143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport com.android.calendar.R; 204143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport com.android.calendar.Utils; 214143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport com.android.calendar.widget.CalendarAppWidgetModel.DayInfo; 224143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport com.android.calendar.widget.CalendarAppWidgetModel.EventInfo; 234143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawaimport com.android.calendar.widget.CalendarAppWidgetModel.RowInfo; 244143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawa 25a38da7d3d03e1a1151d199dbac0a00ff2ff75a89RoboErikimport android.app.AlarmManager; 26a38da7d3d03e1a1151d199dbac0a00ff2ff75a89RoboErikimport android.app.PendingIntent; 27c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.appwidget.AppWidgetManager; 28c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.content.BroadcastReceiver; 29bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.content.ContentResolver; 30bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.content.Context; 31c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.content.CursorLoader; 32bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.content.Intent; 33c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.content.Loader; 34a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawaimport android.content.res.Resources; 35bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.database.Cursor; 3647d40324272ae39af0872bf5cbf27e1800478021Mason Tangimport android.database.MatrixCursor; 37bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.net.Uri; 38c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErikimport android.os.Handler; 39a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Attendees; 40a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Calendars; 4191a0898ea5a25f07f65955245d2ba12ae7238608Michael Chanimport android.provider.CalendarContract.EventsEntity; 42a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Instances; 43bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.text.format.DateUtils; 44bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.text.format.Time; 45bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.util.Log; 46bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.view.View; 47bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.widget.RemoteViews; 4847d40324272ae39af0872bf5cbf27e1800478021Mason Tangimport android.widget.RemoteViewsService; 4947d40324272ae39af0872bf5cbf27e1800478021Mason Tang 50bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 5147d40324272ae39af0872bf5cbf27e1800478021Mason Tangpublic class CalendarAppWidgetService extends RemoteViewsService { 520c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa private static final String TAG = "CalendarWidget"; 53bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 543ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang static final int EVENT_MIN_COUNT = 20; 553ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang static final int EVENT_MAX_COUNT = 503; 56c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik // Minimum delay between queries on the database for widget updates in ms 57c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik static final int WIDGET_UPDATE_THROTTLE = 500; 5847d40324272ae39af0872bf5cbf27e1800478021Mason Tang 59bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, " 60bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " 613ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT; 62bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 6391a0898ea5a25f07f65955245d2ba12ae7238608Michael Chan private static final String EVENT_SELECTION = Calendars.VISIBLE + "=1 AND " 6491a0898ea5a25f07f65955245d2ba12ae7238608Michael Chan + EventsEntity.ALL_DAY + "=0"; 6542dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik private static final String EVENT_SELECTION_HIDE_DECLINED = Calendars.VISIBLE + "=1 AND " 66f3ae356b004095319457a43d1d68aa4d0eb73e6fMichael Chan + EventsEntity.ALL_DAY + "=0 AND " 67bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED; 68bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 69bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang static final String[] EVENT_PROJECTION = new String[] { 70bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang Instances.ALL_DAY, 71bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang Instances.BEGIN, 72bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang Instances.END, 73bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang Instances.TITLE, 74bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang Instances.EVENT_LOCATION, 75bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang Instances.EVENT_ID, 763ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang Instances.START_DAY, 77a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa Instances.END_DAY, 7842dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik Instances.CALENDAR_COLOR, 7942dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik Instances.SELF_ATTENDEE_STATUS, 80bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang }; 81bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 82bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang static final int INDEX_ALL_DAY = 0; 83bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang static final int INDEX_BEGIN = 1; 84bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang static final int INDEX_END = 2; 85bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang static final int INDEX_TITLE = 3; 86bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang static final int INDEX_EVENT_LOCATION = 4; 87bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang static final int INDEX_EVENT_ID = 5; 883ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang static final int INDEX_START_DAY = 6; 893ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang static final int INDEX_END_DAY = 7; 90a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa static final int INDEX_COLOR = 8; 9142dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik static final int INDEX_SELF_ATTENDEE_STATUS = 9; 92bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 933ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang static final int MAX_DAYS = 7; 943ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 953ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang private static final long SEARCH_DURATION = MAX_DAYS * DateUtils.DAY_IN_MILLIS; 96bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 970c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa /** 980c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa * Update interval used when no next-update calculated, or bad trigger time in past. 990c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa * Unit: milliseconds. 1000c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa */ 1010c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa private static final long UPDATE_TIME_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6; 102bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 10347d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 10447d40324272ae39af0872bf5cbf27e1800478021Mason Tang public RemoteViewsFactory onGetViewFactory(Intent intent) { 10547d40324272ae39af0872bf5cbf27e1800478021Mason Tang return new CalendarFactory(getApplicationContext(), intent); 106bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 107bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 108f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik public static class CalendarFactory extends BroadcastReceiver implements 109c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik RemoteViewsService.RemoteViewsFactory, Loader.OnLoadCompleteListener<Cursor> { 110f9df037f350fad73659307ba05f230d2db69051aMason Tang private static final boolean LOGD = false; 11142dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik private static final int DECLINED_EVENT_ALPHA = 0x66000000; 1120c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa 1130c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa // Suppress unnecessary logging about update time. Need to be static as this object is 1140c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa // re-instanciated frequently. 1150c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa // TODO: It seems loadData() is called via onCreate() four times, which should mean 1160c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa // unnecessary CalendarFactory object is created and dropped. It is not efficient. 1170c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa private static long sLastUpdateTime = UPDATE_TIME_NO_EVENTS; 1180c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa 11947d40324272ae39af0872bf5cbf27e1800478021Mason Tang private Context mContext; 120a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa private Resources mResources; 121f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik private static CalendarAppWidgetModel mModel; 122f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik private static Cursor mCursor; 123f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik private static volatile Integer mLock = new Integer(0); 124f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik private int mLastLock; 125c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private CursorLoader mLoader; 126c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private Handler mHandler = new Handler(); 127c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private int mAppWidgetId; 12842dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik private int mDeclinedColor; 12942dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik private int mStandardColor; 130c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 131c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private Runnable mTimezoneChanged = new Runnable() { 132c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik @Override 133c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public void run() { 134c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mLoader != null) { 135c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mLoader.forceLoad(); 136c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 137c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 138c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik }; 139c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 140c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private Runnable mUpdateLoader = new Runnable() { 141c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik @Override 142c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public void run() { 143c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (mLoader != null) { 144c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik Uri uri = createLoaderUri(); 145c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mLoader.setUri(uri); 14642dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik String selection = Utils.getHideDeclinedEvents(mContext) ? 14742dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik EVENT_SELECTION_HIDE_DECLINED : EVENT_SELECTION; 14842dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik mLoader.setSelection(selection); 149f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik synchronized (mLock) { 150f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mLastLock = ++mLock; 151f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 152c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mLoader.forceLoad(); 153c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 154c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 155c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik }; 15647d40324272ae39af0872bf5cbf27e1800478021Mason Tang 15747d40324272ae39af0872bf5cbf27e1800478021Mason Tang protected CalendarFactory(Context context, Intent intent) { 15847d40324272ae39af0872bf5cbf27e1800478021Mason Tang mContext = context; 159a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa mResources = context.getResources(); 160c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mAppWidgetId = intent.getIntExtra( 161c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); 16242dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik 16395d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color); 16495d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color); 165bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 166bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 167f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik public CalendarFactory() { 168f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // This is being created as part of onReceive 169f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik 170f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 171f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik 17247d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 17347d40324272ae39af0872bf5cbf27e1800478021Mason Tang public void onCreate() { 174c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik initLoader(); 175bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 176bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 17747d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 178d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung public void onDataSetChanged() { 179d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung } 180d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung 181d4b2dbb63039e8e63ef92b4b984aae9c68e1a3b5Winson Chung @Override 18247d40324272ae39af0872bf5cbf27e1800478021Mason Tang public void onDestroy() { 183673304efa38aa2a3e65e0820e5c70d3a4889c34fIsaac Katzenelson if (mCursor != null) { 184673304efa38aa2a3e65e0820e5c70d3a4889c34fIsaac Katzenelson mCursor.close(); 185673304efa38aa2a3e65e0820e5c70d3a4889c34fIsaac Katzenelson } 186673304efa38aa2a3e65e0820e5c70d3a4889c34fIsaac Katzenelson if (mLoader != null) { 187673304efa38aa2a3e65e0820e5c70d3a4889c34fIsaac Katzenelson mLoader.reset(); 188673304efa38aa2a3e65e0820e5c70d3a4889c34fIsaac Katzenelson } 189bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 190bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 19147d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 19247d40324272ae39af0872bf5cbf27e1800478021Mason Tang public RemoteViews getLoadingView() { 19347d40324272ae39af0872bf5cbf27e1800478021Mason Tang RemoteViews views = new RemoteViews(mContext.getPackageName(), 19447d40324272ae39af0872bf5cbf27e1800478021Mason Tang R.layout.appwidget_loading); 19547d40324272ae39af0872bf5cbf27e1800478021Mason Tang return views; 196bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 197bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 19847d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 19947d40324272ae39af0872bf5cbf27e1800478021Mason Tang public RemoteViews getViewAt(int position) { 20047d40324272ae39af0872bf5cbf27e1800478021Mason Tang // we use getCount here so that it doesn't return null when empty 20147d40324272ae39af0872bf5cbf27e1800478021Mason Tang if (position < 0 || position >= getCount()) { 20247d40324272ae39af0872bf5cbf27e1800478021Mason Tang return null; 20347d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 204bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 205254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik if (mModel == null) { 206254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik RemoteViews views = new RemoteViews(mContext.getPackageName(), 207254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik R.layout.appwidget_loading); 20807d9fee87bc02849c0dd7b66326a320d53cf5479Michael Chan final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, 20907d9fee87bc02849c0dd7b66326a320d53cf5479Michael Chan 0, 0); 210254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik views.setOnClickFillInIntent(R.id.appwidget_loading, intent); 211254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik return views; 212254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik 213254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik } 214254b13ee080e3ce69cabf7207a9bfb95c965a5daRoboErik if (mModel.mEventInfos.isEmpty() || mModel.mRowInfos.isEmpty()) { 2153ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang RemoteViews views = new RemoteViews(mContext.getPackageName(), 2163ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang R.layout.appwidget_no_events); 21707d9fee87bc02849c0dd7b66326a320d53cf5479Michael Chan final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, 21807d9fee87bc02849c0dd7b66326a320d53cf5479Michael Chan 0, 0); 2194143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawa views.setOnClickFillInIntent(R.id.appwidget_no_events, intent); 2203ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang return views; 2213ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang } 2223ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 2233ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang RowInfo rowInfo = mModel.mRowInfos.get(position); 2243ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang if (rowInfo.mType == RowInfo.TYPE_DAY) { 2253ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang RemoteViews views = new RemoteViews(mContext.getPackageName(), 2263ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang R.layout.appwidget_day); 2273ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang DayInfo dayInfo = mModel.mDayInfos.get(rowInfo.mIndex); 2283ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang updateTextView(views, R.id.date, View.VISIBLE, dayInfo.mDayLabel); 2293ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang return views; 2303ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang } else { 23142dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik RemoteViews views; 232a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa final EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); 23342dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik if (eventInfo.allDay) { 23442dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views = new RemoteViews(mContext.getPackageName(), 23542dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik R.layout.widget_all_day_item); 23642dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik } else { 23742dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views = new RemoteViews(mContext.getPackageName(), R.layout.widget_item); 23842dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik } 2394acb2fd087308dea146b8b10f5278c59df387680RoboErik int displayColor = Utils.getDisplayColorFromColor(eventInfo.color); 240a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa 241a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa final long now = System.currentTimeMillis(); 242a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa if (!eventInfo.allDay && eventInfo.start <= now && now <= eventInfo.end) { 24342dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.widget_row, "setBackgroundColor", 244a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa mResources.getColor(R.color.appwidget_row_in_progress)); 245ffaeace621183dfe8770471a30b2f1138aac5f86Daisuke Miyakawa } else { 24642dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.widget_row, "setBackgroundColor", 0); 247a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa } 248a07cedd5af736339a7dd6166970311c487366500Daisuke Miyakawa 249a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa updateTextView(views, R.id.when, eventInfo.visibWhen, eventInfo.when); 250a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa updateTextView(views, R.id.where, eventInfo.visibWhere, eventInfo.where); 251a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa updateTextView(views, R.id.title, eventInfo.visibTitle, eventInfo.title); 252bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 25342dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setViewVisibility(R.id.agenda_item_color, View.VISIBLE); 25442dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik 25542dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik int selfAttendeeStatus = eventInfo.selfAttendeeStatus; 25642dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { 25742dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.title, "setTextColor", mDeclinedColor); 25842dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.when, "setTextColor", mDeclinedColor); 25942dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.where, "setTextColor", mDeclinedColor); 26042dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik // views.setInt(R.id.agenda_item_color, "setDrawStyle", 26142dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik // ColorChipView.DRAW_CROSS_HATCHED); 26242dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.agenda_item_color, "setImageResource", 263aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan R.drawable.widget_chip_responded_bg); 26442dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik // 40% opacity 26542dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.agenda_item_color, "setColorFilter", 266210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik Utils.getDeclinedColorFromColor(displayColor)); 26742dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik } else { 26842dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.title, "setTextColor", mStandardColor); 26942dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.when, "setTextColor", mStandardColor); 27042dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.where, "setTextColor", mStandardColor); 27142dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { 27242dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.agenda_item_color, "setImageResource", 27342dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik R.drawable.widget_chip_not_responded_bg); 27442dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik } else { 27542dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setInt(R.id.agenda_item_color, "setImageResource", 27642dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik R.drawable.widget_chip_responded_bg); 27742dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik } 2784acb2fd087308dea146b8b10f5278c59df387680RoboErik views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); 27942dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik } 280bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 2819da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik long start = eventInfo.start; 2829da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik long end = eventInfo.end; 2834143cfabb36f0c3adcad410a6eed29bc2b89d6e3Daisuke Miyakawa // An element in ListView. 2849da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik if (eventInfo.allDay) { 2859da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik String tz = Utils.getTimeZone(mContext, null); 2869da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik Time recycle = new Time(); 2879da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik start = Utils.convertAlldayLocalToUTC(recycle, start, tz); 2889da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik end = Utils.convertAlldayLocalToUTC(recycle, end, tz); 2899da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik } 290bdbf15078ad5efdf27c021d7aca8c8aa4693878cMichael Chan final Intent fillInIntent = CalendarAppWidgetProvider.getLaunchFillInIntent( 29107d9fee87bc02849c0dd7b66326a320d53cf5479Michael Chan mContext, eventInfo.id, start, end); 29242dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik views.setOnClickFillInIntent(R.id.widget_row, fillInIntent); 29347d40324272ae39af0872bf5cbf27e1800478021Mason Tang return views; 29447d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 295bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 296bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 29747d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 29847d40324272ae39af0872bf5cbf27e1800478021Mason Tang public int getViewTypeCount() { 2993ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang return 4; 30047d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 301bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 30247d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 30347d40324272ae39af0872bf5cbf27e1800478021Mason Tang public int getCount() { 30447d40324272ae39af0872bf5cbf27e1800478021Mason Tang // if there are no events, we still return 1 to represent the "no 30547d40324272ae39af0872bf5cbf27e1800478021Mason Tang // events" view 3064299eb1c1d81042de46a8d3972fde8719820d06cRoboErik if (mModel == null) { 3074299eb1c1d81042de46a8d3972fde8719820d06cRoboErik return 1; 3084299eb1c1d81042de46a8d3972fde8719820d06cRoboErik } 3093ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang return Math.max(1, mModel.mRowInfos.size()); 31047d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 311bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 31247d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 31347d40324272ae39af0872bf5cbf27e1800478021Mason Tang public long getItemId(int position) { 314673304efa38aa2a3e65e0820e5c70d3a4889c34fIsaac Katzenelson if (mModel == null || mModel.mRowInfos.isEmpty()) { 315713011baae993d4ca07a8267e3a0f9945753c2ffRoboErik return 0; 316713011baae993d4ca07a8267e3a0f9945753c2ffRoboErik } 317c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik RowInfo rowInfo = mModel.mRowInfos.get(position); 318c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (rowInfo.mType == RowInfo.TYPE_DAY) { 319c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return rowInfo.mIndex; 320c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 321c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); 322c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik long prime = 31; 323c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik long result = 1; 324c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik result = prime * result + (int) (eventInfo.id ^ (eventInfo.id >>> 32)); 325c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik result = prime * result + (int) (eventInfo.start ^ (eventInfo.start >>> 32)); 326c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return result; 32747d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 328bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 32947d40324272ae39af0872bf5cbf27e1800478021Mason Tang @Override 33047d40324272ae39af0872bf5cbf27e1800478021Mason Tang public boolean hasStableIds() { 33147d40324272ae39af0872bf5cbf27e1800478021Mason Tang return true; 332bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 333bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 33447d40324272ae39af0872bf5cbf27e1800478021Mason Tang /** 335c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * Query across all calendars for upcoming event instances from now 336c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * until some time in the future. Widen the time range that we query by 337c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * one day on each end so that we can catch all-day events. All-day 338c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * events are stored starting at midnight in UTC but should be included 339c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * in the list of events starting at midnight local time. This may fetch 340c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * more events than we actually want, so we filter them out later. 34147d40324272ae39af0872bf5cbf27e1800478021Mason Tang * 34247d40324272ae39af0872bf5cbf27e1800478021Mason Tang * @param resolver {@link ContentResolver} to use when querying 34347d40324272ae39af0872bf5cbf27e1800478021Mason Tang * {@link Instances#CONTENT_URI}. 34447d40324272ae39af0872bf5cbf27e1800478021Mason Tang * @param searchDuration Distance into the future to look for event 34547d40324272ae39af0872bf5cbf27e1800478021Mason Tang * instances, in milliseconds. 34647d40324272ae39af0872bf5cbf27e1800478021Mason Tang * @param now Current system time to use for this update, possibly from 34747d40324272ae39af0872bf5cbf27e1800478021Mason Tang * {@link System#currentTimeMillis()}. 34847d40324272ae39af0872bf5cbf27e1800478021Mason Tang */ 349c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public void initLoader() { 350c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik if (LOGD) 351c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik Log.d(TAG, "Querying for widget events..."); 352c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 35347d40324272ae39af0872bf5cbf27e1800478021Mason Tang // Search for events from now until some time in the future 354c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik Uri uri = createLoaderUri(); 35542dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik String selection = Utils.getHideDeclinedEvents(mContext) ? EVENT_SELECTION_HIDE_DECLINED 35642dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik : EVENT_SELECTION; 35742dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik mLoader = new CursorLoader(mContext, uri, EVENT_PROJECTION, selection, null, 35842dabd1e1e89b0d4af5afb776c6ecc53f6ae9fbaRoboErik EVENT_SORT_ORDER); 359c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mLoader.setUpdateThrottle(WIDGET_UPDATE_THROTTLE); 360f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik synchronized (mLock) { 361f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mLastLock = ++mLock; 362f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 363c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mLoader.startLoading(); 364c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik mLoader.registerListener(mAppWidgetId, this); 365c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 366c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 367bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 368c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik /** 369c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * @return The uri for the loader 370c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik */ 371c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik private Uri createLoaderUri() { 372c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik long now = System.currentTimeMillis(); 37347d40324272ae39af0872bf5cbf27e1800478021Mason Tang // Add a day on either side to catch all-day events 37447d40324272ae39af0872bf5cbf27e1800478021Mason Tang long begin = now - DateUtils.DAY_IN_MILLIS; 375c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik long end = now + SEARCH_DURATION + DateUtils.DAY_IN_MILLIS; 376bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 377e0cb5ba884c52e9d36875fb4a9ebdf40a81cb642Michael Chan Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI, Long.toString(begin) + "/" + end); 378c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik return uri; 37921a183875fbbfa54f5a2a87779888a5fb7d1af44Erik } 38021a183875fbbfa54f5a2a87779888a5fb7d1af44Erik 3813f888688c0f2644ad3de032d5d1cf623a7b092fdAndy McFadden /* @VisibleForTesting */ 38247d40324272ae39af0872bf5cbf27e1800478021Mason Tang protected static CalendarAppWidgetModel buildAppWidgetModel( 38321a183875fbbfa54f5a2a87779888a5fb7d1af44Erik Context context, Cursor cursor, String timeZone) { 384ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan CalendarAppWidgetModel model = new CalendarAppWidgetModel(context, timeZone); 38521a183875fbbfa54f5a2a87779888a5fb7d1af44Erik model.buildFromCursor(cursor, timeZone); 38647d40324272ae39af0872bf5cbf27e1800478021Mason Tang return model; 387bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 388bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 38947d40324272ae39af0872bf5cbf27e1800478021Mason Tang /** 3900c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa * Calculates and returns the next time we should push widget updates. 39147d40324272ae39af0872bf5cbf27e1800478021Mason Tang */ 392ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan private long calculateUpdateTime(CalendarAppWidgetModel model, long now, String timeZone) { 3930c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa // Make sure an update happens at midnight or earlier 394ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan long minUpdateTime = getNextMidnightTimeMillis(timeZone); 3950c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa for (EventInfo event : model.mEventInfos) { 3960c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa final long start; 3970c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa final long end; 3989da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik start = event.start; 3999da910f65de0a325d4268d88f0cf2e2fd653f24aRoboErik end = event.end; 400bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 4010c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa // We want to update widget when we enter/exit time range of an event. 4020c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa if (now < start) { 4030c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa minUpdateTime = Math.min(minUpdateTime, start); 4040c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa } else if (now < end) { 4050c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa minUpdateTime = Math.min(minUpdateTime, end); 4060c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa } 407bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 4080c715c837a7ecc2cfda3a62d952f8dc7e79a39f3Daisuke Miyakawa return minUpdateTime; 409bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 410bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 411ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan private static long getNextMidnightTimeMillis(String timezone) { 41247d40324272ae39af0872bf5cbf27e1800478021Mason Tang Time time = new Time(); 41347d40324272ae39af0872bf5cbf27e1800478021Mason Tang time.setToNow(); 41447d40324272ae39af0872bf5cbf27e1800478021Mason Tang time.monthDay++; 41547d40324272ae39af0872bf5cbf27e1800478021Mason Tang time.hour = 0; 41647d40324272ae39af0872bf5cbf27e1800478021Mason Tang time.minute = 0; 41747d40324272ae39af0872bf5cbf27e1800478021Mason Tang time.second = 0; 418ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan long midnightDeviceTz = time.normalize(true); 419ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan 420ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan time.timezone = timezone; 421ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan time.setToNow(); 422ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan time.monthDay++; 423ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan time.hour = 0; 424ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan time.minute = 0; 425ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan time.second = 0; 426ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan long midnightHomeTz = time.normalize(true); 427ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan 428ece2fbd8c2695910148ffa20fb46a508443fd034Michael Chan return Math.min(midnightDeviceTz, midnightHomeTz); 429bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 430bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 43147d40324272ae39af0872bf5cbf27e1800478021Mason Tang static void updateTextView(RemoteViews views, int id, int visibility, String string) { 43247d40324272ae39af0872bf5cbf27e1800478021Mason Tang views.setViewVisibility(id, visibility); 43347d40324272ae39af0872bf5cbf27e1800478021Mason Tang if (visibility == View.VISIBLE) { 43447d40324272ae39af0872bf5cbf27e1800478021Mason Tang views.setTextViewText(id, string); 43547d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 43647d40324272ae39af0872bf5cbf27e1800478021Mason Tang } 437c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 438c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik /* 439c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * (non-Javadoc) 440c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * @see 441c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * android.content.Loader.OnLoadCompleteListener#onLoadComplete(android 442c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik * .content.Loader, java.lang.Object) 443c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik */ 444c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik @Override 445c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) { 44682a8afab75ee998fcc90a4bcbc62f4912bc582adRoboErik if (cursor == null) { 44782a8afab75ee998fcc90a4bcbc62f4912bc582adRoboErik return; 44882a8afab75ee998fcc90a4bcbc62f4912bc582adRoboErik } 449f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // If a newer update has happened since we started clean up and 450f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // return 451f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik synchronized (mLock) { 452f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (mLastLock != mLock) { 453f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik cursor.close(); 454f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik return; 455f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 456f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // Copy it to a local static cursor. 457f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); 458f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik cursor.close(); 459c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 460f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik final long now = System.currentTimeMillis(); 461f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (mCursor != null) { 462f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mCursor.close(); 463f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 464f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mCursor = matrixCursor; 465f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik String tz = Utils.getTimeZone(mContext, mTimezoneChanged); 466f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mModel = buildAppWidgetModel(mContext, mCursor, tz); 467f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik 468f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // Schedule an alarm to wake ourselves up for the next update. 469f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // We also cancel 470f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // all existing wake-ups because PendingIntents don't match 471f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // against extras. 472f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik long triggerTime = calculateUpdateTime(mModel, now, tz); 473f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik 474f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // If no next-update calculated, or bad trigger time in past, 475f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // schedule 476f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik // update about six hours from now. 477f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (triggerTime < now) { 478f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik Log.w(TAG, "Encountered bad trigger time " + formatDebugTime(triggerTime, now)); 479f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik triggerTime = now + UPDATE_TIME_NO_EVENTS; 480f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 481c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 482f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik final AlarmManager alertManager = (AlarmManager) mContext 483f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik .getSystemService(Context.ALARM_SERVICE); 484f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik final PendingIntent pendingUpdate = CalendarAppWidgetProvider 485f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik .getUpdateIntent(mContext); 486f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik 487f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik alertManager.cancel(pendingUpdate); 488f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik alertManager.set(AlarmManager.RTC, triggerTime, pendingUpdate); 489f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik Time time = new Time(Utils.getTimeZone(mContext, null)); 490f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik time.setToNow(); 491f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik 492f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (time.normalize(true) != sLastUpdateTime) { 493f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik Time time2 = new Time(Utils.getTimeZone(mContext, null)); 494f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik time2.set(sLastUpdateTime); 495f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik time2.normalize(true); 496f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (time.year != time2.year || time.yearDay != time2.yearDay) { 497f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik final Intent updateIntent = new Intent( 498f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik Utils.getWidgetUpdateAction(mContext)); 499f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mContext.sendBroadcast(updateIntent); 500f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 501a38da7d3d03e1a1151d199dbac0a00ff2ff75a89RoboErik 502f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik sLastUpdateTime = time.toMillis(true); 503f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 504a38da7d3d03e1a1151d199dbac0a00ff2ff75a89RoboErik 505f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext); 506f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (mAppWidgetId == -1) { 507f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik int[] ids = widgetManager.getAppWidgetIds(CalendarAppWidgetProvider 508f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik .getComponentName(mContext)); 509a38da7d3d03e1a1151d199dbac0a00ff2ff75a89RoboErik 510f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.events_list); 511f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } else { 512f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik widgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId, R.id.events_list); 513a38da7d3d03e1a1151d199dbac0a00ff2ff75a89RoboErik } 514a38da7d3d03e1a1151d199dbac0a00ff2ff75a89RoboErik } 515c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 516c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik 517c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik @Override 518c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik public void onReceive(Context context, Intent intent) { 519f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (LOGD) 520f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik Log.d(TAG, "AppWidgetService received an intent. It was " + intent.toString()); 521f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mContext = context; 522f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik if (mLoader == null) { 523f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mAppWidgetId = -1; 524f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik initLoader(); 525f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } else { 526f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mHandler.removeCallbacks(mUpdateLoader); 527f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik mHandler.post(mUpdateLoader); 528f1416205f0bd6d40ed20c49752fdf8da63258611RoboErik } 529c46c2dc5dbec57616d799b1d0290d7c827b48d0cRoboErik } 5303ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang } 531bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang 5323ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang /** 5333ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang * Format given time for debugging output. 5343ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang * 5353ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang * @param unixTime Target time to report. 5363ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang * @param now Current system time from {@link System#currentTimeMillis()} 5373ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang * for calculating time difference. 5383ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang */ 5393ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang static String formatDebugTime(long unixTime, long now) { 5403ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang Time time = new Time(); 5413ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang time.set(unixTime); 5423ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang 5433ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang long delta = unixTime - now; 5443ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang if (delta > DateUtils.MINUTE_IN_MILLIS) { 5453ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang delta /= DateUtils.MINUTE_IN_MILLIS; 5463ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang return String.format("[%d] %s (%+d mins)", unixTime, 5473ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang time.format("%H:%M:%S"), delta); 5483ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang } else { 5493ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang delta /= DateUtils.SECOND_IN_MILLIS; 5503ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang return String.format("[%d] %s (%+d secs)", unixTime, 5513ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang time.format("%H:%M:%S"), delta); 552bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 553bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang } 554bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang} 555