CalendarAppWidgetModel.java revision 77cf2c757257fdb17320be030001b7c8eff8253d
1bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang/*
2bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang * Copyright (C) 2010 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
1921a183875fbbfa54f5a2a87779888a5fb7d1af44Erikimport com.android.calendar.R;
2021a183875fbbfa54f5a2a87779888a5fb7d1af44Erikimport com.android.calendar.Utils;
2121a183875fbbfa54f5a2a87779888a5fb7d1af44Erik
223ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport android.content.Context;
233ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport android.database.Cursor;
243ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport android.text.TextUtils;
253ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport android.text.format.DateFormat;
263ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport android.text.format.DateUtils;
273ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport android.text.format.Time;
283ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport android.util.Log;
29bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangimport android.view.View;
30bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
313ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport java.util.ArrayList;
323ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport java.util.LinkedList;
333ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport java.util.List;
343ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tangimport java.util.TimeZone;
35bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
36bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tangclass CalendarAppWidgetModel {
373ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    private static final String TAG = CalendarAppWidgetModel.class.getSimpleName();
383ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    private static final boolean LOGD = false;
393ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
4021a183875fbbfa54f5a2a87779888a5fb7d1af44Erik    private String mHomeTZName;
4121a183875fbbfa54f5a2a87779888a5fb7d1af44Erik    private boolean mShowTZ;
423ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    /**
433ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * {@link RowInfo} is a class that represents a single row in the widget. It
443ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * is actually only a pointer to either a {@link DayInfo} or an
453ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * {@link EventInfo} instance, since a row in the widget might be either a
463ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * day header or an event.
47bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang     */
483ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    static class RowInfo {
493ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        static final int TYPE_DAY = 0;
503ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        static final int TYPE_MEETING = 1;
513ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
523ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        /**
533ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang         *  mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING)
543ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang         */
553ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        final int mType;
563ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
573ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        /**
583ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang         * If mType is TYPE_DAY, then mData is the index into day infos.
593ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang         * Otherwise mType is TYPE_MEETING and mData is the index into event
603ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang         * infos.
613ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang         */
623ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        final int mIndex;
63bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
643ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        RowInfo(int type, int index) {
653ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            mType = type;
663ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            mIndex = index;
67bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        }
68bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang    }
69bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
703ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    /**
713ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * {@link EventInfo} is a class that represents an event in the widget. It
723ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * contains all of the data necessary to display that event, including the
733ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * properly localized strings and visibility settings.
743ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     */
753ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    static class EventInfo {
76bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE)
77bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        String when;
78bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE)
79bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        String where;
80bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE)
81bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        String title;
82bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
8347d40324272ae39af0872bf5cbf27e1800478021Mason Tang        long start;
843ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        long end;
853ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        boolean allDay;
86a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa        int color;
8747d40324272ae39af0872bf5cbf27e1800478021Mason Tang
88bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        public EventInfo() {
89bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            visibWhen = View.GONE;
90bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            visibWhere = View.GONE;
91bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            visibTitle = View.GONE;
92bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        }
93bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
94bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        @Override
95bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        public String toString() {
96bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            StringBuilder builder = new StringBuilder();
97bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append("EventInfo [visibTitle=");
98bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(visibTitle);
99bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(", title=");
100bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(title);
101bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(", visibWhen=");
102bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(visibWhen);
103bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(", when=");
104bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(when);
105bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(", visibWhere=");
106bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(visibWhere);
107bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(", where=");
108bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append(where);
109a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            builder.append(", color=");
110a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            builder.append(String.format("0x%x", color));
111bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            builder.append("]");
112bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            return builder.toString();
113bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        }
114bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
115bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        @Override
116bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        public int hashCode() {
117bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            final int prime = 31;
118bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            int result = 1;
1193ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            result = prime * result + (allDay ? 1231 : 1237);
1203ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            result = prime * result + (int) (end ^ (end >>> 32));
1213ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            result = prime * result + (int) (start ^ (start >>> 32));
122bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            result = prime * result + ((title == null) ? 0 : title.hashCode());
123bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            result = prime * result + visibTitle;
124bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            result = prime * result + visibWhen;
125bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            result = prime * result + visibWhere;
126bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            result = prime * result + ((when == null) ? 0 : when.hashCode());
127bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            result = prime * result + ((where == null) ? 0 : where.hashCode());
128a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            result = prime * result + color;
129bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            return result;
130bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        }
131bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
132bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        @Override
133bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        public boolean equals(Object obj) {
1343ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (this == obj)
135bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return true;
1363ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (obj == null)
137bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
1383ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (getClass() != obj.getClass())
139bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
140bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            EventInfo other = (EventInfo) obj;
1413ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (allDay != other.allDay)
1423ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return false;
1433ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (end != other.end)
1443ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return false;
1453ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (start != other.start)
1463ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return false;
147bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            if (title == null) {
1483ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                if (other.title != null)
149bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                    return false;
1503ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            } else if (!title.equals(other.title))
151bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
1523ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (visibTitle != other.visibTitle)
153bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
1543ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (visibWhen != other.visibWhen)
155bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
1563ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (visibWhere != other.visibWhere)
157bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
158bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            if (when == null) {
1593ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                if (other.when != null)
160bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                    return false;
1613ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            } else if (!when.equals(other.when))
162bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
163bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            if (where == null) {
1643ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                if (other.where != null)
165bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                    return false;
166a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            } else if (!where.equals(other.where)) {
167bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang                return false;
168a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            } else if (color != other.color) {
169a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa                return false;
170a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            }
171bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang            return true;
172bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        }
1733ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    }
1743ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
1753ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    /**
1763ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * {@link DayInfo} is a class that represents a day header in the widget. It
1773ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * contains all of the data necessary to display that day header, including
1783ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     * the properly localized string.
1793ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang     */
1803ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    static class DayInfo {
1813ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
1823ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        /** The Julian day */
1833ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        final int mJulianDay;
1843ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
1853ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        /** The string representation of this day header, to be displayed */
1863ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        final String mDayLabel;
1873ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
1883ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        DayInfo(int julianDay, String label) {
1893ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            mJulianDay = julianDay;
1903ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            mDayLabel = label;
1913ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
1923ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
1933ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        @Override
1943ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        public String toString() {
1953ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            return mDayLabel;
1963ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
1973ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
1983ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        @Override
1993ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        public int hashCode() {
2003ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            final int prime = 31;
2013ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            int result = 1;
2023ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            result = prime * result + ((mDayLabel == null) ? 0 : mDayLabel.hashCode());
2033ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            result = prime * result + mJulianDay;
2043ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            return result;
2053ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
2063ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2073ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        @Override
2083ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        public boolean equals(Object obj) {
2093ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (this == obj)
2103ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return true;
2113ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (obj == null)
2123ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return false;
2133ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (getClass() != obj.getClass())
2143ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return false;
2153ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            DayInfo other = (DayInfo) obj;
2163ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (mDayLabel == null) {
2173ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                if (other.mDayLabel != null)
2183ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                    return false;
2193ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            } else if (!mDayLabel.equals(other.mDayLabel))
2203ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return false;
2213ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (mJulianDay != other.mJulianDay)
2223ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                return false;
2233ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            return true;
2243ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
2253ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2263ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    }
2273ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2283ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    String mDayOfWeek;
2293ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    String mDayOfMonth;
2303ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final List<RowInfo> mRowInfos;
2313ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final List<EventInfo> mEventInfos;
2323ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final List<DayInfo> mDayInfos;
2333ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final Context mContext;
2343ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final long mNow;
2353ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final long mStartOfNextDay;
2363ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final int mTodayJulianDay;
2373ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    final int mMaxJulianDay;
2383ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2393ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    public CalendarAppWidgetModel(Context context) {
2403ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mNow = System.currentTimeMillis();
2413ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        Time time = new Time();
2423ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        time.set(mNow);
2433ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        time.monthDay++;
2443ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        time.hour = 0;
2453ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        time.minute = 0;
2463ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        time.second = 0;
2473ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mStartOfNextDay = time.normalize(true);
2483ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2493ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        long localOffset = TimeZone.getDefault().getOffset(mNow) / 1000;
2503ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mTodayJulianDay = Time.getJulianDay(mNow, localOffset);
2513ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1;
2523ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2533ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        // Calendar header
2543ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        String dayOfWeek = DateUtils.getDayOfWeekString(
2553ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                time.weekDay + 1, DateUtils.LENGTH_MEDIUM).toUpperCase();
2563ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2573ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mDayOfWeek = dayOfWeek;
2583ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mDayOfMonth = Integer.toString(time.monthDay);
2593ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2603ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mEventInfos = new ArrayList<EventInfo>(50);
2613ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mRowInfos = new ArrayList<RowInfo>(50);
2623ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mDayInfos = new ArrayList<DayInfo>(8);
2633ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        mContext = context;
2643ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    }
2653ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
26621a183875fbbfa54f5a2a87779888a5fb7d1af44Erik    public void buildFromCursor(Cursor cursor, String timeZone) {
267a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa        final Time recycle = new Time(timeZone);
2683ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        final ArrayList<LinkedList<RowInfo>> mBuckets =
269a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa                new ArrayList<LinkedList<RowInfo>>(CalendarAppWidgetService.MAX_DAYS);
2703ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) {
2713ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            mBuckets.add(new LinkedList<RowInfo>());
2723ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
27321a183875fbbfa54f5a2a87779888a5fb7d1af44Erik        recycle.setToNow();
27421a183875fbbfa54f5a2a87779888a5fb7d1af44Erik        mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone());
27521a183875fbbfa54f5a2a87779888a5fb7d1af44Erik        if (mShowTZ) {
27621a183875fbbfa54f5a2a87779888a5fb7d1af44Erik            mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0,
27721a183875fbbfa54f5a2a87779888a5fb7d1af44Erik                    TimeZone.SHORT);
27821a183875fbbfa54f5a2a87779888a5fb7d1af44Erik        }
2793ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2803ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        cursor.moveToPosition(-1);
2813ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        while (cursor.moveToNext()) {
282a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final int rowId = cursor.getPosition();
283a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID);
284a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0;
2853ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN);
2863ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            long end = cursor.getLong(CalendarAppWidgetService.INDEX_END);
287a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE);
288a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final String location =
289a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa                    cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION);
2903ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            // we don't compute these ourselves because it seems to produce the
2913ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            // wrong endDay for all day events
292a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY);
293a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY);
294a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR);
2953ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
2963ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            // Adjust all-day times into local timezone
2973ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (allDay) {
2983ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                start = Utils.convertUtcToLocal(recycle, start);
2993ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                end = Utils.convertUtcToLocal(recycle, end);
3003ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
301bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
3023ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (LOGD) {
3033ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start
3043ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                        + " end:" + end + " eventId:" + eventId);
3053ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
3063ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
3073ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            // we might get some extra events when querying, in order to
3083ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            // deal with all-day events
3093ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (end < mNow) {
3103ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                continue;
3113ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
3123ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
3133ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            int i = mEventInfos.size();
3143ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            mEventInfos.add(populateEventInfo(
315a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa                    allDay, start, end, startDay, endDay, title, location, color));
3163ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            // populate the day buckets that this event falls into
3173ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            int from = Math.max(startDay, mTodayJulianDay);
3183ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            int to = Math.min(endDay, mMaxJulianDay);
3193ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            for (int day = from; day <= to; day++) {
3203ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                LinkedList<RowInfo> bucket = mBuckets.get(day - mTodayJulianDay);
3213ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i);
3223ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                if (allDay) {
3233ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                    bucket.addFirst(rowInfo);
3243ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                } else {
3253ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                    bucket.add(rowInfo);
3263ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                }
3273ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
3283ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
3293ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
3303ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        int day = mTodayJulianDay;
3313ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        int count = 0;
3323ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        for (LinkedList<RowInfo> bucket : mBuckets) {
3333ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (!bucket.isEmpty()) {
33450088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                // We don't show day header in today
33550088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                if (day != mTodayJulianDay) {
33650088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                    final DayInfo dayInfo = populateDayInfo(day, recycle);
33750088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                    // Add the day header
33850088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                    final int dayIndex = mDayInfos.size();
33950088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                    mDayInfos.add(dayInfo);
34050088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                    mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex));
34150088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa                }
34250088e364aaa3074476e1300a270aafb0653df47Daisuke Miyakawa
3433ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                // Add the event row infos
3443ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                mRowInfos.addAll(bucket);
3453ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                count += bucket.size();
3463ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
3473ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            day++;
3483ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) {
3493ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                break;
3503ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
3513ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
3523ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    }
3533ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
3543ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    private EventInfo populateEventInfo(boolean allDay, long start, long end,
355a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa            int startDay, int endDay, String title, String location, int color) {
3563ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        EventInfo eventInfo = new EventInfo();
3573ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
3583ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        // Compute a human-readable string for the start time of the event
35921a183875fbbfa54f5a2a87779888a5fb7d1af44Erik        StringBuilder whenString = new StringBuilder();
3603ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        int visibWhen;
3613ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        if (allDay) {
36221a183875fbbfa54f5a2a87779888a5fb7d1af44Erik            whenString.setLength(0);
3633ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            visibWhen = View.GONE;
3643ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        } else {
3653ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            int flags = DateUtils.FORMAT_ABBREV_ALL;
3663ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            flags |= DateUtils.FORMAT_SHOW_TIME;
3673ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (DateFormat.is24HourFormat(mContext)) {
3683ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                flags |= DateUtils.FORMAT_24HOUR;
3693ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
3703ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            if (endDay > startDay) {
3713ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang                flags |= DateUtils.FORMAT_SHOW_DATE;
37221a183875fbbfa54f5a2a87779888a5fb7d1af44Erik            }
37377cf2c757257fdb17320be030001b7c8eff8253dDaisuke Miyakawa            whenString.append(Utils.formatDateRange(mContext, start, end, flags));
37477cf2c757257fdb17320be030001b7c8eff8253dDaisuke Miyakawa
37521a183875fbbfa54f5a2a87779888a5fb7d1af44Erik            String tz = Utils.getTimeZone(mContext, null);
37621a183875fbbfa54f5a2a87779888a5fb7d1af44Erik            if (mShowTZ) {
37721a183875fbbfa54f5a2a87779888a5fb7d1af44Erik                whenString.append(" ").append(mHomeTZName);
3783ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            }
3793ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            visibWhen = View.VISIBLE;
380bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        }
3813ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        eventInfo.start = start;
3823ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        eventInfo.end = end;
3833ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        eventInfo.allDay = allDay;
38421a183875fbbfa54f5a2a87779888a5fb7d1af44Erik        eventInfo.when = whenString.toString();
3853ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        eventInfo.visibWhen = visibWhen;
386a71e0a520493532dbb7e9bfa164ab78e59e797a3Daisuke Miyakawa        eventInfo.color = color;
3873ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
3883ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        // What
3893ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        if (TextUtils.isEmpty(title)) {
3903ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            eventInfo.title = mContext.getString(R.string.no_title_label);
3913ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        } else {
3923ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            eventInfo.title = title;
3933ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
3943ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        eventInfo.visibTitle = View.VISIBLE;
3953ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
3963ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        // Where
3973ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        if (!TextUtils.isEmpty(location)) {
3983ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            eventInfo.visibWhere = View.VISIBLE;
3993ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            eventInfo.where = location;
4003ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        } else {
4013ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            eventInfo.visibWhere = View.GONE;
4023ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
4033ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        return eventInfo;
4043ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    }
4053ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
4063ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang    private DayInfo populateDayInfo(int julianDay, Time recycle) {
4073ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        long millis = recycle.setJulianDay(julianDay);
4083ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_UTC;
4093ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
4103ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
4113ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        String label;
4123ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        if (julianDay == mTodayJulianDay + 1) {
4133ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            label = mContext.getString(R.string.tomorrow);
4143ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        } else {
4153ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang            label = DateUtils.formatDateRange(mContext, millis, millis, flags);
4163ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        }
4173ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
4183ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_UTC;
4193ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        flags |= DateUtils.FORMAT_SHOW_DATE;
4203ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
4213ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        label += ", ";
4223ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        label += DateUtils.formatDateRange(mContext, millis, millis, flags);
4233ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang
4243ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        return new DayInfo(julianDay, label);
425bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang    }
426bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang
427bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang    @Override
428bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang    public String toString() {
429bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        StringBuilder builder = new StringBuilder();
430bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        builder.append("\nCalendarAppWidgetModel [eventInfos=");
4313ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        builder.append(mEventInfos);
432bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        builder.append(", dayOfMonth=");
4333ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        builder.append(mDayOfMonth);
434bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        builder.append(", dayOfWeek=");
4353ea333d41c04fd5f3a5d45f540c17894874429e8Mason Tang        builder.append(mDayOfWeek);
436bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        builder.append("]");
437bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang        return builder.toString();
438bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang    }
439bb3f08abac01e3083d837e5e823b311e3c039e90Mason Tang}