Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.calendar.widget;
     18 
     19 import com.android.calendar.R;
     20 import com.android.calendar.Utils;
     21 
     22 import android.content.Context;
     23 import android.database.Cursor;
     24 import android.text.TextUtils;
     25 import android.text.format.DateFormat;
     26 import android.text.format.DateUtils;
     27 import android.text.format.Time;
     28 import android.util.Log;
     29 import android.view.View;
     30 
     31 import java.util.ArrayList;
     32 import java.util.LinkedList;
     33 import java.util.List;
     34 import java.util.TimeZone;
     35 
     36 class CalendarAppWidgetModel {
     37     private static final String TAG = CalendarAppWidgetModel.class.getSimpleName();
     38     private static final boolean LOGD = false;
     39 
     40     private String mHomeTZName;
     41     private boolean mShowTZ;
     42     /**
     43      * {@link RowInfo} is a class that represents a single row in the widget. It
     44      * is actually only a pointer to either a {@link DayInfo} or an
     45      * {@link EventInfo} instance, since a row in the widget might be either a
     46      * day header or an event.
     47      */
     48     static class RowInfo {
     49         static final int TYPE_DAY = 0;
     50         static final int TYPE_MEETING = 1;
     51 
     52         /**
     53          *  mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING)
     54          */
     55         final int mType;
     56 
     57         /**
     58          * If mType is TYPE_DAY, then mData is the index into day infos.
     59          * Otherwise mType is TYPE_MEETING and mData is the index into event
     60          * infos.
     61          */
     62         final int mIndex;
     63 
     64         RowInfo(int type, int index) {
     65             mType = type;
     66             mIndex = index;
     67         }
     68     }
     69 
     70     /**
     71      * {@link EventInfo} is a class that represents an event in the widget. It
     72      * contains all of the data necessary to display that event, including the
     73      * properly localized strings and visibility settings.
     74      */
     75     static class EventInfo {
     76         int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE)
     77         String when;
     78         int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE)
     79         String where;
     80         int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE)
     81         String title;
     82         int selfAttendeeStatus;
     83 
     84         long id;
     85         long start;
     86         long end;
     87         boolean allDay;
     88         int color;
     89 
     90         public EventInfo() {
     91             visibWhen = View.GONE;
     92             visibWhere = View.GONE;
     93             visibTitle = View.GONE;
     94         }
     95 
     96         @Override
     97         public String toString() {
     98             StringBuilder builder = new StringBuilder();
     99             builder.append("EventInfo [visibTitle=");
    100             builder.append(visibTitle);
    101             builder.append(", title=");
    102             builder.append(title);
    103             builder.append(", visibWhen=");
    104             builder.append(visibWhen);
    105             builder.append(", id=");
    106             builder.append(id);
    107             builder.append(", when=");
    108             builder.append(when);
    109             builder.append(", visibWhere=");
    110             builder.append(visibWhere);
    111             builder.append(", where=");
    112             builder.append(where);
    113             builder.append(", color=");
    114             builder.append(String.format("0x%x", color));
    115             builder.append(", selfAttendeeStatus=");
    116             builder.append(selfAttendeeStatus);
    117             builder.append("]");
    118             return builder.toString();
    119         }
    120 
    121         @Override
    122         public int hashCode() {
    123             final int prime = 31;
    124             int result = 1;
    125             result = prime * result + (allDay ? 1231 : 1237);
    126             result = prime * result + (int) (id ^ (id >>> 32));
    127             result = prime * result + (int) (end ^ (end >>> 32));
    128             result = prime * result + (int) (start ^ (start >>> 32));
    129             result = prime * result + ((title == null) ? 0 : title.hashCode());
    130             result = prime * result + visibTitle;
    131             result = prime * result + visibWhen;
    132             result = prime * result + visibWhere;
    133             result = prime * result + ((when == null) ? 0 : when.hashCode());
    134             result = prime * result + ((where == null) ? 0 : where.hashCode());
    135             result = prime * result + color;
    136             result = prime * result + selfAttendeeStatus;
    137             return result;
    138         }
    139 
    140         @Override
    141         public boolean equals(Object obj) {
    142             if (this == obj)
    143                 return true;
    144             if (obj == null)
    145                 return false;
    146             if (getClass() != obj.getClass())
    147                 return false;
    148             EventInfo other = (EventInfo) obj;
    149             if (id != other.id)
    150                 return false;
    151             if (allDay != other.allDay)
    152                 return false;
    153             if (end != other.end)
    154                 return false;
    155             if (start != other.start)
    156                 return false;
    157             if (title == null) {
    158                 if (other.title != null)
    159                     return false;
    160             } else if (!title.equals(other.title))
    161                 return false;
    162             if (visibTitle != other.visibTitle)
    163                 return false;
    164             if (visibWhen != other.visibWhen)
    165                 return false;
    166             if (visibWhere != other.visibWhere)
    167                 return false;
    168             if (when == null) {
    169                 if (other.when != null)
    170                     return false;
    171             } else if (!when.equals(other.when)) {
    172                 return false;
    173             }
    174             if (where == null) {
    175                 if (other.where != null)
    176                     return false;
    177             } else if (!where.equals(other.where)) {
    178                 return false;
    179             }
    180             if (color != other.color) {
    181                 return false;
    182             }
    183             if (selfAttendeeStatus != other.selfAttendeeStatus) {
    184                 return false;
    185             }
    186             return true;
    187         }
    188     }
    189 
    190     /**
    191      * {@link DayInfo} is a class that represents a day header in the widget. It
    192      * contains all of the data necessary to display that day header, including
    193      * the properly localized string.
    194      */
    195     static class DayInfo {
    196 
    197         /** The Julian day */
    198         final int mJulianDay;
    199 
    200         /** The string representation of this day header, to be displayed */
    201         final String mDayLabel;
    202 
    203         DayInfo(int julianDay, String label) {
    204             mJulianDay = julianDay;
    205             mDayLabel = label;
    206         }
    207 
    208         @Override
    209         public String toString() {
    210             return mDayLabel;
    211         }
    212 
    213         @Override
    214         public int hashCode() {
    215             final int prime = 31;
    216             int result = 1;
    217             result = prime * result + ((mDayLabel == null) ? 0 : mDayLabel.hashCode());
    218             result = prime * result + mJulianDay;
    219             return result;
    220         }
    221 
    222         @Override
    223         public boolean equals(Object obj) {
    224             if (this == obj)
    225                 return true;
    226             if (obj == null)
    227                 return false;
    228             if (getClass() != obj.getClass())
    229                 return false;
    230             DayInfo other = (DayInfo) obj;
    231             if (mDayLabel == null) {
    232                 if (other.mDayLabel != null)
    233                     return false;
    234             } else if (!mDayLabel.equals(other.mDayLabel))
    235                 return false;
    236             if (mJulianDay != other.mJulianDay)
    237                 return false;
    238             return true;
    239         }
    240 
    241     }
    242 
    243     final List<RowInfo> mRowInfos;
    244     final List<EventInfo> mEventInfos;
    245     final List<DayInfo> mDayInfos;
    246     final Context mContext;
    247     final long mNow;
    248     final int mTodayJulianDay;
    249     final int mMaxJulianDay;
    250 
    251     public CalendarAppWidgetModel(Context context, String timeZone) {
    252         mNow = System.currentTimeMillis();
    253         Time time = new Time(timeZone);
    254         time.setToNow(); // This is needed for gmtoff to be set
    255         mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff);
    256         mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1;
    257         mEventInfos = new ArrayList<EventInfo>(50);
    258         mRowInfos = new ArrayList<RowInfo>(50);
    259         mDayInfos = new ArrayList<DayInfo>(8);
    260         mContext = context;
    261     }
    262 
    263     public void buildFromCursor(Cursor cursor, String timeZone) {
    264         final Time recycle = new Time(timeZone);
    265         final ArrayList<LinkedList<RowInfo>> mBuckets =
    266                 new ArrayList<LinkedList<RowInfo>>(CalendarAppWidgetService.MAX_DAYS);
    267         for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) {
    268             mBuckets.add(new LinkedList<RowInfo>());
    269         }
    270         recycle.setToNow();
    271         mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone());
    272         if (mShowTZ) {
    273             mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0,
    274                     TimeZone.SHORT);
    275         }
    276 
    277         cursor.moveToPosition(-1);
    278         String tz = Utils.getTimeZone(mContext, null);
    279         while (cursor.moveToNext()) {
    280             final int rowId = cursor.getPosition();
    281             final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID);
    282             final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0;
    283             long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN);
    284             long end = cursor.getLong(CalendarAppWidgetService.INDEX_END);
    285             final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE);
    286             final String location =
    287                     cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION);
    288             // we don't compute these ourselves because it seems to produce the
    289             // wrong endDay for all day events
    290             final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY);
    291             final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY);
    292             final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR);
    293             final int selfStatus = cursor
    294                     .getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS);
    295 
    296             // Adjust all-day times into local timezone
    297             if (allDay) {
    298                 start = Utils.convertAlldayUtcToLocal(recycle, start, tz);
    299                 end = Utils.convertAlldayUtcToLocal(recycle, end, tz);
    300             }
    301 
    302             if (LOGD) {
    303                 Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start
    304                         + " end:" + end + " eventId:" + eventId);
    305             }
    306 
    307             // we might get some extra events when querying, in order to
    308             // deal with all-day events
    309             if (end < mNow) {
    310                 continue;
    311             }
    312 
    313             int i = mEventInfos.size();
    314             mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title,
    315                     location, color, selfStatus));
    316             // populate the day buckets that this event falls into
    317             int from = Math.max(startDay, mTodayJulianDay);
    318             int to = Math.min(endDay, mMaxJulianDay);
    319             for (int day = from; day <= to; day++) {
    320                 LinkedList<RowInfo> bucket = mBuckets.get(day - mTodayJulianDay);
    321                 RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i);
    322                 if (allDay) {
    323                     bucket.addFirst(rowInfo);
    324                 } else {
    325                     bucket.add(rowInfo);
    326                 }
    327             }
    328         }
    329 
    330         int day = mTodayJulianDay;
    331         int count = 0;
    332         for (LinkedList<RowInfo> bucket : mBuckets) {
    333             if (!bucket.isEmpty()) {
    334                 // We don't show day header in today
    335                 if (day != mTodayJulianDay) {
    336                     final DayInfo dayInfo = populateDayInfo(day, recycle);
    337                     // Add the day header
    338                     final int dayIndex = mDayInfos.size();
    339                     mDayInfos.add(dayInfo);
    340                     mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex));
    341                 }
    342 
    343                 // Add the event row infos
    344                 mRowInfos.addAll(bucket);
    345                 count += bucket.size();
    346             }
    347             day++;
    348             if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) {
    349                 break;
    350             }
    351         }
    352     }
    353 
    354     private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end,
    355             int startDay, int endDay, String title, String location, int color, int selfStatus) {
    356         EventInfo eventInfo = new EventInfo();
    357 
    358         // Compute a human-readable string for the start time of the event
    359         StringBuilder whenString = new StringBuilder();
    360         int visibWhen;
    361         int flags = DateUtils.FORMAT_ABBREV_ALL;
    362         visibWhen = View.VISIBLE;
    363         if (allDay) {
    364             flags |= DateUtils.FORMAT_SHOW_DATE;
    365             whenString.append(Utils.formatDateRange(mContext, start, end, flags));
    366         } else {
    367             flags |= DateUtils.FORMAT_SHOW_TIME;
    368             if (DateFormat.is24HourFormat(mContext)) {
    369                 flags |= DateUtils.FORMAT_24HOUR;
    370             }
    371             if (endDay > startDay) {
    372                 flags |= DateUtils.FORMAT_SHOW_DATE;
    373             }
    374             whenString.append(Utils.formatDateRange(mContext, start, end, flags));
    375 
    376             if (mShowTZ) {
    377                 whenString.append(" ").append(mHomeTZName);
    378             }
    379         }
    380         eventInfo.id = eventId;
    381         eventInfo.start = start;
    382         eventInfo.end = end;
    383         eventInfo.allDay = allDay;
    384         eventInfo.when = whenString.toString();
    385         eventInfo.visibWhen = visibWhen;
    386         eventInfo.color = color;
    387         eventInfo.selfAttendeeStatus = selfStatus;
    388 
    389         // What
    390         if (TextUtils.isEmpty(title)) {
    391             eventInfo.title = mContext.getString(R.string.no_title_label);
    392         } else {
    393             eventInfo.title = title;
    394         }
    395         eventInfo.visibTitle = View.VISIBLE;
    396 
    397         // Where
    398         if (!TextUtils.isEmpty(location)) {
    399             eventInfo.visibWhere = View.VISIBLE;
    400             eventInfo.where = location;
    401         } else {
    402             eventInfo.visibWhere = View.GONE;
    403         }
    404         return eventInfo;
    405     }
    406 
    407     private DayInfo populateDayInfo(int julianDay, Time recycle) {
    408         long millis = recycle.setJulianDay(julianDay);
    409         int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE;
    410 
    411         String label;
    412         if (julianDay == mTodayJulianDay + 1) {
    413             label = mContext.getString(R.string.agenda_tomorrow,
    414                     Utils.formatDateRange(mContext, millis, millis, flags).toString());
    415         } else {
    416             flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
    417             label = Utils.formatDateRange(mContext, millis, millis, flags);
    418         }
    419         return new DayInfo(julianDay, label);
    420     }
    421 
    422     @Override
    423     public String toString() {
    424         StringBuilder builder = new StringBuilder();
    425         builder.append("\nCalendarAppWidgetModel [eventInfos=");
    426         builder.append(mEventInfos);
    427         builder.append("]");
    428         return builder.toString();
    429     }
    430 }