MonthWeekEventsView.java revision c18dd7aea8c8ee06dd34f268ea9e0e9e7c235b7f
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 17package com.android.calendar.month; 18 19import com.android.calendar.Event; 20import com.android.calendar.R; 21import com.android.calendar.Utils; 22import com.android.calendar.Utils.BusyBitsSegment; 23 24import android.content.Context; 25import android.content.res.Configuration; 26import android.content.res.Resources; 27import android.graphics.Canvas; 28import android.graphics.Paint; 29import android.graphics.Paint.Align; 30import android.graphics.Paint.Style; 31import android.graphics.Typeface; 32import android.graphics.drawable.Drawable; 33import android.text.TextPaint; 34import android.text.TextUtils; 35import android.text.format.DateUtils; 36import android.text.format.Time; 37import android.util.Log; 38 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.Formatter; 42import java.util.HashMap; 43import java.util.Iterator; 44import java.util.List; 45import java.util.Locale; 46 47public class MonthWeekEventsView extends SimpleWeekView { 48 private static final String TAG = "MonthView"; 49 50 public static final String VIEW_PARAMS_ORIENTATION = "orientation"; 51 52 private static int TEXT_SIZE_MONTH_NUMBER = 32; 53 private static int TEXT_SIZE_EVENT = 14; 54 private static int TEXT_SIZE_MORE_EVENTS = 12; 55 private static int TEXT_SIZE_MONTH_NAME = 14; 56 private static int TEXT_SIZE_WEEK_NUM = 12; 57 58 private static final int DEFAULT_EDGE_SPACING = 4; 59 private static int PADDING_MONTH_NUMBER = 4; 60 private static int PADDING_WEEK_NUMBER = 16; 61 private static int DAY_SEPARATOR_OUTER_WIDTH = 5; 62 private static int DAY_SEPARATOR_INNER_WIDTH = 1; 63 private static int DAY_SEPARATOR_VERTICAL_LENGTH = 53; 64 private static int DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT = 64; 65 66 private static int EVENT_X_OFFSET_LANDSCAPE = 44; 67 private static int EVENT_Y_OFFSET_LANDSCAPE = 11; 68 private static int EVENT_Y_OFFSET_PORTRAIT = 18; 69 private static int EVENT_SQUARE_WIDTH = 10; 70 private static int EVENT_SQUARE_BORDER = 1; 71 private static int EVENT_LINE_PADDING = 4; 72 private static int EVENT_RIGHT_PADDING = 4; 73 private static int EVENT_BOTTOM_PADDING = 15; 74 75 private static int BUSY_BITS_MARGIN = 2; 76 private static int BUSY_BITS_WIDTH = 8; 77 78 79 private static int SPACING_WEEK_NUMBER = 19; 80 private static boolean mScaled = false; 81 private boolean mShowDetailsInMonth; 82 83 protected Time mToday = new Time(); 84 protected boolean mHasToday = false; 85 protected int mTodayIndex = -1; 86 protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; 87 protected List<ArrayList<Event>> mEvents = null; 88 // This is for drawing the outlines around event chips and supports up to 10 89 // events being drawn on each day. The code will expand this if necessary. 90 protected FloatRef mEventOutlines = new FloatRef(10 * 4 * 4 * 7); 91 92 protected static StringBuilder mStringBuilder = new StringBuilder(50); 93 // TODO recreate formatter when locale changes 94 protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); 95 96 protected Paint mMonthNamePaint; 97 protected TextPaint mEventPaint; 98 protected TextPaint mEventExtrasPaint; 99 protected Paint mWeekNumPaint; 100 101 protected Drawable mTodayDrawable; 102 103 protected int mMonthNumHeight; 104 protected int mEventHeight; 105 protected int mExtrasHeight; 106 protected int mWeekNumHeight; 107 108 protected int mMonthNumColor; 109 protected int mMonthNumOtherColor; 110 protected int mMonthNumTodayColor; 111 protected int mMonthNameColor; 112 protected int mMonthNameOtherColor; 113 protected int mMonthEventColor; 114 protected int mMonthEventExtraColor; 115 protected int mMonthEventOtherColor; 116 protected int mMonthEventExtraOtherColor; 117 protected int mMonthWeekNumColor; 118 protected int mMonthBusyBitsBgColor; 119 protected int mMonthBusyBitsFgColor; 120 121 protected int mEventChipOutlineColor = 0xFFFFFFFF; 122 protected int mDaySeparatorOuterColor = 0x33FFFFFF; 123 protected int mDaySeparatorInnerColor = 0x1A000000; 124 125 126 /** 127 * This provides a reference to a float array which allows for easy size 128 * checking and reallocation. Used for drawing lines. 129 */ 130 private class FloatRef { 131 float[] array; 132 133 public FloatRef(int size) { 134 array = new float[size]; 135 } 136 137 public void ensureSize(int newSize) { 138 if (newSize >= array.length) { 139 // Add enough space for 7 more boxes to be drawn 140 array = Arrays.copyOf(array, newSize + 16 * 7); 141 } 142 } 143 } 144 145 146 /** 147 * @param context 148 */ 149 public MonthWeekEventsView(Context context) { 150 super(context); 151 152 Resources resources = context.getResources(); 153 TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number); 154 155 156 mPadding = DEFAULT_EDGE_SPACING; 157 if (mScale != 1 && !mScaled) { 158 PADDING_MONTH_NUMBER *= mScale; 159 PADDING_WEEK_NUMBER *= mScale; 160 SPACING_WEEK_NUMBER *= mScale; 161 TEXT_SIZE_MONTH_NUMBER *= mScale; 162 TEXT_SIZE_EVENT *= mScale; 163 TEXT_SIZE_MORE_EVENTS *= mScale; 164 TEXT_SIZE_MONTH_NAME *= mScale; 165 TEXT_SIZE_WEEK_NUM *= mScale; 166 DAY_SEPARATOR_OUTER_WIDTH *= mScale; 167 DAY_SEPARATOR_INNER_WIDTH *= mScale; 168 DAY_SEPARATOR_VERTICAL_LENGTH *= mScale; 169 DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT *= mScale; 170 EVENT_X_OFFSET_LANDSCAPE *= mScale; 171 EVENT_Y_OFFSET_LANDSCAPE *= mScale; 172 EVENT_Y_OFFSET_PORTRAIT *= mScale; 173 EVENT_SQUARE_WIDTH *= mScale; 174 EVENT_LINE_PADDING *= mScale; 175 EVENT_BOTTOM_PADDING *= mScale; 176 EVENT_RIGHT_PADDING *= mScale; 177 BUSY_BITS_MARGIN *= mScale; 178 BUSY_BITS_WIDTH *= mScale; 179 180 mPadding = (int) (DEFAULT_EDGE_SPACING * mScale); 181 mScaled = true; 182 } 183 mShowDetailsInMonth = Utils.getConfigBool(context, R.bool.show_details_in_month); 184 } 185 186 public void setEvents(List<ArrayList<Event>> sortedEvents) { 187 mEvents = sortedEvents; 188 if (sortedEvents == null) { 189 return; 190 } 191 if (sortedEvents.size() != mNumDays) { 192 if (Log.isLoggable(TAG, Log.ERROR)) { 193 Log.wtf(TAG, "Events size must be same as days displayed: size=" 194 + sortedEvents.size() + " days=" + mNumDays); 195 } 196 mEvents = null; 197 return; 198 } 199 } 200 201 protected void loadColors(Context context) { 202 Resources res = context.getResources(); 203 mMonthWeekNumColor = res.getColor(R.color.month_week_num_color); 204 mMonthNumColor = res.getColor(R.color.month_day_number); 205 mMonthNumOtherColor = res.getColor(R.color.month_day_number_other); 206 mMonthNumTodayColor = res.getColor(R.color.month_today_number); 207 mMonthNameColor = mMonthNumColor; 208 mMonthNameOtherColor = mMonthNumOtherColor; 209 mMonthEventColor = res.getColor(R.color.month_event_color); 210 mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color); 211 mMonthEventOtherColor = res.getColor(R.color.month_event_other_color); 212 mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color); 213 mMonthBusyBitsBgColor = res.getColor(R.color.month_busybits_backgound_color); 214 mMonthBusyBitsFgColor = res.getColor(R.color.month_busybits_foregound_color); 215 216 mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light); 217 } 218 219 /** 220 * Sets up the text and style properties for painting. Override this if you 221 * want to use a different paint. 222 */ 223 @Override 224 protected void setPaintProperties() { 225 loadColors(mContext); 226 // TODO modify paint properties depending on isMini 227 p.setStyle(Style.FILL); 228 229 mMonthNumPaint = new Paint(); 230 mMonthNumPaint.setFakeBoldText(false); 231 mMonthNumPaint.setAntiAlias(true); 232 mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER); 233 mMonthNumPaint.setColor(mMonthNumColor); 234 mMonthNumPaint.setStyle(Style.FILL); 235 mMonthNumPaint.setTextAlign(Align.LEFT); 236 mMonthNumPaint.setTypeface(Typeface.DEFAULT_BOLD); 237 238 mMonthNumHeight = (int) (-mMonthNumPaint.ascent()); 239 240 mEventPaint = new TextPaint(); 241 mEventPaint.setFakeBoldText(false); 242 mEventPaint.setAntiAlias(true); 243 mEventPaint.setTextSize(TEXT_SIZE_EVENT); 244 mEventPaint.setColor(mMonthEventColor); 245 246 mEventHeight = (int) (-mEventPaint.ascent()); 247 248 mEventExtrasPaint = new TextPaint(); 249 mEventExtrasPaint.setFakeBoldText(false); 250 mEventExtrasPaint.setAntiAlias(true); 251 mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); 252 mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT); 253 mEventExtrasPaint.setColor(mMonthEventExtraColor); 254 mEventExtrasPaint.setStyle(Style.FILL); 255 mEventExtrasPaint.setTextAlign(Align.LEFT); 256 257 mWeekNumPaint = new Paint(); 258 mWeekNumPaint.setFakeBoldText(false); 259 mWeekNumPaint.setAntiAlias(true); 260 mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM); 261 mWeekNumPaint.setColor(mWeekNumColor); 262 mWeekNumPaint.setStyle(Style.FILL); 263 mWeekNumPaint.setTextAlign(Align.RIGHT); 264 265 mWeekNumHeight = (int) (-mWeekNumPaint.ascent()); 266 } 267 268 @Override 269 public void setWeekParams(HashMap<String, Integer> params, String tz) { 270 super.setWeekParams(params, tz); 271 272 if (params.containsKey(VIEW_PARAMS_ORIENTATION)) { 273 mOrientation = params.get(VIEW_PARAMS_ORIENTATION); 274 } 275 276 mToday.timezone = tz; 277 mToday.setToNow(); 278 mToday.normalize(true); 279 int julianToday = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff); 280 if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) { 281 mHasToday = true; 282 mTodayIndex = julianToday - mFirstJulianDay; 283 } else { 284 mHasToday = false; 285 mTodayIndex = -1; 286 } 287 mNumCells = mNumDays + 1; 288 } 289 290 @Override 291 protected void onDraw(Canvas canvas) { 292 drawBackground(canvas); 293 drawWeekNums(canvas); 294 drawDaySeparators(canvas); 295 if (mShowDetailsInMonth) { 296 drawEvents(canvas); 297 } else { 298 drawBusyBits(canvas); 299 } 300 } 301 302 @Override 303 protected void drawDaySeparators(Canvas canvas) { 304 // mDaySeparatorOuterColor 305 float lines[] = new float[8 * 4]; 306 int count = 7 * 4; 307 int wkNumOffset = 0; 308 int effectiveWidth = mWidth - mPadding * 2; 309 count += 4; 310 wkNumOffset = 1; 311 effectiveWidth -= SPACING_WEEK_NUMBER; 312 lines[0] = mPadding; 313 lines[1] = DAY_SEPARATOR_OUTER_WIDTH / 2 + 1; 314 lines[2] = mWidth - mPadding; 315 lines[3] = lines[1]; 316 int y0 = DAY_SEPARATOR_OUTER_WIDTH / 2 + DAY_SEPARATOR_INNER_WIDTH; 317 int y1; 318 if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { 319 y1 = y0 + DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT; 320 } else { 321 y1 = y0 + DAY_SEPARATOR_VERTICAL_LENGTH; 322 } 323 324 for (int i = 4; i < count;) { 325 int x = (i / 4 - wkNumOffset) * effectiveWidth / (mNumDays) + mPadding 326 + (SPACING_WEEK_NUMBER * wkNumOffset); 327 lines[i++] = x; 328 lines[i++] = y0; 329 lines[i++] = x; 330 lines[i++] = y1; 331 } 332 p.setColor(mDaySeparatorOuterColor); 333 p.setStrokeWidth(DAY_SEPARATOR_OUTER_WIDTH); 334 canvas.drawLines(lines, 0, count, p); 335 p.setColor(mDaySeparatorInnerColor); 336 p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH); 337 canvas.drawLines(lines, 0, count, p); 338 } 339 340 @Override 341 protected void drawBackground(Canvas canvas) { 342 if (mHasToday) { 343 p.setColor(mSelectedWeekBGColor); 344 } else { 345 return; 346 } 347 int wkNumOffset = 0; 348 int effectiveWidth = mWidth - mPadding * 2; 349 wkNumOffset = 1; 350 effectiveWidth -= SPACING_WEEK_NUMBER; 351 r.top = DAY_SEPARATOR_OUTER_WIDTH + 1; 352 r.bottom = mHeight; 353 r.left = (mTodayIndex) * effectiveWidth / (mNumDays) + mPadding 354 + (SPACING_WEEK_NUMBER * wkNumOffset) + DAY_SEPARATOR_OUTER_WIDTH / 2 + 1; 355 r.right = (mTodayIndex + 1) * effectiveWidth / (mNumDays) + mPadding 356 + (SPACING_WEEK_NUMBER * wkNumOffset) - DAY_SEPARATOR_OUTER_WIDTH / 2; 357 mTodayDrawable.setBounds(r); 358 mTodayDrawable.draw(canvas); 359 } 360 361 @Override 362 protected void drawWeekNums(Canvas canvas) { 363 int y; 364 365 int i = 0; 366 int offset = 0; 367 int effectiveWidth = mWidth - mPadding * 2; 368 int todayIndex = mTodayIndex; 369 int x = PADDING_WEEK_NUMBER + mPadding; 370 int numCount = mNumDays; 371 y = mWeekNumHeight + PADDING_MONTH_NUMBER; 372 if (mShowWeekNum) { 373 canvas.drawText(mDayNumbers[0], x, y, mWeekNumPaint); 374 numCount++; 375 i++; 376 todayIndex++; 377 offset++; 378 } 379 effectiveWidth -= SPACING_WEEK_NUMBER; 380 381 y = (mMonthNumHeight + PADDING_MONTH_NUMBER); 382 383 boolean isFocusMonth = mFocusDay[i]; 384 mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); 385 for (; i < numCount; i++) { 386 if (mHasToday && todayIndex == i) { 387 mMonthNumPaint.setColor(mMonthNumTodayColor); 388 if (i + 1 < numCount) { 389 // Make sure the color will be set back on the next 390 // iteration 391 isFocusMonth = !mFocusDay[i + 1]; 392 } 393 } else if (mFocusDay[i] != isFocusMonth) { 394 isFocusMonth = mFocusDay[i]; 395 mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); 396 } 397 x = (i - offset) * effectiveWidth / (mNumDays) + mPadding + PADDING_MONTH_NUMBER 398 + SPACING_WEEK_NUMBER; 399 canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); 400 401 } 402 } 403 404 protected void drawEvents(Canvas canvas) { 405 if (mEvents == null) { 406 return; 407 } 408 int wkNumOffset = 0; 409 int effectiveWidth = mWidth - mPadding * 2; 410 wkNumOffset = 1; 411 effectiveWidth -= SPACING_WEEK_NUMBER; 412 413 int day = -1; 414 int outlineCount = 0; 415 for (ArrayList<Event> eventDay : mEvents) { 416 day++; 417 if (eventDay == null || eventDay.size() == 0) { 418 continue; 419 } 420 int ySquare; 421 int xSquare = day * effectiveWidth / (mNumDays) + mPadding 422 + (SPACING_WEEK_NUMBER * wkNumOffset); 423 if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { 424 ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + PADDING_MONTH_NUMBER; 425 xSquare += PADDING_MONTH_NUMBER + 1; 426 } else { 427 ySquare = EVENT_Y_OFFSET_LANDSCAPE; 428 xSquare += EVENT_X_OFFSET_LANDSCAPE; 429 } 430 int rightEdge = (day + 1) * effectiveWidth / (mNumDays) + mPadding 431 + (SPACING_WEEK_NUMBER * wkNumOffset) - EVENT_RIGHT_PADDING; 432 int eventCount = 0; 433 Iterator<Event> iter = eventDay.iterator(); 434 while (iter.hasNext()) { 435 Event event = iter.next(); 436 int newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext()); 437 if (newY == ySquare) { 438 break; 439 } 440 outlineCount = addChipOutline(mEventOutlines, outlineCount, xSquare, ySquare); 441 eventCount++; 442 ySquare = newY; 443 } 444 445 int remaining = eventDay.size() - eventCount; 446 if (remaining > 0) { 447 drawMoreEvents(canvas, remaining, xSquare); 448 } 449 } 450 if (outlineCount > 0) { 451 p.setColor(mEventChipOutlineColor); 452 p.setStrokeWidth(EVENT_SQUARE_BORDER); 453 canvas.drawLines(mEventOutlines.array, 0, outlineCount, p); 454 } 455 } 456 457 protected int addChipOutline(FloatRef lines, int count, int x, int y) { 458 lines.ensureSize(count + 16); 459 // top of box 460 lines.array[count++] = x; 461 lines.array[count++] = y; 462 lines.array[count++] = x + EVENT_SQUARE_WIDTH; 463 lines.array[count++] = y; 464 // right side of box 465 lines.array[count++] = x + EVENT_SQUARE_WIDTH; 466 lines.array[count++] = y; 467 lines.array[count++] = x + EVENT_SQUARE_WIDTH; 468 lines.array[count++] = y + EVENT_SQUARE_WIDTH; 469 // left side of box 470 lines.array[count++] = x; 471 lines.array[count++] = y; 472 lines.array[count++] = x; 473 lines.array[count++] = y + EVENT_SQUARE_WIDTH + 1; 474 // bottom of box 475 lines.array[count++] = x; 476 lines.array[count++] = y + EVENT_SQUARE_WIDTH; 477 lines.array[count++] = x + EVENT_SQUARE_WIDTH + 1; 478 lines.array[count++] = y + EVENT_SQUARE_WIDTH; 479 480 return count; 481 } 482 483 /** 484 * Attempts to draw the given event. Returns the y for the next event or the 485 * original y if the event will not fit. An event is considered to not fit 486 * if the event and its extras won't fit or if there are more events and the 487 * more events line would not fit after drawing this event. 488 * 489 * @param event the event to draw 490 * @param x the top left corner for this event's color chip 491 * @param y the top left corner for this event's color chip 492 * @return the y for the next event or the original y if it won't fit 493 */ 494 protected int drawEvent( 495 Canvas canvas, Event event, int x, int y, int rightEdge, boolean moreEvents) { 496 int requiredSpace = EVENT_LINE_PADDING + mEventHeight; 497 int multiplier = 1; 498 if (moreEvents) { 499 multiplier++; 500 } 501 if (!event.allDay) { 502 multiplier++; 503 } 504 requiredSpace *= multiplier; 505 if (requiredSpace + y >= mHeight - EVENT_BOTTOM_PADDING) { 506 // Not enough space, return 507 return y; 508 } 509 r.left = x; 510 r.right = x + EVENT_SQUARE_WIDTH; 511 r.top = y; 512 r.bottom = y + EVENT_SQUARE_WIDTH; 513 p.setColor(event.color); 514 canvas.drawRect(r, p); 515 516 int textX = x + EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING; 517 int textY = y + mEventHeight - EVENT_LINE_PADDING / 2; 518 float avail = rightEdge - textX; 519 CharSequence text = TextUtils.ellipsize( 520 event.title, mEventPaint, avail, TextUtils.TruncateAt.END); 521 canvas.drawText(text.toString(), textX, textY, mEventPaint); 522 if (!event.allDay) { 523 textY += mEventHeight + EVENT_LINE_PADDING; 524 mStringBuilder.setLength(0); 525 text = DateUtils.formatDateRange(mContext, mFormatter, event.startMillis, 526 event.endMillis, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL, 527 Utils.getTimeZone(mContext, null)).toString(); 528 text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END); 529 canvas.drawText(text.toString(), textX, textY, mEventExtrasPaint); 530 } 531 532 return textY + EVENT_LINE_PADDING; 533 } 534 535 protected void drawMoreEvents(Canvas canvas, int remainingEvents, int x) { 536 FloatRef lines = new FloatRef(4 * 4); 537 int y = mHeight - EVENT_BOTTOM_PADDING + EVENT_LINE_PADDING / 2 - mEventHeight; 538 addChipOutline(lines, 0, x, y); 539 canvas.drawLines(lines.array, mEventExtrasPaint); 540 String text = mContext.getResources().getQuantityString( 541 R.plurals.month_more_events, remainingEvents); 542 y = mHeight - EVENT_BOTTOM_PADDING; 543 x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING; 544 mEventExtrasPaint.setFakeBoldText(true); 545 canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint); 546 mEventExtrasPaint.setFakeBoldText(false); 547 } 548 549 // Draws the DNA view of events 550 551 protected void drawBusyBits(Canvas canvas) { 552 553 int wkNumOffset = 1; 554 int effectiveWidth = mWidth - mPadding * 2 - SPACING_WEEK_NUMBER; 555 int top = DAY_SEPARATOR_OUTER_WIDTH + BUSY_BITS_MARGIN; 556 int bottom = mHeight - BUSY_BITS_MARGIN; 557 558 // Draw background for all days first since even if there are not 559 // events, we still need to show the background bar 560 561 for (int i = 1; i <= mNumDays; i++) { 562 r.top = top; 563 r.bottom = bottom; 564 r.right = i * effectiveWidth / (mNumDays) + mPadding 565 + (SPACING_WEEK_NUMBER * wkNumOffset) - DAY_SEPARATOR_OUTER_WIDTH / 2; 566 r.left = r.right - BUSY_BITS_WIDTH; 567 p.setColor(mMonthBusyBitsBgColor); 568 p.setStyle(Style.FILL); 569 canvas.drawRect(r, p); 570 } 571 572 if (mEvents != null) { 573 p.setColor(mMonthBusyBitsFgColor); 574 p.setStyle(Style.FILL); 575 int day = 0; 576 577 // For each day draw events 578 579 for (ArrayList<Event> eventDay : mEvents) { 580 // Create a list of segments that correspond to busy times 581 ArrayList<BusyBitsSegment> segments = Utils.createBusyBitSegments(top, bottom, 0, 582 24 * 60, mFirstJulianDay + day, eventDay); 583 day++; 584 if (segments == null) { 585 continue; 586 } 587 // iterate and draw each segment 588 for (BusyBitsSegment s : segments) { 589 r.right = day * effectiveWidth / (mNumDays) + mPadding 590 + (SPACING_WEEK_NUMBER * wkNumOffset) - DAY_SEPARATOR_OUTER_WIDTH / 2; 591 r.left = r.right - BUSY_BITS_WIDTH; 592 r.top = s.start; 593 r.bottom = s.end; 594 canvas.drawRect(r, p); 595 } 596 } 597 } 598 } 599 600 @Override 601 protected void updateSelectionPositions() { 602 if (mHasSelectedDay) { 603 int selectedPosition = mSelectedDay - mWeekStart; 604 if (selectedPosition < 0) { 605 selectedPosition += 7; 606 } 607 int effectiveWidth = mWidth - mPadding * 2; 608 effectiveWidth -= SPACING_WEEK_NUMBER; 609 mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding; 610 mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding; 611 mSelectedLeft += SPACING_WEEK_NUMBER; 612 mSelectedRight += SPACING_WEEK_NUMBER; 613 } 614 } 615 616 @Override 617 public Time getDayFromLocation(float x) { 618 int dayStart = SPACING_WEEK_NUMBER + mPadding; 619 if (x < dayStart || x > mWidth - mPadding) { 620 return null; 621 } 622 // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels 623 int dayPosition = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); 624 int day = mFirstJulianDay + dayPosition; 625 626 Time time = new Time(mTimeZone); 627 if (mWeek == 0) { 628 // This week is weird... 629 if (day < Time.EPOCH_JULIAN_DAY) { 630 day++; 631 } else if (day == Time.EPOCH_JULIAN_DAY) { 632 time.set(1, 0, 1970); 633 time.normalize(true); 634 return time; 635 } 636 } 637 638 time.setJulianDay(day); 639 return time; 640 } 641 642} 643