public AgendaByDayAdapter(Context context) {
   mContext = context;
   mAgendaAdapter = new AgendaAdapter(context, R.layout.agenda_item);
   mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   mStringBuilder = new StringBuilder(50);
   mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
   mTimeZone = Utils.getTimeZone(context, mTZUpdater);
   mTmpTime = new Time(mTimeZone);
 }
 private void init(Context c) {
   mListContext = c;
   mTracker = VelocityTracker.obtain();
   mTempTime = new Time(Utils.getTimeZone(c, mTimezoneUpdater));
   if (mScale == 0) {
     mScale = c.getResources().getDisplayMetrics().density;
     if (mScale != 1) {
       MIN_VELOCITY_FOR_FLING *= mScale;
       MULTIPLE_MONTH_VELOCITY_THRESHOLD *= mScale;
       FLING_VELOCITY_DIVIDER *= mScale;
     }
   }
 }
 @Override
 public void run() {
   mTimeZone = Utils.getTimeZone(mContext, this);
   mTmpTime = new Time(mTimeZone);
   notifyDataSetChanged();
 }
  public void calculateDays(DayAdapterInfo dayAdapterInfo) {
    Cursor cursor = dayAdapterInfo.cursor;
    ArrayList<RowInfo> rowInfo = new ArrayList<RowInfo>();
    int prevStartDay = -1;

    Time tempTime = new Time(mTimeZone);
    long now = System.currentTimeMillis();
    tempTime.set(now);
    mTodayJulianDay = Time.getJulianDay(now, tempTime.gmtoff);

    LinkedList<MultipleDayInfo> multipleDayList = new LinkedList<MultipleDayInfo>();
    for (int position = 0; cursor.moveToNext(); position++) {
      int startDay = cursor.getInt(AgendaWindowAdapter.INDEX_START_DAY);
      long id = cursor.getLong(AgendaWindowAdapter.INDEX_EVENT_ID);
      long startTime = cursor.getLong(AgendaWindowAdapter.INDEX_BEGIN);
      long endTime = cursor.getLong(AgendaWindowAdapter.INDEX_END);
      long instanceId = cursor.getLong(AgendaWindowAdapter.INDEX_INSTANCE_ID);
      boolean allDay = cursor.getInt(AgendaWindowAdapter.INDEX_ALL_DAY) != 0;
      if (allDay) {
        startTime = Utils.convertAlldayUtcToLocal(tempTime, startTime, mTimeZone);
        endTime = Utils.convertAlldayUtcToLocal(tempTime, endTime, mTimeZone);
      }
      // Skip over the days outside of the adapter's range
      startDay = Math.max(startDay, dayAdapterInfo.start);
      // Make sure event's start time is not before the start of the day
      // (setJulianDay sets the time to 12:00am)
      long adapterStartTime = tempTime.setJulianDay(startDay);
      startTime = Math.max(startTime, adapterStartTime);

      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;
              }
              long nextMidnight =
                  Utils.getNextMidnight(tempTime, info.mEventStartTimeMilli, mTimeZone);

              long infoEndTime =
                  (info.mEndDay == currentDay) ? info.mEventEndTimeMilli : nextMidnight;
              rowInfo.add(
                  new RowInfo(
                      TYPE_MEETING,
                      currentDay,
                      info.mPosition,
                      info.mEventId,
                      info.mEventStartTimeMilli,
                      infoEndTime,
                      info.mInstanceId,
                      info.mAllDay));

              info.mEventStartTimeMilli = nextMidnight;
            }
          }

          // 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;
      }

      // If this event spans multiple days, then add it to the multipleDay
      // list.
      int endDay = cursor.getInt(AgendaWindowAdapter.INDEX_END_DAY);

      // Skip over the days outside of the adapter's range
      endDay = Math.min(endDay, dayAdapterInfo.end);
      if (endDay > startDay) {
        long nextMidnight = Utils.getNextMidnight(tempTime, startTime, mTimeZone);
        multipleDayList.add(
            new MultipleDayInfo(position, endDay, id, nextMidnight, endTime, instanceId, allDay));
        // Add in the event for this cursor position - since it is the start of a multi-day
        // event, the end time is midnight
        rowInfo.add(
            new RowInfo(
                TYPE_MEETING, startDay, position, id, startTime, nextMidnight, instanceId, allDay));
      } else {
        // Add in the event for this cursor position
        rowInfo.add(
            new RowInfo(
                TYPE_MEETING, startDay, position, id, startTime, endTime, instanceId, allDay));
      }
    }

    // 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) {
      for (int currentDay = prevStartDay + 1; currentDay <= dayAdapterInfo.end; 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;
          }
          long nextMidnight = Utils.getNextMidnight(tempTime, info.mEventStartTimeMilli, mTimeZone);
          long infoEndTime = (info.mEndDay == currentDay) ? info.mEventEndTimeMilli : nextMidnight;
          rowInfo.add(
              new RowInfo(
                  TYPE_MEETING,
                  currentDay,
                  info.mPosition,
                  info.mEventId,
                  info.mEventStartTimeMilli,
                  infoEndTime,
                  info.mInstanceId,
                  info.mAllDay));

          info.mEventStartTimeMilli = nextMidnight;
        }
      }
    }
    mRowInfo = rowInfo;
  }
  @Override
  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 = null;
      View agendaDayView = null;
      if ((convertView != null) && (convertView.getTag() != null)) {
        // Listview may get confused and pass in a different type of
        // view since we keep shifting data around. Not a big problem.
        Object tag = convertView.getTag();
        if (tag instanceof ViewHolder) {
          agendaDayView = convertView;
          holder = (ViewHolder) tag;
          holder.julianDay = row.mDay;
        }
      }

      if (holder == 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.dayView = (TextView) agendaDayView.findViewById(R.id.day);
        holder.dateView = (TextView) agendaDayView.findViewById(R.id.date);
        holder.julianDay = row.mDay;
        holder.grayed = false;
        agendaDayView.setTag(holder);
      }

      // Re-use the member variable "mTime" which is set to the local
      // time zone.
      // It's difficult to find and update all these adapters when the
      // home tz changes so check it here and update if needed.
      String tz = Utils.getTimeZone(mContext, mTZUpdater);
      if (!TextUtils.equals(tz, mTmpTime.timezone)) {
        mTimeZone = tz;
        mTmpTime = new Time(tz);
      }

      // Build the text for the day of the week.
      // Should be yesterday/today/tomorrow (if applicable) + day of the week

      Time date = mTmpTime;
      long millis = date.setJulianDay(row.mDay);
      int flags = DateUtils.FORMAT_SHOW_WEEKDAY;
      mStringBuilder.setLength(0);

      String dayViewText = Utils.getDayOfWeekString(row.mDay, mTodayJulianDay, millis, mContext);

      // Build text for the date
      // Format should be month day

      mStringBuilder.setLength(0);
      flags = DateUtils.FORMAT_SHOW_DATE;
      String dateViewText =
          DateUtils.formatDateRange(mContext, mFormatter, millis, millis, flags, mTimeZone)
              .toString();

      if (AgendaWindowAdapter.BASICLOG) {
        dayViewText += " P:" + position;
        dateViewText += " P:" + position;
      }
      holder.dayView.setText(dayViewText);
      holder.dateView.setText(dateViewText);

      // Set the background of the view, it is grayed for day that are in the past and today
      if (row.mDay > mTodayJulianDay) {
        agendaDayView.setBackgroundResource(R.drawable.agenda_item_bg_primary);
        holder.grayed = false;
      } else {
        agendaDayView.setBackgroundResource(R.drawable.agenda_item_bg_secondary);
        holder.grayed = true;
      }
      return agendaDayView;
    } else if (row.mType == TYPE_MEETING) {
      View itemView = mAgendaAdapter.getView(row.mPosition, convertView, parent);
      AgendaAdapter.ViewHolder holder = ((AgendaAdapter.ViewHolder) itemView.getTag());
      TextView title = holder.title;
      // The holder in the view stores information from the cursor, but the cursor has no
      // notion of multi-day event and the start time of each instance of a multi-day event
      // is the same.  RowInfo has the correct info , so take it from there.
      holder.startTimeMilli = row.mEventStartTimeMilli;
      boolean allDay = holder.allDay;
      if (AgendaWindowAdapter.BASICLOG) {
        title.setText(title.getText() + " P:" + position);
      } else {
        title.setText(title.getText());
      }

      // if event in the past or started already, un-bold the title and set the background
      if ((!allDay && row.mEventStartTimeMilli <= System.currentTimeMillis())
          || (allDay && row.mDay <= mTodayJulianDay)) {
        itemView.setBackgroundResource(R.drawable.agenda_item_bg_secondary);
        title.setTypeface(Typeface.DEFAULT);
        holder.grayed = true;
      } else {
        itemView.setBackgroundResource(R.drawable.agenda_item_bg_primary);
        title.setTypeface(Typeface.DEFAULT_BOLD);
        holder.grayed = false;
      }
      holder.julianDay = row.mDay;
      return itemView;
    } else {
      // Error
      throw new IllegalStateException("Unknown event type:" + row.mType);
    }
  }
 @Override
 public void run() {
   if (mTempTime != null && mListContext != null) {
     mTempTime.timezone = Utils.getTimeZone(mListContext, mTimezoneUpdater);
   }
 }