FileDocCategorySizeDatePackage
AgendaByDayAdapter.javaAPI DocAndroid 1.5 API13377Wed May 06 22:42:42 BST 2009com.android.calendar

AgendaByDayAdapter.java

/*
 * Copyright (C) 2008 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;

import android.content.Context;
import android.database.Cursor;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.LinkedList;

public class AgendaByDayAdapter extends BaseAdapter {
    private static final int TYPE_DAY = 0;
    private static final int TYPE_MEETING = 1;
    private static final int TYPE_LAST = 2;

    private final Context mContext;
    private final AgendaAdapter mAgendaAdapter;
    private final LayoutInflater mInflater;
    private ArrayList<RowInfo> mRowInfo;
    private int mTodayJulianDay;
    private Time mTime = new Time();

    public AgendaByDayAdapter(Context context, AgendaAdapter agendaAdapter) {
        mContext = context;
        mAgendaAdapter = agendaAdapter;
        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    public int getCount() {
        if (mRowInfo != null) {
            return mRowInfo.size();
        }
        return mAgendaAdapter.getCount();
    }

    public Object getItem(int position) {
        if (mRowInfo != null) {
            RowInfo row = mRowInfo.get(position);
            if (row.mType == TYPE_DAY) {
                return row;
            } else {
                return mAgendaAdapter.getItem(row.mData);
            }
        }
        return mAgendaAdapter.getItem(position);
    }

    public long getItemId(int position) {
        if (mRowInfo != null) {
            RowInfo row = mRowInfo.get(position);
            if (row.mType == TYPE_DAY) {
                return position;
            } else {
                return mAgendaAdapter.getItemId(row.mData);
            }
        }
        return mAgendaAdapter.getItemId(position);
    }

    @Override
    public int getViewTypeCount() {
        return TYPE_LAST;
    }

    @Override
    public int getItemViewType(int position) {
        return mRowInfo != null && mRowInfo.size() > position ?
                mRowInfo.get(position).mType : TYPE_DAY;
    }

    private static class ViewHolder {
        TextView dateView;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        if ((mRowInfo == null) || (position > mRowInfo.size())) {
            // If we have no row info, mAgendaAdapter returns the view.
            return mAgendaAdapter.getView(position, convertView, parent);
        }

        RowInfo row = mRowInfo.get(position);
        if (row.mType == TYPE_DAY) {
            ViewHolder holder;
            View agendaDayView;
            if ((convertView == null) || (convertView.getTag() == null)) {
                // Create a new AgendaView with a ViewHolder for fast access to
                // views w/o calling findViewById()
                holder = new ViewHolder();
                agendaDayView = mInflater.inflate(R.layout.agenda_day, parent, false);
                holder.dateView = (TextView) agendaDayView.findViewById(R.id.date);
                agendaDayView.setTag(holder);
            } else {
                agendaDayView = convertView;
                holder = (ViewHolder) convertView.getTag();
            }

            // Re-use the member variable "mTime" which is set to the local timezone.
            Time date = mTime;
            long millis = date.setJulianDay(row.mData);
            int flags = DateUtils.FORMAT_SHOW_YEAR
                    | DateUtils.FORMAT_SHOW_DATE;
            
            if (row.mData == mTodayJulianDay) {
                String dayText = mContext.getResources().getText(R.string.agenda_today) + ", ";
                holder.dateView.setText(dayText + DateUtils.formatDateTime(mContext, millis, flags));
            } else {
                flags |= DateUtils.FORMAT_SHOW_WEEKDAY;
                holder.dateView.setText(DateUtils.formatDateTime(mContext, millis, flags));
            }


            return agendaDayView;
        } else if (row.mType == TYPE_MEETING) {
            return mAgendaAdapter.getView(row.mData, convertView, parent);
        } else {
            // Error
            throw new IllegalStateException("Unknown event type:" + row.mType);
        }
    }

    public void clearDayHeaderInfo() {
        mRowInfo = null;
    }

    public void calculateDays(Cursor cursor) {
        ArrayList<RowInfo> rowInfo = new ArrayList<RowInfo>();
        int prevStartDay = -1;
        Time time = new Time();
        long now = System.currentTimeMillis();
        time.set(now);
        mTodayJulianDay = Time.getJulianDay(now, time.gmtoff);
        LinkedList<MultipleDayInfo> multipleDayList = new LinkedList<MultipleDayInfo>();
        for (int position = 0; cursor.moveToNext(); position++) {
            boolean allDay = cursor.getInt(AgendaActivity.INDEX_ALL_DAY) != 0;
            int startDay = cursor.getInt(AgendaActivity.INDEX_START_DAY);

            if (startDay != prevStartDay) {
                // Check if we skipped over any empty days
                if (prevStartDay == -1) {
                    rowInfo.add(new RowInfo(TYPE_DAY, startDay));
                } else {
                    // If there are any multiple-day events that span the empty
                    // range of days, then create day headers and events for
                    // those multiple-day events.
                    boolean dayHeaderAdded = false;
                    for (int currentDay = prevStartDay + 1; currentDay <= startDay; currentDay++) {
                        dayHeaderAdded = false;
                        Iterator<MultipleDayInfo> iter = multipleDayList.iterator();
                        while (iter.hasNext()) {
                            MultipleDayInfo info = iter.next();
                            // If this event has ended then remove it from the
                            // list.
                            if (info.mEndDay < currentDay) {
                                iter.remove();
                                continue;
                            }

                            // If this is the first event for the day, then
                            // insert a day header.
                            if (!dayHeaderAdded) {
                                rowInfo.add(new RowInfo(TYPE_DAY, currentDay));
                                dayHeaderAdded = true;
                            }
                            rowInfo.add(new RowInfo(TYPE_MEETING, info.mPosition));
                        }
                    }

                    // If the day header was not added for the start day, then
                    // add it now.
                    if (!dayHeaderAdded) {
                        rowInfo.add(new RowInfo(TYPE_DAY, startDay));
                    }
                }
                prevStartDay = startDay;
            }

            // Add in the event for this cursor position
            rowInfo.add(new RowInfo(TYPE_MEETING, position));

            // If this event spans multiple days, then add it to the multipleDay
            // list.
            int endDay = cursor.getInt(AgendaActivity.INDEX_END_DAY);
            if (endDay > startDay) {
                multipleDayList.add(new MultipleDayInfo(position, endDay));
            }
        }

        // There are no more cursor events but we might still have multiple-day
        // events left.  So create day headers and events for those.
        if (prevStartDay > 0) {
            // Get the Julian day for the last day of this month.  To do that,
            // we set the date to one less than the first day of the next month,
            // and then normalize.
            time.setJulianDay(prevStartDay);
            time.month += 1;
            time.monthDay = 0;  // monthDay starts with 1, so this is the previous day
            long millis = time.normalize(true /* ignore isDst */);
            int lastDayOfMonth = Time.getJulianDay(millis, time.gmtoff);

            for (int currentDay = prevStartDay + 1; currentDay <= lastDayOfMonth; currentDay++) {
                boolean dayHeaderAdded = false;
                Iterator<MultipleDayInfo> iter = multipleDayList.iterator();
                while (iter.hasNext()) {
                    MultipleDayInfo info = iter.next();
                    // If this event has ended then remove it from the
                    // list.
                    if (info.mEndDay < currentDay) {
                        iter.remove();
                        continue;
                    }

                    // If this is the first event for the day, then
                    // insert a day header.
                    if (!dayHeaderAdded) {
                        rowInfo.add(new RowInfo(TYPE_DAY, currentDay));
                        dayHeaderAdded = true;
                    }
                    rowInfo.add(new RowInfo(TYPE_MEETING, info.mPosition));
                }
            }
        }
        mRowInfo = rowInfo;
    }

    private static class RowInfo {
        // 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 Julian day.  Otherwise
        // mType is TYPE_MEETING and mData is the cursor position.
        final int mData;

        RowInfo(int type, int data) {
            mType = type;
            mData = data;
        }
    }

    private static class MultipleDayInfo {
        final int mPosition;
        final int mEndDay;

        MultipleDayInfo(int position, int endDay) {
            mPosition = position;
            mEndDay = endDay;
        }
    }

    /**
     * Searches for the day that matches the given Time object and returns the
     * list position of that day.  If there are no events for that day, then it
     * finds the nearest day (before or after) that has events and returns the
     * list position for that day.
     *
     * @param time the date to search for
     * @return the cursor position of the first event for that date, or zero
     * if no match was found
     */
    public int findDayPositionNearestTime(Time time) {
        if (mRowInfo == null) {
            return 0;
        }
        long millis = time.toMillis(false /* use isDst */);
        int julianDay = Time.getJulianDay(millis, time.gmtoff);
        int minDistance = 1000;  // some big number
        int minIndex = 0;
        int len = mRowInfo.size();
        for (int index = 0; index < len; index++) {
            RowInfo row = mRowInfo.get(index);
            if (row.mType == TYPE_DAY) {
                int distance = Math.abs(julianDay - row.mData);
                if (distance == 0) {
                    return index;
                }
                if (distance < minDistance) {
                    minDistance = distance;
                    minIndex = index;
                }
            }
        }

        // We didn't find an exact match so take the nearest day that had
        // events.
        return minIndex;
    }

    /**
     * Finds the Julian day containing the event at the given position.
     *
     * @param position the list position of an event
     * @return the Julian day containing that event
     */
    public int findJulianDayFromPosition(int position) {
        if (mRowInfo == null || position < 0) {
            return 0;
        }

        int len = mRowInfo.size();
        if (position >= len) return 0;  // no row info at this position

        for (int index = position; index >= 0; index--) {
            RowInfo row = mRowInfo.get(index);
            if (row.mType == TYPE_DAY) {
                return row.mData;
            }
        }
        return 0;
    }

    /**
     * Converts a list position to a cursor position.  The list contains
     * day headers as well as events.  The cursor contains only events.
     *
     * @param listPos the list position of an event
     * @return the corresponding cursor position of that event
     */
    public int getCursorPosition(int listPos) {
        if (mRowInfo != null && listPos >= 0) {
            RowInfo row = mRowInfo.get(listPos);
            if (row.mType == TYPE_MEETING) {
                return row.mData;
            }
        }
        return listPos;
    }

    @Override
    public boolean areAllItemsEnabled() {
        return false;
    }

    @Override
    public boolean isEnabled(int position) {
        if (mRowInfo != null && position < mRowInfo.size()) {
            RowInfo row = mRowInfo.get(position);
            return row.mType == TYPE_MEETING;
        }
        return true;
    }
}