| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.calendar.widget; |
| |
| import com.android.calendar.R; |
| import com.android.calendar.Utils; |
| |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.text.TextUtils; |
| import android.text.format.DateFormat; |
| import android.text.format.DateUtils; |
| import android.text.format.Time; |
| import android.util.Log; |
| import android.view.View; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.TimeZone; |
| |
| class CalendarAppWidgetModel { |
| private static final String TAG = CalendarAppWidgetModel.class.getSimpleName(); |
| private static final boolean LOGD = false; |
| |
| private String mHomeTZName; |
| private boolean mShowTZ; |
| /** |
| * {@link RowInfo} is a class that represents a single row in the widget. It |
| * is actually only a pointer to either a {@link DayInfo} or an |
| * {@link EventInfo} instance, since a row in the widget might be either a |
| * day header or an event. |
| */ |
| static class RowInfo { |
| static final int TYPE_DAY = 0; |
| static final int TYPE_MEETING = 1; |
| |
| /** |
| * mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING) |
| */ |
| final int mType; |
| |
| /** |
| * If mType is TYPE_DAY, then mData is the index into day infos. |
| * Otherwise mType is TYPE_MEETING and mData is the index into event |
| * infos. |
| */ |
| final int mIndex; |
| |
| RowInfo(int type, int index) { |
| mType = type; |
| mIndex = index; |
| } |
| } |
| |
| /** |
| * {@link EventInfo} is a class that represents an event in the widget. It |
| * contains all of the data necessary to display that event, including the |
| * properly localized strings and visibility settings. |
| */ |
| static class EventInfo { |
| int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE) |
| String when; |
| int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE) |
| String where; |
| int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE) |
| String title; |
| int selfAttendeeStatus; |
| |
| long id; |
| long start; |
| long end; |
| boolean allDay; |
| int color; |
| |
| public EventInfo() { |
| visibWhen = View.GONE; |
| visibWhere = View.GONE; |
| visibTitle = View.GONE; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| builder.append("EventInfo [visibTitle="); |
| builder.append(visibTitle); |
| builder.append(", title="); |
| builder.append(title); |
| builder.append(", visibWhen="); |
| builder.append(visibWhen); |
| builder.append(", id="); |
| builder.append(id); |
| builder.append(", when="); |
| builder.append(when); |
| builder.append(", visibWhere="); |
| builder.append(visibWhere); |
| builder.append(", where="); |
| builder.append(where); |
| builder.append(", color="); |
| builder.append(String.format("0x%x", color)); |
| builder.append(", selfAttendeeStatus="); |
| builder.append(selfAttendeeStatus); |
| builder.append("]"); |
| return builder.toString(); |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + (allDay ? 1231 : 1237); |
| result = prime * result + (int) (id ^ (id >>> 32)); |
| result = prime * result + (int) (end ^ (end >>> 32)); |
| result = prime * result + (int) (start ^ (start >>> 32)); |
| result = prime * result + ((title == null) ? 0 : title.hashCode()); |
| result = prime * result + visibTitle; |
| result = prime * result + visibWhen; |
| result = prime * result + visibWhere; |
| result = prime * result + ((when == null) ? 0 : when.hashCode()); |
| result = prime * result + ((where == null) ? 0 : where.hashCode()); |
| result = prime * result + color; |
| result = prime * result + selfAttendeeStatus; |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj == null) |
| return false; |
| if (getClass() != obj.getClass()) |
| return false; |
| EventInfo other = (EventInfo) obj; |
| if (id != other.id) |
| return false; |
| if (allDay != other.allDay) |
| return false; |
| if (end != other.end) |
| return false; |
| if (start != other.start) |
| return false; |
| if (title == null) { |
| if (other.title != null) |
| return false; |
| } else if (!title.equals(other.title)) |
| return false; |
| if (visibTitle != other.visibTitle) |
| return false; |
| if (visibWhen != other.visibWhen) |
| return false; |
| if (visibWhere != other.visibWhere) |
| return false; |
| if (when == null) { |
| if (other.when != null) |
| return false; |
| } else if (!when.equals(other.when)) { |
| return false; |
| } |
| if (where == null) { |
| if (other.where != null) |
| return false; |
| } else if (!where.equals(other.where)) { |
| return false; |
| } |
| if (color != other.color) { |
| return false; |
| } |
| if (selfAttendeeStatus != other.selfAttendeeStatus) { |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| /** |
| * {@link DayInfo} is a class that represents a day header in the widget. It |
| * contains all of the data necessary to display that day header, including |
| * the properly localized string. |
| */ |
| static class DayInfo { |
| |
| /** The Julian day */ |
| final int mJulianDay; |
| |
| /** The string representation of this day header, to be displayed */ |
| final String mDayLabel; |
| |
| DayInfo(int julianDay, String label) { |
| mJulianDay = julianDay; |
| mDayLabel = label; |
| } |
| |
| @Override |
| public String toString() { |
| return mDayLabel; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + ((mDayLabel == null) ? 0 : mDayLabel.hashCode()); |
| result = prime * result + mJulianDay; |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj == null) |
| return false; |
| if (getClass() != obj.getClass()) |
| return false; |
| DayInfo other = (DayInfo) obj; |
| if (mDayLabel == null) { |
| if (other.mDayLabel != null) |
| return false; |
| } else if (!mDayLabel.equals(other.mDayLabel)) |
| return false; |
| if (mJulianDay != other.mJulianDay) |
| return false; |
| return true; |
| } |
| |
| } |
| |
| final List<RowInfo> mRowInfos; |
| final List<EventInfo> mEventInfos; |
| final List<DayInfo> mDayInfos; |
| final Context mContext; |
| final long mNow; |
| final int mTodayJulianDay; |
| final int mMaxJulianDay; |
| |
| public CalendarAppWidgetModel(Context context, String timeZone) { |
| mNow = System.currentTimeMillis(); |
| Time time = new Time(timeZone); |
| time.setToNow(); // This is needed for gmtoff to be set |
| mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff); |
| mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1; |
| mEventInfos = new ArrayList<EventInfo>(50); |
| mRowInfos = new ArrayList<RowInfo>(50); |
| mDayInfos = new ArrayList<DayInfo>(8); |
| mContext = context; |
| } |
| |
| public void buildFromCursor(Cursor cursor, String timeZone) { |
| final Time recycle = new Time(timeZone); |
| final ArrayList<LinkedList<RowInfo>> mBuckets = |
| new ArrayList<LinkedList<RowInfo>>(CalendarAppWidgetService.MAX_DAYS); |
| for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) { |
| mBuckets.add(new LinkedList<RowInfo>()); |
| } |
| recycle.setToNow(); |
| mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone()); |
| if (mShowTZ) { |
| mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0, |
| TimeZone.SHORT); |
| } |
| |
| cursor.moveToPosition(-1); |
| String tz = Utils.getTimeZone(mContext, null); |
| while (cursor.moveToNext()) { |
| final int rowId = cursor.getPosition(); |
| final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID); |
| final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0; |
| long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN); |
| long end = cursor.getLong(CalendarAppWidgetService.INDEX_END); |
| final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE); |
| final String location = |
| cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION); |
| // we don't compute these ourselves because it seems to produce the |
| // wrong endDay for all day events |
| final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY); |
| final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY); |
| final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR); |
| final int selfStatus = cursor |
| .getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS); |
| |
| // Adjust all-day times into local timezone |
| if (allDay) { |
| start = Utils.convertAlldayUtcToLocal(recycle, start, tz); |
| end = Utils.convertAlldayUtcToLocal(recycle, end, tz); |
| } |
| |
| if (LOGD) { |
| Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start |
| + " end:" + end + " eventId:" + eventId); |
| } |
| |
| // we might get some extra events when querying, in order to |
| // deal with all-day events |
| if (end < mNow) { |
| continue; |
| } |
| |
| int i = mEventInfos.size(); |
| mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title, |
| location, color, selfStatus)); |
| // populate the day buckets that this event falls into |
| int from = Math.max(startDay, mTodayJulianDay); |
| int to = Math.min(endDay, mMaxJulianDay); |
| for (int day = from; day <= to; day++) { |
| LinkedList<RowInfo> bucket = mBuckets.get(day - mTodayJulianDay); |
| RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i); |
| if (allDay) { |
| bucket.addFirst(rowInfo); |
| } else { |
| bucket.add(rowInfo); |
| } |
| } |
| } |
| |
| int day = mTodayJulianDay; |
| int count = 0; |
| for (LinkedList<RowInfo> bucket : mBuckets) { |
| if (!bucket.isEmpty()) { |
| // We don't show day header in today |
| if (day != mTodayJulianDay) { |
| final DayInfo dayInfo = populateDayInfo(day, recycle); |
| // Add the day header |
| final int dayIndex = mDayInfos.size(); |
| mDayInfos.add(dayInfo); |
| mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex)); |
| } |
| |
| // Add the event row infos |
| mRowInfos.addAll(bucket); |
| count += bucket.size(); |
| } |
| day++; |
| if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) { |
| break; |
| } |
| } |
| } |
| |
| private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end, |
| int startDay, int endDay, String title, String location, int color, int selfStatus) { |
| EventInfo eventInfo = new EventInfo(); |
| |
| // Compute a human-readable string for the start time of the event |
| StringBuilder whenString = new StringBuilder(); |
| int visibWhen; |
| int flags = DateUtils.FORMAT_ABBREV_ALL; |
| visibWhen = View.VISIBLE; |
| if (allDay) { |
| flags |= DateUtils.FORMAT_SHOW_DATE; |
| whenString.append(Utils.formatDateRange(mContext, start, end, flags)); |
| } else { |
| flags |= DateUtils.FORMAT_SHOW_TIME; |
| if (DateFormat.is24HourFormat(mContext)) { |
| flags |= DateUtils.FORMAT_24HOUR; |
| } |
| if (endDay > startDay) { |
| flags |= DateUtils.FORMAT_SHOW_DATE; |
| } |
| whenString.append(Utils.formatDateRange(mContext, start, end, flags)); |
| |
| if (mShowTZ) { |
| whenString.append(" ").append(mHomeTZName); |
| } |
| } |
| eventInfo.id = eventId; |
| eventInfo.start = start; |
| eventInfo.end = end; |
| eventInfo.allDay = allDay; |
| eventInfo.when = whenString.toString(); |
| eventInfo.visibWhen = visibWhen; |
| eventInfo.color = color; |
| eventInfo.selfAttendeeStatus = selfStatus; |
| |
| // What |
| if (TextUtils.isEmpty(title)) { |
| eventInfo.title = mContext.getString(R.string.no_title_label); |
| } else { |
| eventInfo.title = title; |
| } |
| eventInfo.visibTitle = View.VISIBLE; |
| |
| // Where |
| if (!TextUtils.isEmpty(location)) { |
| eventInfo.visibWhere = View.VISIBLE; |
| eventInfo.where = location; |
| } else { |
| eventInfo.visibWhere = View.GONE; |
| } |
| return eventInfo; |
| } |
| |
| private DayInfo populateDayInfo(int julianDay, Time recycle) { |
| long millis = recycle.setJulianDay(julianDay); |
| int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE; |
| |
| String label; |
| if (julianDay == mTodayJulianDay + 1) { |
| label = mContext.getString(R.string.agenda_tomorrow, |
| Utils.formatDateRange(mContext, millis, millis, flags).toString()); |
| } else { |
| flags |= DateUtils.FORMAT_SHOW_WEEKDAY; |
| label = Utils.formatDateRange(mContext, millis, millis, flags); |
| } |
| return new DayInfo(julianDay, label); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| builder.append("\nCalendarAppWidgetModel [eventInfos="); |
| builder.append(mEventInfos); |
| builder.append("]"); |
| return builder.toString(); |
| } |
| } |