182b318634af1b6594e60cfd1f2df4d3845f80587Erik/*
282b318634af1b6594e60cfd1f2df4d3845f80587Erik * Copyright (C) 2010 The Android Open Source Project
382b318634af1b6594e60cfd1f2df4d3845f80587Erik *
482b318634af1b6594e60cfd1f2df4d3845f80587Erik * Licensed under the Apache License, Version 2.0 (the "License");
582b318634af1b6594e60cfd1f2df4d3845f80587Erik * you may not use this file except in compliance with the License.
682b318634af1b6594e60cfd1f2df4d3845f80587Erik * You may obtain a copy of the License at
782b318634af1b6594e60cfd1f2df4d3845f80587Erik *
882b318634af1b6594e60cfd1f2df4d3845f80587Erik *      http://www.apache.org/licenses/LICENSE-2.0
982b318634af1b6594e60cfd1f2df4d3845f80587Erik *
1082b318634af1b6594e60cfd1f2df4d3845f80587Erik * Unless required by applicable law or agreed to in writing, software
1182b318634af1b6594e60cfd1f2df4d3845f80587Erik * distributed under the License is distributed on an "AS IS" BASIS,
1282b318634af1b6594e60cfd1f2df4d3845f80587Erik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1382b318634af1b6594e60cfd1f2df4d3845f80587Erik * See the License for the specific language governing permissions and
1482b318634af1b6594e60cfd1f2df4d3845f80587Erik * limitations under the License.
1582b318634af1b6594e60cfd1f2df4d3845f80587Erik */
1682b318634af1b6594e60cfd1f2df4d3845f80587Erik
1782b318634af1b6594e60cfd1f2df4d3845f80587Erikpackage com.android.calendar.month;
1882b318634af1b6594e60cfd1f2df4d3845f80587Erik
193553621c824afe1f3eacbe8555678005ca1264a1Erikimport com.android.calendar.Event;
203553621c824afe1f3eacbe8555678005ca1264a1Erikimport com.android.calendar.R;
2140bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport com.android.calendar.Utils;
22f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik
235b872523e28fc778a3fc34e7e9096ca1197c6509RoboErikimport android.animation.Animator;
245b872523e28fc778a3fc34e7e9096ca1197c6509RoboErikimport android.animation.AnimatorListenerAdapter;
255b872523e28fc778a3fc34e7e9096ca1197c6509RoboErikimport android.animation.ObjectAnimator;
26f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErikimport android.app.Service;
2782b318634af1b6594e60cfd1f2df4d3845f80587Erikimport android.content.Context;
283553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.content.res.Configuration;
293553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.content.res.Resources;
303553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.graphics.Canvas;
31092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErikimport android.graphics.Color;
323553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.graphics.Paint;
333553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.graphics.Paint.Align;
343553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.graphics.Paint.Style;
3540bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport android.graphics.Typeface;
3640bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport android.graphics.drawable.Drawable;
37a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErikimport android.provider.CalendarContract.Attendees;
3840bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport android.text.TextPaint;
3940bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport android.text.TextUtils;
4067f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErikimport android.text.format.DateFormat;
4140bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport android.text.format.DateUtils;
423553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.text.format.Time;
433553621c824afe1f3eacbe8555678005ca1264a1Erikimport android.util.Log;
4467f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErikimport android.view.MotionEvent;
4567f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErikimport android.view.accessibility.AccessibilityEvent;
46f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErikimport android.view.accessibility.AccessibilityManager;
473553621c824afe1f3eacbe8555678005ca1264a1Erik
483553621c824afe1f3eacbe8555678005ca1264a1Erikimport java.util.ArrayList;
49f445411ff661014c4b5a37e2f97552989e9278ecRoboErikimport java.util.Arrays;
5040bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport java.util.Formatter;
513553621c824afe1f3eacbe8555678005ca1264a1Erikimport java.util.HashMap;
5240bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport java.util.Iterator;
5340bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport java.util.List;
5440bcd101b212c9863c3110c05a487a7ae6ebc3caErikimport java.util.Locale;
5582b318634af1b6594e60cfd1f2df4d3845f80587Erik
56e21674aefc9755ba519dfc5b3ff4348be4814903Erikpublic class MonthWeekEventsView extends SimpleWeekView {
57092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik
583553621c824afe1f3eacbe8555678005ca1264a1Erik    private static final String TAG = "MonthView";
593553621c824afe1f3eacbe8555678005ca1264a1Erik
6069f9b450fc255203b758f445163f371c22c816c0Andy McFadden    private static final boolean DEBUG_LAYOUT = false;
6169f9b450fc255203b758f445163f371c22c816c0Andy McFadden
623553621c824afe1f3eacbe8555678005ca1264a1Erik    public static final String VIEW_PARAMS_ORIENTATION = "orientation";
635b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public static final String VIEW_PARAMS_ANIMATE_TODAY = "animate_today";
643553621c824afe1f3eacbe8555678005ca1264a1Erik
6569f9b450fc255203b758f445163f371c22c816c0Andy McFadden    /* NOTE: these are not constants, and may be multiplied by a scale factor */
663553621c824afe1f3eacbe8555678005ca1264a1Erik    private static int TEXT_SIZE_MONTH_NUMBER = 32;
67c76ed496f5aa09ec85cc304b70c527c73cf8243eRoboErik    private static int TEXT_SIZE_EVENT = 12;
68c76ed496f5aa09ec85cc304b70c527c73cf8243eRoboErik    private static int TEXT_SIZE_EVENT_TITLE = 14;
693553621c824afe1f3eacbe8555678005ca1264a1Erik    private static int TEXT_SIZE_MORE_EVENTS = 12;
703553621c824afe1f3eacbe8555678005ca1264a1Erik    private static int TEXT_SIZE_MONTH_NAME = 14;
713553621c824afe1f3eacbe8555678005ca1264a1Erik    private static int TEXT_SIZE_WEEK_NUM = 12;
723553621c824afe1f3eacbe8555678005ca1264a1Erik
73092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int DNA_MARGIN = 4;
74092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int DNA_ALL_DAY_HEIGHT = 4;
75092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int DNA_MIN_SEGMENT_HEIGHT = 4;
76092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int DNA_WIDTH = 8;
77092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int DNA_ALL_DAY_WIDTH = 32;
78092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int DNA_SIDE_PADDING = 6;
79092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int CONFLICT_COLOR = Color.BLACK;
80458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden    private static int EVENT_TEXT_COLOR = Color.WHITE;
81092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik
823bff945d29d6ef277391745cd160f3bf16b98905RoboErik    private static int DEFAULT_EDGE_SPACING = 0;
833bff945d29d6ef277391745cd160f3bf16b98905RoboErik    private static int SIDE_PADDING_MONTH_NUMBER = 4;
84763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int TOP_PADDING_MONTH_NUMBER = 4;
85092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int TOP_PADDING_WEEK_NUMBER = 4;
86092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int SIDE_PADDING_WEEK_NUMBER = 20;
873bff945d29d6ef277391745cd160f3bf16b98905RoboErik    private static int DAY_SEPARATOR_OUTER_WIDTH = 0;
8840bcd101b212c9863c3110c05a487a7ae6ebc3caErik    private static int DAY_SEPARATOR_INNER_WIDTH = 1;
8940bcd101b212c9863c3110c05a487a7ae6ebc3caErik    private static int DAY_SEPARATOR_VERTICAL_LENGTH = 53;
9040bcd101b212c9863c3110c05a487a7ae6ebc3caErik    private static int DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT = 64;
91092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int MIN_WEEK_WIDTH = 50;
9240bcd101b212c9863c3110c05a487a7ae6ebc3caErik
93f658df6b9c285f24c45cdca3ab90f2ca65e5b7cfRoboErik    private static int EVENT_X_OFFSET_LANDSCAPE = 38;
9469f9b450fc255203b758f445163f371c22c816c0Andy McFadden    private static int EVENT_Y_OFFSET_LANDSCAPE = 8;
9569f9b450fc255203b758f445163f371c22c816c0Andy McFadden    private static int EVENT_Y_OFFSET_PORTRAIT = 7;
9640bcd101b212c9863c3110c05a487a7ae6ebc3caErik    private static int EVENT_SQUARE_WIDTH = 10;
97a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik    private static int EVENT_SQUARE_BORDER = 2;
9869f9b450fc255203b758f445163f371c22c816c0Andy McFadden    private static int EVENT_LINE_PADDING = 2;
9940bcd101b212c9863c3110c05a487a7ae6ebc3caErik    private static int EVENT_RIGHT_PADDING = 4;
10069f9b450fc255203b758f445163f371c22c816c0Andy McFadden    private static int EVENT_BOTTOM_PADDING = 3;
1013553621c824afe1f3eacbe8555678005ca1264a1Erik
1025b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private static int TODAY_HIGHLIGHT_WIDTH = 2;
10382400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson
104092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private static int SPACING_WEEK_NUMBER = 24;
105458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden    private static boolean mInitialized = false;
1063bff945d29d6ef277391745cd160f3bf16b98905RoboErik    private static boolean mShowDetailsInMonth;
1073553621c824afe1f3eacbe8555678005ca1264a1Erik
1083553621c824afe1f3eacbe8555678005ca1264a1Erik    protected Time mToday = new Time();
1093553621c824afe1f3eacbe8555678005ca1264a1Erik    protected boolean mHasToday = false;
1103553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mTodayIndex = -1;
1113553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE;
11240bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected List<ArrayList<Event>> mEvents = null;
1135b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    protected ArrayList<Event> mUnsortedEvents = null;
114092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    HashMap<Integer, Utils.DNAStrand> mDna = null;
115f445411ff661014c4b5a37e2f97552989e9278ecRoboErik    // This is for drawing the outlines around event chips and supports up to 10
116f445411ff661014c4b5a37e2f97552989e9278ecRoboErik    // events being drawn on each day. The code will expand this if necessary.
117f445411ff661014c4b5a37e2f97552989e9278ecRoboErik    protected FloatRef mEventOutlines = new FloatRef(10 * 4 * 4 * 7);
11840bcd101b212c9863c3110c05a487a7ae6ebc3caErik
11971b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson
12071b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson
12140bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected static StringBuilder mStringBuilder = new StringBuilder(50);
12240bcd101b212c9863c3110c05a487a7ae6ebc3caErik    // TODO recreate formatter when locale changes
12340bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
1243553621c824afe1f3eacbe8555678005ca1264a1Erik
1253553621c824afe1f3eacbe8555678005ca1264a1Erik    protected Paint mMonthNamePaint;
12640bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected TextPaint mEventPaint;
127458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden    protected TextPaint mSolidBackgroundEventPaint;
128458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden    protected TextPaint mFramedEventPaint;
129210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik    protected TextPaint mDeclinedEventPaint;
13040bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected TextPaint mEventExtrasPaint;
131210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik    protected TextPaint mEventDeclinedExtrasPaint;
1323553621c824afe1f3eacbe8555678005ca1264a1Erik    protected Paint mWeekNumPaint;
133092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    protected Paint mDNAAllDayPaint;
134092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    protected Paint mDNATimePaint;
135a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik    protected Paint mEventSquarePaint;
13671b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson
1373553621c824afe1f3eacbe8555678005ca1264a1Erik
13840bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected Drawable mTodayDrawable;
13940bcd101b212c9863c3110c05a487a7ae6ebc3caErik
1403553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthNumHeight;
14169f9b450fc255203b758f445163f371c22c816c0Andy McFadden    protected int mMonthNumAscentHeight;
1423553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mEventHeight;
14369f9b450fc255203b758f445163f371c22c816c0Andy McFadden    protected int mEventAscentHeight;
1443553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mExtrasHeight;
14569f9b450fc255203b758f445163f371c22c816c0Andy McFadden    protected int mExtrasAscentHeight;
14669f9b450fc255203b758f445163f371c22c816c0Andy McFadden    protected int mExtrasDescent;
14769f9b450fc255203b758f445163f371c22c816c0Andy McFadden    protected int mWeekNumAscentHeight;
1483553621c824afe1f3eacbe8555678005ca1264a1Erik
14967d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik    protected int mMonthBGColor;
15067d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik    protected int mMonthBGOtherColor;
15167d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik    protected int mMonthBGTodayColor;
1523553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthNumColor;
1533553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthNumOtherColor;
1543553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthNumTodayColor;
1553553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthNameColor;
1563553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthNameOtherColor;
1573553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthEventColor;
158210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik    protected int mMonthDeclinedEventColor;
159210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik    protected int mMonthDeclinedExtrasColor;
1603553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthEventExtraColor;
1613553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthEventOtherColor;
1623553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthEventExtraOtherColor;
1633553621c824afe1f3eacbe8555678005ca1264a1Erik    protected int mMonthWeekNumColor;
16482400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson    protected int mMonthBusyBitsBgColor;
16571b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson    protected int mMonthBusyBitsBusyTimeColor;
16671b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson    protected int mMonthBusyBitsConflictTimeColor;
16759a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    private int mClickedDayIndex = -1;
16859a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    private int mClickedDayColor;
16959a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    private static final int mClickedAlpha = 128;
17082b318634af1b6594e60cfd1f2df4d3845f80587Erik
17140bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected int mEventChipOutlineColor = 0xFFFFFFFF;
172e9baa2499d9592343e30c983116e3f8dc921d3b0RoboErik    protected int mDaySeparatorInnerColor;
1735b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    protected int mTodayAnimateColor;
1745b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
1755b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private boolean mAnimateToday;
1765b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private int mAnimateTodayAlpha = 0;
1775b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private ObjectAnimator mTodayAnimator = null;
1785b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
17959a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    private final TodayAnimatorListener mAnimatorListener = new TodayAnimatorListener();
1805b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
1815b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    class TodayAnimatorListener extends AnimatorListenerAdapter {
1825b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        private volatile Animator mAnimator = null;
1835b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        private volatile boolean mFadingIn = false;
1845b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
1855b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        @Override
1865b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void onAnimationEnd(Animator animation) {
1875b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            synchronized (this) {
1885b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mAnimator != animation) {
1895b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    animation.removeAllListeners();
1905b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    animation.cancel();
1915b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    return;
1925b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
1935b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mFadingIn) {
1945b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    if (mTodayAnimator != null) {
1955b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mTodayAnimator.removeAllListeners();
1965b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mTodayAnimator.cancel();
1975b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    }
1985b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator = ObjectAnimator.ofInt(MonthWeekEventsView.this,
1995b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                            "animateTodayAlpha", 255, 0);
2005b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator = mTodayAnimator;
2015b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mFadingIn = false;
2025b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.addListener(this);
2035b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.setDuration(600);
2045b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.start();
2055b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                } else {
2065b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimateToday = false;
2075b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimateTodayAlpha = 0;
2085b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator.removeAllListeners();
2095b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator = null;
2105b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator = null;
2115b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    invalidate();
2125b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
2135b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            }
2145b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2155b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2165b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void setAnimator(Animator animation) {
2175b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mAnimator = animation;
2185b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2195b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2205b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void setFadingIn(boolean fadingIn) {
2215b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mFadingIn = fadingIn;
2225b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2235b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2245b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
22540bcd101b212c9863c3110c05a487a7ae6ebc3caErik
226092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private int[] mDayXs;
22782400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson
22882b318634af1b6594e60cfd1f2df4d3845f80587Erik    /**
229f445411ff661014c4b5a37e2f97552989e9278ecRoboErik     * This provides a reference to a float array which allows for easy size
230f445411ff661014c4b5a37e2f97552989e9278ecRoboErik     * checking and reallocation. Used for drawing lines.
231f445411ff661014c4b5a37e2f97552989e9278ecRoboErik     */
232f445411ff661014c4b5a37e2f97552989e9278ecRoboErik    private class FloatRef {
233f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        float[] array;
234f445411ff661014c4b5a37e2f97552989e9278ecRoboErik
235f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        public FloatRef(int size) {
236f445411ff661014c4b5a37e2f97552989e9278ecRoboErik            array = new float[size];
237f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        }
238f445411ff661014c4b5a37e2f97552989e9278ecRoboErik
239f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        public void ensureSize(int newSize) {
240f445411ff661014c4b5a37e2f97552989e9278ecRoboErik            if (newSize >= array.length) {
241f445411ff661014c4b5a37e2f97552989e9278ecRoboErik                // Add enough space for 7 more boxes to be drawn
242f445411ff661014c4b5a37e2f97552989e9278ecRoboErik                array = Arrays.copyOf(array, newSize + 16 * 7);
243f445411ff661014c4b5a37e2f97552989e9278ecRoboErik            }
244f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        }
245f445411ff661014c4b5a37e2f97552989e9278ecRoboErik    }
246f445411ff661014c4b5a37e2f97552989e9278ecRoboErik
247f445411ff661014c4b5a37e2f97552989e9278ecRoboErik    /**
2485b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik     * Shows up as an error if we don't include this.
24982b318634af1b6594e60cfd1f2df4d3845f80587Erik     */
25082b318634af1b6594e60cfd1f2df4d3845f80587Erik    public MonthWeekEventsView(Context context) {
25182b318634af1b6594e60cfd1f2df4d3845f80587Erik        super(context);
252092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    }
253092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik
254092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    // Sets the list of events for this week. Takes a sorted list of arrays
255092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    // divided up by day for generating the large month version and the full
256092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    // arraylist sorted by start time to generate the dna version.
257092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    public void setEvents(List<ArrayList<Event>> sortedEvents, ArrayList<Event> unsortedEvents) {
258092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        setEvents(sortedEvents);
259092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        // The MIN_WEEK_WIDTH is a hack to prevent the view from trying to
260092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        // generate dna bits before its width has been fixed.
2615b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        createDna(unsortedEvents);
2625b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
2635b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2645b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    /**
2655b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik     * Sets up the dna bits for the view. This will return early if the view
2665b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik     * isn't in a state that will create a valid set of dna yet (such as the
2675b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik     * views width not being set correctly yet).
2685b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik     */
2695b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public void createDna(ArrayList<Event> unsortedEvents) {
2707d66103066f04ba2b7c4ddfcfdafd0368ad9d9cbAndy McFadden        if (unsortedEvents == null || mWidth <= MIN_WEEK_WIDTH || getContext() == null) {
2715b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            // Stash the list of events for use when this view is ready, or
2725b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            // just clear it if a null set has been passed to this view
2735b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mUnsortedEvents = unsortedEvents;
274092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            mDna = null;
275092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            return;
2765b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        } else {
2775b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            // clear the cached set of events since we're ready to build it now
2785b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mUnsortedEvents = null;
279092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        }
280092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        // Create the drawing coordinates for dna
281092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        if (!mShowDetailsInMonth) {
282092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            int numDays = mEvents.size();
283763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik            int effectiveWidth = mWidth - mPadding * 2;
284763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik            if (mShowWeekNum) {
285763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik                effectiveWidth -= SPACING_WEEK_NUMBER;
286763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik            }
287092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            DNA_ALL_DAY_WIDTH = effectiveWidth / numDays - 2 * DNA_SIDE_PADDING;
288092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH);
289092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            mDayXs = new int[numDays];
290092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            for (int day = 0; day < numDays; day++) {
291092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                mDayXs[day] = computeDayLeftPosition(day) + DNA_WIDTH / 2 + DNA_SIDE_PADDING;
292092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik
293092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            }
294092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik
295092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            int top = DAY_SEPARATOR_INNER_WIDTH + DNA_MARGIN + DNA_ALL_DAY_HEIGHT + 1;
296092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            int bottom = mHeight - DNA_MARGIN;
297092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            mDna = Utils.createDNAStrands(mFirstJulianDay, unsortedEvents, top, bottom,
2987d66103066f04ba2b7c4ddfcfdafd0368ad9d9cbAndy McFadden                    DNA_MIN_SEGMENT_HEIGHT, mDayXs, getContext());
299092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        }
3003553621c824afe1f3eacbe8555678005ca1264a1Erik    }
3013553621c824afe1f3eacbe8555678005ca1264a1Erik
30240bcd101b212c9863c3110c05a487a7ae6ebc3caErik    public void setEvents(List<ArrayList<Event>> sortedEvents) {
3033553621c824afe1f3eacbe8555678005ca1264a1Erik        mEvents = sortedEvents;
3043553621c824afe1f3eacbe8555678005ca1264a1Erik        if (sortedEvents == null) {
3053553621c824afe1f3eacbe8555678005ca1264a1Erik            return;
3063553621c824afe1f3eacbe8555678005ca1264a1Erik        }
3073553621c824afe1f3eacbe8555678005ca1264a1Erik        if (sortedEvents.size() != mNumDays) {
3083553621c824afe1f3eacbe8555678005ca1264a1Erik            if (Log.isLoggable(TAG, Log.ERROR)) {
3093553621c824afe1f3eacbe8555678005ca1264a1Erik                Log.wtf(TAG, "Events size must be same as days displayed: size="
3103553621c824afe1f3eacbe8555678005ca1264a1Erik                        + sortedEvents.size() + " days=" + mNumDays);
3113553621c824afe1f3eacbe8555678005ca1264a1Erik            }
3123553621c824afe1f3eacbe8555678005ca1264a1Erik            mEvents = null;
3133553621c824afe1f3eacbe8555678005ca1264a1Erik            return;
3143553621c824afe1f3eacbe8555678005ca1264a1Erik        }
3153553621c824afe1f3eacbe8555678005ca1264a1Erik    }
3163553621c824afe1f3eacbe8555678005ca1264a1Erik
3173553621c824afe1f3eacbe8555678005ca1264a1Erik    protected void loadColors(Context context) {
3183553621c824afe1f3eacbe8555678005ca1264a1Erik        Resources res = context.getResources();
3193553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthWeekNumColor = res.getColor(R.color.month_week_num_color);
3203553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumColor = res.getColor(R.color.month_day_number);
3213553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumOtherColor = res.getColor(R.color.month_day_number_other);
3223553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumTodayColor = res.getColor(R.color.month_today_number);
3233553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNameColor = mMonthNumColor;
3243553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNameOtherColor = mMonthNumOtherColor;
3253553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthEventColor = res.getColor(R.color.month_event_color);
326210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mMonthDeclinedEventColor = res.getColor(R.color.agenda_item_declined_color);
327210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mMonthDeclinedExtrasColor = res.getColor(R.color.agenda_item_where_declined_text_color);
3283553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color);
3293553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthEventOtherColor = res.getColor(R.color.month_event_other_color);
3303553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color);
33167d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        mMonthBGTodayColor = res.getColor(R.color.month_today_bgcolor);
33267d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        mMonthBGOtherColor = res.getColor(R.color.month_other_bgcolor);
33367d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        mMonthBGColor = res.getColor(R.color.month_bgcolor);
334e9baa2499d9592343e30c983116e3f8dc921d3b0RoboErik        mDaySeparatorInnerColor = res.getColor(R.color.month_grid_lines);
3355b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        mTodayAnimateColor = res.getColor(R.color.today_highlight_color);
33659a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        mClickedDayColor = res.getColor(R.color.day_clicked_background_color);
33740bcd101b212c9863c3110c05a487a7ae6ebc3caErik        mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light);
3383553621c824afe1f3eacbe8555678005ca1264a1Erik    }
3393553621c824afe1f3eacbe8555678005ca1264a1Erik
3403553621c824afe1f3eacbe8555678005ca1264a1Erik    /**
3413553621c824afe1f3eacbe8555678005ca1264a1Erik     * Sets up the text and style properties for painting. Override this if you
3423553621c824afe1f3eacbe8555678005ca1264a1Erik     * want to use a different paint.
3433553621c824afe1f3eacbe8555678005ca1264a1Erik     */
3443553621c824afe1f3eacbe8555678005ca1264a1Erik    @Override
3457203d809b39fb07caab177d8e8bd9428097b73b6Erik    protected void initView() {
3467203d809b39fb07caab177d8e8bd9428097b73b6Erik        super.initView();
3477203d809b39fb07caab177d8e8bd9428097b73b6Erik
348458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (!mInitialized) {
3493bff945d29d6ef277391745cd160f3bf16b98905RoboErik            Resources resources = getContext().getResources();
3507d66103066f04ba2b7c4ddfcfdafd0368ad9d9cbAndy McFadden            mShowDetailsInMonth = Utils.getConfigBool(getContext(), R.bool.show_details_in_month);
351e0942157628776cff5573da8ce85fb3f3c3cfc35Mindy Pereira            TEXT_SIZE_EVENT_TITLE = resources.getInteger(R.integer.text_size_event_title);
3523bff945d29d6ef277391745cd160f3bf16b98905RoboErik            TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number);
3533bff945d29d6ef277391745cd160f3bf16b98905RoboErik            SIDE_PADDING_MONTH_NUMBER = resources.getInteger(R.integer.month_day_number_margin);
354092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            CONFLICT_COLOR = resources.getColor(R.color.month_dna_conflict_time_color);
355458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            EVENT_TEXT_COLOR = resources.getColor(R.color.calendar_event_text_color);
3563bff945d29d6ef277391745cd160f3bf16b98905RoboErik            if (mScale != 1) {
3573bff945d29d6ef277391745cd160f3bf16b98905RoboErik                TOP_PADDING_MONTH_NUMBER *= mScale;
358092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                TOP_PADDING_WEEK_NUMBER *= mScale;
3593bff945d29d6ef277391745cd160f3bf16b98905RoboErik                SIDE_PADDING_MONTH_NUMBER *= mScale;
3603bff945d29d6ef277391745cd160f3bf16b98905RoboErik                SIDE_PADDING_WEEK_NUMBER *= mScale;
3613bff945d29d6ef277391745cd160f3bf16b98905RoboErik                SPACING_WEEK_NUMBER *= mScale;
3623bff945d29d6ef277391745cd160f3bf16b98905RoboErik                TEXT_SIZE_MONTH_NUMBER *= mScale;
3633bff945d29d6ef277391745cd160f3bf16b98905RoboErik                TEXT_SIZE_EVENT *= mScale;
364c76ed496f5aa09ec85cc304b70c527c73cf8243eRoboErik                TEXT_SIZE_EVENT_TITLE *= mScale;
3653bff945d29d6ef277391745cd160f3bf16b98905RoboErik                TEXT_SIZE_MORE_EVENTS *= mScale;
3663bff945d29d6ef277391745cd160f3bf16b98905RoboErik                TEXT_SIZE_MONTH_NAME *= mScale;
3673bff945d29d6ef277391745cd160f3bf16b98905RoboErik                TEXT_SIZE_WEEK_NUM *= mScale;
3683bff945d29d6ef277391745cd160f3bf16b98905RoboErik                DAY_SEPARATOR_OUTER_WIDTH *= mScale;
3693bff945d29d6ef277391745cd160f3bf16b98905RoboErik                DAY_SEPARATOR_INNER_WIDTH *= mScale;
3703bff945d29d6ef277391745cd160f3bf16b98905RoboErik                DAY_SEPARATOR_VERTICAL_LENGTH *= mScale;
3713bff945d29d6ef277391745cd160f3bf16b98905RoboErik                DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT *= mScale;
3723bff945d29d6ef277391745cd160f3bf16b98905RoboErik                EVENT_X_OFFSET_LANDSCAPE *= mScale;
3733bff945d29d6ef277391745cd160f3bf16b98905RoboErik                EVENT_Y_OFFSET_LANDSCAPE *= mScale;
3743bff945d29d6ef277391745cd160f3bf16b98905RoboErik                EVENT_Y_OFFSET_PORTRAIT *= mScale;
3753bff945d29d6ef277391745cd160f3bf16b98905RoboErik                EVENT_SQUARE_WIDTH *= mScale;
376a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik                EVENT_SQUARE_BORDER *= mScale;
3773bff945d29d6ef277391745cd160f3bf16b98905RoboErik                EVENT_LINE_PADDING *= mScale;
3783bff945d29d6ef277391745cd160f3bf16b98905RoboErik                EVENT_BOTTOM_PADDING *= mScale;
3793bff945d29d6ef277391745cd160f3bf16b98905RoboErik                EVENT_RIGHT_PADDING *= mScale;
380092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                DNA_MARGIN *= mScale;
381092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                DNA_WIDTH *= mScale;
382092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                DNA_ALL_DAY_HEIGHT *= mScale;
383092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                DNA_MIN_SEGMENT_HEIGHT *= mScale;
384092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                DNA_SIDE_PADDING *= mScale;
3853bff945d29d6ef277391745cd160f3bf16b98905RoboErik                DEFAULT_EDGE_SPACING *= mScale;
386092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                DNA_ALL_DAY_WIDTH *= mScale;
3875b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                TODAY_HIGHLIGHT_WIDTH *= mScale;
388092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            }
389092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            if (!mShowDetailsInMonth) {
390092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                TOP_PADDING_MONTH_NUMBER += DNA_ALL_DAY_HEIGHT + DNA_MARGIN;
3913bff945d29d6ef277391745cd160f3bf16b98905RoboErik            }
392458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            mInitialized = true;
3937203d809b39fb07caab177d8e8bd9428097b73b6Erik        }
3943bff945d29d6ef277391745cd160f3bf16b98905RoboErik        mPadding = DEFAULT_EDGE_SPACING;
3953f888688c0f2644ad3de032d5d1cf623a7b092fdAndy McFadden        loadColors(getContext());
3963553621c824afe1f3eacbe8555678005ca1264a1Erik        // TODO modify paint properties depending on isMini
3973553621c824afe1f3eacbe8555678005ca1264a1Erik
3983553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumPaint = new Paint();
39940bcd101b212c9863c3110c05a487a7ae6ebc3caErik        mMonthNumPaint.setFakeBoldText(false);
4003553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumPaint.setAntiAlias(true);
4013553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER);
4023553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumPaint.setColor(mMonthNumColor);
4033553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumPaint.setStyle(Style.FILL);
404a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik        mMonthNumPaint.setTextAlign(Align.RIGHT);
40564d67bea6bdb8516c1685b0bb19bc217c3cbe14eRoboErik        mMonthNumPaint.setTypeface(Typeface.DEFAULT);
4063553621c824afe1f3eacbe8555678005ca1264a1Erik
40769f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mMonthNumAscentHeight = (int) (-mMonthNumPaint.ascent() + 0.5f);
40869f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mMonthNumHeight = (int) (mMonthNumPaint.descent() - mMonthNumPaint.ascent() + 0.5f);
4093553621c824afe1f3eacbe8555678005ca1264a1Erik
41040bcd101b212c9863c3110c05a487a7ae6ebc3caErik        mEventPaint = new TextPaint();
411f658df6b9c285f24c45cdca3ab90f2ca65e5b7cfRoboErik        mEventPaint.setFakeBoldText(true);
4123553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventPaint.setAntiAlias(true);
413c76ed496f5aa09ec85cc304b70c527c73cf8243eRoboErik        mEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE);
4143553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventPaint.setColor(mMonthEventColor);
4153553621c824afe1f3eacbe8555678005ca1264a1Erik
416458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        mSolidBackgroundEventPaint = new TextPaint(mEventPaint);
417458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        mSolidBackgroundEventPaint.setColor(EVENT_TEXT_COLOR);
418458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        mFramedEventPaint = new TextPaint(mSolidBackgroundEventPaint);
419458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
420210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mDeclinedEventPaint = new TextPaint();
421210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mDeclinedEventPaint.setFakeBoldText(true);
422210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mDeclinedEventPaint.setAntiAlias(true);
423c76ed496f5aa09ec85cc304b70c527c73cf8243eRoboErik        mDeclinedEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE);
424210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mDeclinedEventPaint.setColor(mMonthDeclinedEventColor);
425210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik
42669f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mEventAscentHeight = (int) (-mEventPaint.ascent() + 0.5f);
42769f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mEventHeight = (int) (mEventPaint.descent() - mEventPaint.ascent() + 0.5f);
4283553621c824afe1f3eacbe8555678005ca1264a1Erik
42940bcd101b212c9863c3110c05a487a7ae6ebc3caErik        mEventExtrasPaint = new TextPaint();
4303553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventExtrasPaint.setFakeBoldText(false);
4313553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventExtrasPaint.setAntiAlias(true);
43240bcd101b212c9863c3110c05a487a7ae6ebc3caErik        mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER);
4333553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT);
4343553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventExtrasPaint.setColor(mMonthEventExtraColor);
4353553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventExtrasPaint.setStyle(Style.FILL);
4363553621c824afe1f3eacbe8555678005ca1264a1Erik        mEventExtrasPaint.setTextAlign(Align.LEFT);
43769f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mExtrasHeight = (int)(mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f);
43869f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mExtrasAscentHeight = (int)(-mEventExtrasPaint.ascent() + 0.5f);
43969f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mExtrasDescent = (int)(mEventExtrasPaint.descent() + 0.5f);
4403553621c824afe1f3eacbe8555678005ca1264a1Erik
441210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint = new TextPaint();
442210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint.setFakeBoldText(false);
443210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint.setAntiAlias(true);
444210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER);
445210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT);
446210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint.setColor(mMonthDeclinedExtrasColor);
447210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint.setStyle(Style.FILL);
448210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventDeclinedExtrasPaint.setTextAlign(Align.LEFT);
449210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik
4503553621c824afe1f3eacbe8555678005ca1264a1Erik        mWeekNumPaint = new Paint();
4513553621c824afe1f3eacbe8555678005ca1264a1Erik        mWeekNumPaint.setFakeBoldText(false);
4523553621c824afe1f3eacbe8555678005ca1264a1Erik        mWeekNumPaint.setAntiAlias(true);
4533553621c824afe1f3eacbe8555678005ca1264a1Erik        mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM);
4543553621c824afe1f3eacbe8555678005ca1264a1Erik        mWeekNumPaint.setColor(mWeekNumColor);
4553553621c824afe1f3eacbe8555678005ca1264a1Erik        mWeekNumPaint.setStyle(Style.FILL);
4563553621c824afe1f3eacbe8555678005ca1264a1Erik        mWeekNumPaint.setTextAlign(Align.RIGHT);
4573553621c824afe1f3eacbe8555678005ca1264a1Erik
45869f9b450fc255203b758f445163f371c22c816c0Andy McFadden        mWeekNumAscentHeight = (int) (-mWeekNumPaint.ascent() + 0.5f);
45971b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson
460092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNAAllDayPaint = new Paint();
461092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNATimePaint = new Paint();
462092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNATimePaint.setColor(mMonthBusyBitsBusyTimeColor);
463092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNATimePaint.setStyle(Style.FILL_AND_STROKE);
464092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNATimePaint.setStrokeWidth(DNA_WIDTH);
465092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNATimePaint.setAntiAlias(false);
466092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNAAllDayPaint.setColor(mMonthBusyBitsConflictTimeColor);
467092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNAAllDayPaint.setStyle(Style.FILL_AND_STROKE);
468092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH);
469092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        mDNAAllDayPaint.setAntiAlias(false);
470a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik
471a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik        mEventSquarePaint = new Paint();
472a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik        mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER);
473a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik        mEventSquarePaint.setAntiAlias(false);
47469f9b450fc255203b758f445163f371c22c816c0Andy McFadden
47569f9b450fc255203b758f445163f371c22c816c0Andy McFadden        if (DEBUG_LAYOUT) {
47669f9b450fc255203b758f445163f371c22c816c0Andy McFadden            Log.d("EXTRA", "mScale=" + mScale);
47769f9b450fc255203b758f445163f371c22c816c0Andy McFadden            Log.d("EXTRA", "mMonthNumPaint ascent=" + mMonthNumPaint.ascent()
47869f9b450fc255203b758f445163f371c22c816c0Andy McFadden                    + " descent=" + mMonthNumPaint.descent() + " int height=" + mMonthNumHeight);
47969f9b450fc255203b758f445163f371c22c816c0Andy McFadden            Log.d("EXTRA", "mEventPaint ascent=" + mEventPaint.ascent()
48069f9b450fc255203b758f445163f371c22c816c0Andy McFadden                    + " descent=" + mEventPaint.descent() + " int height=" + mEventHeight
48169f9b450fc255203b758f445163f371c22c816c0Andy McFadden                    + " int ascent=" + mEventAscentHeight);
48269f9b450fc255203b758f445163f371c22c816c0Andy McFadden            Log.d("EXTRA", "mEventExtrasPaint ascent=" + mEventExtrasPaint.ascent()
48369f9b450fc255203b758f445163f371c22c816c0Andy McFadden                    + " descent=" + mEventExtrasPaint.descent() + " int height=" + mExtrasHeight);
48469f9b450fc255203b758f445163f371c22c816c0Andy McFadden            Log.d("EXTRA", "mWeekNumPaint ascent=" + mWeekNumPaint.ascent()
48569f9b450fc255203b758f445163f371c22c816c0Andy McFadden                    + " descent=" + mWeekNumPaint.descent());
48669f9b450fc255203b758f445163f371c22c816c0Andy McFadden        }
4873553621c824afe1f3eacbe8555678005ca1264a1Erik    }
4883553621c824afe1f3eacbe8555678005ca1264a1Erik
4893553621c824afe1f3eacbe8555678005ca1264a1Erik    @Override
4903553621c824afe1f3eacbe8555678005ca1264a1Erik    public void setWeekParams(HashMap<String, Integer> params, String tz) {
4913553621c824afe1f3eacbe8555678005ca1264a1Erik        super.setWeekParams(params, tz);
4923553621c824afe1f3eacbe8555678005ca1264a1Erik
49340bcd101b212c9863c3110c05a487a7ae6ebc3caErik        if (params.containsKey(VIEW_PARAMS_ORIENTATION)) {
49440bcd101b212c9863c3110c05a487a7ae6ebc3caErik            mOrientation = params.get(VIEW_PARAMS_ORIENTATION);
49540bcd101b212c9863c3110c05a487a7ae6ebc3caErik        }
49640bcd101b212c9863c3110c05a487a7ae6ebc3caErik
4975b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        updateToday(tz);
4985b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        mNumCells = mNumDays + 1;
4995b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
5005b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        if (params.containsKey(VIEW_PARAMS_ANIMATE_TODAY) && mHasToday) {
5015b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            synchronized (mAnimatorListener) {
5025b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mTodayAnimator != null) {
5035b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.removeAllListeners();
5045b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.cancel();
5055b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
5065b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha",
5075b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        Math.max(mAnimateTodayAlpha, 80), 255);
5085b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.setDuration(150);
5095b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mAnimatorListener.setAnimator(mTodayAnimator);
5105b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mAnimatorListener.setFadingIn(true);
5115b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.addListener(mAnimatorListener);
5125b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mAnimateToday = true;
5135b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.start();
5145b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            }
5155b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
5165b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
5175b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
5185b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    /**
5195b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik     * @param tz
5205b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik     */
5215b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public boolean updateToday(String tz) {
5223553621c824afe1f3eacbe8555678005ca1264a1Erik        mToday.timezone = tz;
5233553621c824afe1f3eacbe8555678005ca1264a1Erik        mToday.setToNow();
5243553621c824afe1f3eacbe8555678005ca1264a1Erik        mToday.normalize(true);
5253553621c824afe1f3eacbe8555678005ca1264a1Erik        int julianToday = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff);
5263553621c824afe1f3eacbe8555678005ca1264a1Erik        if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) {
5273553621c824afe1f3eacbe8555678005ca1264a1Erik            mHasToday = true;
5283553621c824afe1f3eacbe8555678005ca1264a1Erik            mTodayIndex = julianToday - mFirstJulianDay;
5293553621c824afe1f3eacbe8555678005ca1264a1Erik        } else {
5303553621c824afe1f3eacbe8555678005ca1264a1Erik            mHasToday = false;
5313553621c824afe1f3eacbe8555678005ca1264a1Erik            mTodayIndex = -1;
5323553621c824afe1f3eacbe8555678005ca1264a1Erik        }
5335b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        return mHasToday;
5345b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
5355b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
5365b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public void setAnimateTodayAlpha(int alpha) {
5375b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        mAnimateTodayAlpha = alpha;
5385b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        invalidate();
5393553621c824afe1f3eacbe8555678005ca1264a1Erik    }
5403553621c824afe1f3eacbe8555678005ca1264a1Erik
5413553621c824afe1f3eacbe8555678005ca1264a1Erik    @Override
54240bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected void onDraw(Canvas canvas) {
54340bcd101b212c9863c3110c05a487a7ae6ebc3caErik        drawBackground(canvas);
54440bcd101b212c9863c3110c05a487a7ae6ebc3caErik        drawWeekNums(canvas);
54540bcd101b212c9863c3110c05a487a7ae6ebc3caErik        drawDaySeparators(canvas);
5465b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        if (mHasToday && mAnimateToday) {
5475b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            drawToday(canvas);
5485b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
549ff5c4345f794ec12b0a92f284434ddc1c88ff3a8Isaac Katzenelson        if (mShowDetailsInMonth) {
55096a0b5436f79157e8175b3b761fd5ccfe26a7f62Isaac Katzenelson            drawEvents(canvas);
55182400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson        } else {
5525b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            if (mDna == null && mUnsortedEvents != null) {
5535b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                createDna(mUnsortedEvents);
5545b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            }
555092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            drawDNA(canvas);
556092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        }
55759a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        drawClick(canvas);
558092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    }
559092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik
5605b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    protected void drawToday(Canvas canvas) {
5615b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        r.top = DAY_SEPARATOR_INNER_WIDTH + (TODAY_HIGHLIGHT_WIDTH / 2);
5625b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        r.bottom = mHeight - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f);
5635b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        p.setStyle(Style.STROKE);
5645b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH);
5655b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        r.left = computeDayLeftPosition(mTodayIndex) + (TODAY_HIGHLIGHT_WIDTH / 2);
5665b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        r.right = computeDayLeftPosition(mTodayIndex + 1)
5675b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f);
5685b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        p.setColor(mTodayAnimateColor | (mAnimateTodayAlpha << 24));
5695b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        canvas.drawRect(r, p);
5705b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        p.setStyle(Style.FILL);
5715b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
5725b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
573092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    // TODO move into SimpleWeekView
574092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    // Computes the x position for the left side of the given day
575092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    private int computeDayLeftPosition(int day) {
576092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        int effectiveWidth = mWidth;
577092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        int x = 0;
578092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        int xOffset = 0;
579092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        if (mShowWeekNum) {
580092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            xOffset = SPACING_WEEK_NUMBER + mPadding;
581092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            effectiveWidth -= xOffset;
58296a0b5436f79157e8175b3b761fd5ccfe26a7f62Isaac Katzenelson        }
583092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        x = day * effectiveWidth / mNumDays + xOffset;
584092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        return x;
58540bcd101b212c9863c3110c05a487a7ae6ebc3caErik    }
58640bcd101b212c9863c3110c05a487a7ae6ebc3caErik
58740bcd101b212c9863c3110c05a487a7ae6ebc3caErik    @Override
58840bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected void drawDaySeparators(Canvas canvas) {
58940bcd101b212c9863c3110c05a487a7ae6ebc3caErik        float lines[] = new float[8 * 4];
590092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        int count = 6 * 4;
59140bcd101b212c9863c3110c05a487a7ae6ebc3caErik        int wkNumOffset = 0;
592092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        int i = 0;
593092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        if (mShowWeekNum) {
594092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            // This adds the first line separating the week number
595092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            int xOffset = SPACING_WEEK_NUMBER + mPadding;
596092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            count += 4;
597092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            lines[i++] = xOffset;
598092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            lines[i++] = 0;
599092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            lines[i++] = xOffset;
600092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            lines[i++] = mHeight;
601092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            wkNumOffset++;
602092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        }
603bf4aa400663a072813c87cf9c8aaee2d07abc945Erik        count += 4;
604092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        lines[i++] = 0;
605092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        lines[i++] = 0;
606092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        lines[i++] = mWidth;
607092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        lines[i++] = 0;
6083bff945d29d6ef277391745cd160f3bf16b98905RoboErik        int y0 = 0;
6093bff945d29d6ef277391745cd160f3bf16b98905RoboErik        int y1 = mHeight;
61040bcd101b212c9863c3110c05a487a7ae6ebc3caErik
611092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        while (i < count) {
612092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            int x = computeDayLeftPosition(i / 4 - wkNumOffset);
61340bcd101b212c9863c3110c05a487a7ae6ebc3caErik            lines[i++] = x;
61440bcd101b212c9863c3110c05a487a7ae6ebc3caErik            lines[i++] = y0;
61540bcd101b212c9863c3110c05a487a7ae6ebc3caErik            lines[i++] = x;
61640bcd101b212c9863c3110c05a487a7ae6ebc3caErik            lines[i++] = y1;
61740bcd101b212c9863c3110c05a487a7ae6ebc3caErik        }
61840bcd101b212c9863c3110c05a487a7ae6ebc3caErik        p.setColor(mDaySeparatorInnerColor);
61940bcd101b212c9863c3110c05a487a7ae6ebc3caErik        p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH);
62040bcd101b212c9863c3110c05a487a7ae6ebc3caErik        canvas.drawLines(lines, 0, count, p);
62140bcd101b212c9863c3110c05a487a7ae6ebc3caErik    }
62240bcd101b212c9863c3110c05a487a7ae6ebc3caErik
62340bcd101b212c9863c3110c05a487a7ae6ebc3caErik    @Override
6243553621c824afe1f3eacbe8555678005ca1264a1Erik    protected void drawBackground(Canvas canvas) {
62567d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        int i = 0;
62667d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        int offset = 0;
627092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        r.top = DAY_SEPARATOR_INNER_WIDTH;
6283553621c824afe1f3eacbe8555678005ca1264a1Erik        r.bottom = mHeight;
62967d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        if (mShowWeekNum) {
63067d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            i++;
63167d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            offset++;
63267d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        }
63341cdd1a43d80054c6a336585c40169e1c5538fdaIsaac Katzenelson        if (!mOddMonth[i]) {
63441cdd1a43d80054c6a336585c40169e1c5538fdaIsaac Katzenelson            while (++i < mOddMonth.length && !mOddMonth[i])
63567d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik                ;
63667d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            r.right = computeDayLeftPosition(i - offset);
63767d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            r.left = 0;
638e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setColor(mMonthBGOtherColor);
63967d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            canvas.drawRect(r, p);
64067d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            // compute left edge for i, set up r, draw
64141cdd1a43d80054c6a336585c40169e1c5538fdaIsaac Katzenelson        } else if (!mOddMonth[(i = mOddMonth.length - 1)]) {
64241cdd1a43d80054c6a336585c40169e1c5538fdaIsaac Katzenelson            while (--i >= offset && !mOddMonth[i])
64367d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik                ;
64467d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            i++;
64567d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            // compute left edge for i, set up r, draw
64667d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            r.right = mWidth;
64767d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            r.left = computeDayLeftPosition(i - offset);
648e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setColor(mMonthBGOtherColor);
64967d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            canvas.drawRect(r, p);
65067d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        }
65167d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        if (mHasToday) {
65267d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            p.setColor(mMonthBGTodayColor);
65367d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            r.left = computeDayLeftPosition(mTodayIndex);
65467d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            r.right = computeDayLeftPosition(mTodayIndex + 1);
65567d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            canvas.drawRect(r, p);
65667d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        }
6573553621c824afe1f3eacbe8555678005ca1264a1Erik    }
6583553621c824afe1f3eacbe8555678005ca1264a1Erik
65959a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    // Draw the "clicked" color on the tapped day
66059a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    private void drawClick(Canvas canvas) {
66159a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        if (mClickedDayIndex != -1) {
66259a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            int alpha = p.getAlpha();
66359a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            p.setColor(mClickedDayColor);
66459a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            p.setAlpha(mClickedAlpha);
66559a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            r.left = computeDayLeftPosition(mClickedDayIndex);
66659a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            r.right = computeDayLeftPosition(mClickedDayIndex + 1);
66759a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            r.top = DAY_SEPARATOR_INNER_WIDTH;
66859a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            r.bottom = mHeight;
66959a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            canvas.drawRect(r, p);
67059a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            p.setAlpha(alpha);
67159a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        }
67259a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    }
67359a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson
6743553621c824afe1f3eacbe8555678005ca1264a1Erik    @Override
6753553621c824afe1f3eacbe8555678005ca1264a1Erik    protected void drawWeekNums(Canvas canvas) {
676bf4aa400663a072813c87cf9c8aaee2d07abc945Erik        int y;
6773553621c824afe1f3eacbe8555678005ca1264a1Erik
6783553621c824afe1f3eacbe8555678005ca1264a1Erik        int i = 0;
679a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik        int offset = -1;
6803553621c824afe1f3eacbe8555678005ca1264a1Erik        int todayIndex = mTodayIndex;
681092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        int x = 0;
682ef896fda8812d5f31aa87e8e1525d6f9d8b61e9cErik        int numCount = mNumDays;
6833553621c824afe1f3eacbe8555678005ca1264a1Erik        if (mShowWeekNum) {
684092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            x = SIDE_PADDING_WEEK_NUMBER + mPadding;
68569f9b450fc255203b758f445163f371c22c816c0Andy McFadden            y = mWeekNumAscentHeight + TOP_PADDING_WEEK_NUMBER;
6863553621c824afe1f3eacbe8555678005ca1264a1Erik            canvas.drawText(mDayNumbers[0], x, y, mWeekNumPaint);
687ef896fda8812d5f31aa87e8e1525d6f9d8b61e9cErik            numCount++;
688ef896fda8812d5f31aa87e8e1525d6f9d8b61e9cErik            i++;
689ef896fda8812d5f31aa87e8e1525d6f9d8b61e9cErik            todayIndex++;
690ef896fda8812d5f31aa87e8e1525d6f9d8b61e9cErik            offset++;
691092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik
6923553621c824afe1f3eacbe8555678005ca1264a1Erik        }
6933553621c824afe1f3eacbe8555678005ca1264a1Erik
69469f9b450fc255203b758f445163f371c22c816c0Andy McFadden        y = mMonthNumAscentHeight + TOP_PADDING_MONTH_NUMBER;
6953553621c824afe1f3eacbe8555678005ca1264a1Erik
6963553621c824afe1f3eacbe8555678005ca1264a1Erik        boolean isFocusMonth = mFocusDay[i];
69767d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik        boolean isBold = false;
6983553621c824afe1f3eacbe8555678005ca1264a1Erik        mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor);
699ef896fda8812d5f31aa87e8e1525d6f9d8b61e9cErik        for (; i < numCount; i++) {
7003553621c824afe1f3eacbe8555678005ca1264a1Erik            if (mHasToday && todayIndex == i) {
7013553621c824afe1f3eacbe8555678005ca1264a1Erik                mMonthNumPaint.setColor(mMonthNumTodayColor);
70267d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik                mMonthNumPaint.setFakeBoldText(isBold = true);
703259b8f9a21fffd3858218581e9daaf689b6b888fErik                if (i + 1 < numCount) {
7043553621c824afe1f3eacbe8555678005ca1264a1Erik                    // Make sure the color will be set back on the next
7053553621c824afe1f3eacbe8555678005ca1264a1Erik                    // iteration
7063553621c824afe1f3eacbe8555678005ca1264a1Erik                    isFocusMonth = !mFocusDay[i + 1];
7073553621c824afe1f3eacbe8555678005ca1264a1Erik                }
7083553621c824afe1f3eacbe8555678005ca1264a1Erik            } else if (mFocusDay[i] != isFocusMonth) {
7093553621c824afe1f3eacbe8555678005ca1264a1Erik                isFocusMonth = mFocusDay[i];
7103553621c824afe1f3eacbe8555678005ca1264a1Erik                mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor);
7113553621c824afe1f3eacbe8555678005ca1264a1Erik            }
712a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik            x = computeDayLeftPosition(i - offset) - (SIDE_PADDING_MONTH_NUMBER);
7133553621c824afe1f3eacbe8555678005ca1264a1Erik            canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint);
71467d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            if (isBold) {
71567d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik                mMonthNumPaint.setFakeBoldText(isBold = false);
71667d628eaed439a4b109e5e35f2d536c99680aaf6RoboErik            }
7173553621c824afe1f3eacbe8555678005ca1264a1Erik        }
7183553621c824afe1f3eacbe8555678005ca1264a1Erik    }
7193553621c824afe1f3eacbe8555678005ca1264a1Erik
72040bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected void drawEvents(Canvas canvas) {
72140bcd101b212c9863c3110c05a487a7ae6ebc3caErik        if (mEvents == null) {
72240bcd101b212c9863c3110c05a487a7ae6ebc3caErik            return;
72340bcd101b212c9863c3110c05a487a7ae6ebc3caErik        }
72440bcd101b212c9863c3110c05a487a7ae6ebc3caErik
72540bcd101b212c9863c3110c05a487a7ae6ebc3caErik        int day = -1;
72640bcd101b212c9863c3110c05a487a7ae6ebc3caErik        for (ArrayList<Event> eventDay : mEvents) {
72740bcd101b212c9863c3110c05a487a7ae6ebc3caErik            day++;
72840bcd101b212c9863c3110c05a487a7ae6ebc3caErik            if (eventDay == null || eventDay.size() == 0) {
72940bcd101b212c9863c3110c05a487a7ae6ebc3caErik                continue;
73040bcd101b212c9863c3110c05a487a7ae6ebc3caErik            }
73140bcd101b212c9863c3110c05a487a7ae6ebc3caErik            int ySquare;
732a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik            int xSquare = computeDayLeftPosition(day) + SIDE_PADDING_MONTH_NUMBER + 1;
733a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik            int rightEdge = computeDayLeftPosition(day + 1);
734a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik
73540bcd101b212c9863c3110c05a487a7ae6ebc3caErik            if (mOrientation == Configuration.ORIENTATION_PORTRAIT) {
7363bff945d29d6ef277391745cd160f3bf16b98905RoboErik                ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + TOP_PADDING_MONTH_NUMBER;
737a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik                rightEdge -= SIDE_PADDING_MONTH_NUMBER + 1;
73840bcd101b212c9863c3110c05a487a7ae6ebc3caErik            } else {
73940bcd101b212c9863c3110c05a487a7ae6ebc3caErik                ySquare = EVENT_Y_OFFSET_LANDSCAPE;
740a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik                rightEdge -= EVENT_X_OFFSET_LANDSCAPE;
74140bcd101b212c9863c3110c05a487a7ae6ebc3caErik            }
742458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
743458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Determine if everything will fit when time ranges are shown.
744458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            boolean showTimes = true;
74540bcd101b212c9863c3110c05a487a7ae6ebc3caErik            Iterator<Event> iter = eventDay.iterator();
746458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            int yTest = ySquare;
74740bcd101b212c9863c3110c05a487a7ae6ebc3caErik            while (iter.hasNext()) {
74840bcd101b212c9863c3110c05a487a7ae6ebc3caErik                Event event = iter.next();
749458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                int newY = drawEvent(canvas, event, xSquare, yTest, rightEdge, iter.hasNext(),
750458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                        showTimes, /*doDraw*/ false);
751458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                if (newY == yTest) {
752458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                    showTimes = false;
753458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                    break;
754458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                }
755458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                yTest = newY;
756458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            }
757458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
758458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            int eventCount = 0;
759458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            iter = eventDay.iterator();
760458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            while (iter.hasNext()) {
761458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                Event event = iter.next();
762458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                int newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext(),
763458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                        showTimes, /*doDraw*/ true);
76440bcd101b212c9863c3110c05a487a7ae6ebc3caErik                if (newY == ySquare) {
76540bcd101b212c9863c3110c05a487a7ae6ebc3caErik                    break;
76640bcd101b212c9863c3110c05a487a7ae6ebc3caErik                }
76740bcd101b212c9863c3110c05a487a7ae6ebc3caErik                eventCount++;
76840bcd101b212c9863c3110c05a487a7ae6ebc3caErik                ySquare = newY;
76940bcd101b212c9863c3110c05a487a7ae6ebc3caErik            }
77040bcd101b212c9863c3110c05a487a7ae6ebc3caErik
77140bcd101b212c9863c3110c05a487a7ae6ebc3caErik            int remaining = eventDay.size() - eventCount;
77240bcd101b212c9863c3110c05a487a7ae6ebc3caErik            if (remaining > 0) {
77340bcd101b212c9863c3110c05a487a7ae6ebc3caErik                drawMoreEvents(canvas, remaining, xSquare);
77440bcd101b212c9863c3110c05a487a7ae6ebc3caErik            }
77540bcd101b212c9863c3110c05a487a7ae6ebc3caErik        }
77640bcd101b212c9863c3110c05a487a7ae6ebc3caErik    }
77740bcd101b212c9863c3110c05a487a7ae6ebc3caErik
778f445411ff661014c4b5a37e2f97552989e9278ecRoboErik    protected int addChipOutline(FloatRef lines, int count, int x, int y) {
779f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.ensureSize(count + 16);
78040bcd101b212c9863c3110c05a487a7ae6ebc3caErik        // top of box
781f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = x;
782f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y;
783f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = x + EVENT_SQUARE_WIDTH;
784f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y;
78540bcd101b212c9863c3110c05a487a7ae6ebc3caErik        // right side of box
786f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = x + EVENT_SQUARE_WIDTH;
787f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y;
788f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = x + EVENT_SQUARE_WIDTH;
789f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y + EVENT_SQUARE_WIDTH;
79040bcd101b212c9863c3110c05a487a7ae6ebc3caErik        // left side of box
791f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = x;
792f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y;
793f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = x;
794f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y + EVENT_SQUARE_WIDTH + 1;
79540bcd101b212c9863c3110c05a487a7ae6ebc3caErik        // bottom of box
796f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = x;
797f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y + EVENT_SQUARE_WIDTH;
7982fda24534978f55711526dbde747aa50d68dec40RoboErik        lines.array[count++] = x + EVENT_SQUARE_WIDTH + 1;
799f445411ff661014c4b5a37e2f97552989e9278ecRoboErik        lines.array[count++] = y + EVENT_SQUARE_WIDTH;
80040bcd101b212c9863c3110c05a487a7ae6ebc3caErik
80140bcd101b212c9863c3110c05a487a7ae6ebc3caErik        return count;
80240bcd101b212c9863c3110c05a487a7ae6ebc3caErik    }
80340bcd101b212c9863c3110c05a487a7ae6ebc3caErik
80440bcd101b212c9863c3110c05a487a7ae6ebc3caErik    /**
80540bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * Attempts to draw the given event. Returns the y for the next event or the
80640bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * original y if the event will not fit. An event is considered to not fit
80740bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * if the event and its extras won't fit or if there are more events and the
80840bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * more events line would not fit after drawing this event.
80940bcd101b212c9863c3110c05a487a7ae6ebc3caErik     *
810458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden     * @param canvas the canvas to draw on
81140bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * @param event the event to draw
81240bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * @param x the top left corner for this event's color chip
81340bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * @param y the top left corner for this event's color chip
814458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden     * @param rightEdge the rightmost point we're allowed to draw on (exclusive)
815458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden     * @param moreEvents indicates whether additional events will follow this one
816458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden     * @param showTimes if set, a second line with a time range will be displayed for non-all-day
817458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden     *   events
818458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden     * @param doDraw if set, do the actual drawing; otherwise this just computes the height
819458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden     *   and returns
82040bcd101b212c9863c3110c05a487a7ae6ebc3caErik     * @return the y for the next event or the original y if it won't fit
82140bcd101b212c9863c3110c05a487a7ae6ebc3caErik     */
822458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden    protected int drawEvent(Canvas canvas, Event event, int x, int y, int rightEdge,
823458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            boolean moreEvents, boolean showTimes, boolean doDraw) {
82469f9b450fc255203b758f445163f371c22c816c0Andy McFadden        /*
82569f9b450fc255203b758f445163f371c22c816c0Andy McFadden         * Vertical layout:
82669f9b450fc255203b758f445163f371c22c816c0Andy McFadden         *   (top of box)
82769f9b450fc255203b758f445163f371c22c816c0Andy McFadden         * a. EVENT_Y_OFFSET_LANDSCAPE or portrait equivalent
828458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden         * b. Event title: mEventHeight for a normal event, + 2xBORDER_SPACE for all-day event
82969f9b450fc255203b758f445163f371c22c816c0Andy McFadden         * c. [optional] Time range (mExtrasHeight)
83069f9b450fc255203b758f445163f371c22c816c0Andy McFadden         * d. EVENT_LINE_PADDING
83169f9b450fc255203b758f445163f371c22c816c0Andy McFadden         *
83269f9b450fc255203b758f445163f371c22c816c0Andy McFadden         * Repeat (b,c,d) as needed and space allows.  If we have more events than fit, we need
83369f9b450fc255203b758f445163f371c22c816c0Andy McFadden         * to leave room for something like "+2" at the bottom:
83469f9b450fc255203b758f445163f371c22c816c0Andy McFadden         *
835458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden         * e. "+ more" line (mExtrasHeight)
83669f9b450fc255203b758f445163f371c22c816c0Andy McFadden         *
83769f9b450fc255203b758f445163f371c22c816c0Andy McFadden         * f. EVENT_BOTTOM_PADDING (overlaps EVENT_LINE_PADDING)
83869f9b450fc255203b758f445163f371c22c816c0Andy McFadden         *   (bottom of box)
83969f9b450fc255203b758f445163f371c22c816c0Andy McFadden         */
840458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        final int BORDER_SPACE = EVENT_SQUARE_BORDER + 1;       // want a 1-pixel gap inside border
841458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        final int STROKE_WIDTH_ADJ = EVENT_SQUARE_BORDER / 2;   // adjust bounds for stroke width
842458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        boolean allDay = event.allDay;
843458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        int eventRequiredSpace = mEventHeight;
844458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (allDay) {
845458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Add a few pixels for the box we draw around all-day events.
846458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            eventRequiredSpace += BORDER_SPACE * 2;
847458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        } else if (showTimes) {
84869f9b450fc255203b758f445163f371c22c816c0Andy McFadden            // Need room for the "1pm - 2pm" line.
849458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            eventRequiredSpace += mExtrasHeight;
85040bcd101b212c9863c3110c05a487a7ae6ebc3caErik        }
851458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        int reservedSpace = EVENT_BOTTOM_PADDING;   // leave a bit of room at the bottom
85269f9b450fc255203b758f445163f371c22c816c0Andy McFadden        if (moreEvents) {
853458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // More events follow.  Leave a bit of space between events.
854458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            eventRequiredSpace += EVENT_LINE_PADDING;
855458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
856458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Make sure we have room for the "+ more" line.  (The "+ more" line is expected
857458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // to be <= the height of an event line, so we won't show "+1" when we could be
858458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // showing the event.)
859458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            reservedSpace += mExtrasHeight;
86069f9b450fc255203b758f445163f371c22c816c0Andy McFadden        }
86169f9b450fc255203b758f445163f371c22c816c0Andy McFadden
862458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (y + eventRequiredSpace + reservedSpace > mHeight) {
863458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Not enough space, return original y
86440bcd101b212c9863c3110c05a487a7ae6ebc3caErik            return y;
865458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        } else if (!doDraw) {
866458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            return y + eventRequiredSpace;
86740bcd101b212c9863c3110c05a487a7ae6ebc3caErik        }
868458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
869210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        boolean isDeclined = event.selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED;
870210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        int color = event.color;
871210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        if (isDeclined) {
872210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik            color = Utils.getDeclinedColorFromColor(color);
873210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        }
874458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
875458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        int textX, textY, textRightEdge;
876458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
877458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (allDay) {
878458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // We shift the render offset "inward", because drawRect with a stroke width greater
879458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // than 1 draws outside the specified bounds.  (We don't adjust the left edge, since
880458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // we want to match the existing appearance of the "event square".)
881458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.left = x;
882458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.right = rightEdge - STROKE_WIDTH_ADJ;
883458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.top = y + STROKE_WIDTH_ADJ;
884458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.bottom = y + mEventHeight + BORDER_SPACE * 2 - STROKE_WIDTH_ADJ;
885458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textX = x + BORDER_SPACE;
886458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textY = y + mEventAscentHeight + BORDER_SPACE;
887458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textRightEdge = rightEdge - BORDER_SPACE;
888458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        } else {
889458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.left = x;
890458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.right = x + EVENT_SQUARE_WIDTH;
891458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.bottom = y + mEventAscentHeight;
892458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            r.top = r.bottom - EVENT_SQUARE_WIDTH;
893458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textX = x + EVENT_SQUARE_WIDTH + EVENT_RIGHT_PADDING;
894458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textY = y + mEventAscentHeight;
895458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textRightEdge = rightEdge;
896458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        }
897458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden
898458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        Style boxStyle = Style.STROKE;
899458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        boolean solidBackground = false;
900458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (event.selfAttendeeStatus != Attendees.ATTENDEE_STATUS_INVITED) {
901458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            boxStyle = Style.FILL_AND_STROKE;
902458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            if (allDay) {
903458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden                solidBackground = true;
904458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            }
905458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        }
906458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        mEventSquarePaint.setStyle(boxStyle);
907210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik        mEventSquarePaint.setColor(color);
908a8d8ebd77b91ea94f9d680d343ee1c891bec7f5cRoboErik        canvas.drawRect(r, mEventSquarePaint);
90940bcd101b212c9863c3110c05a487a7ae6ebc3caErik
910458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        float avail = textRightEdge - textX;
91140bcd101b212c9863c3110c05a487a7ae6ebc3caErik        CharSequence text = TextUtils.ellipsize(
91240bcd101b212c9863c3110c05a487a7ae6ebc3caErik                event.title, mEventPaint, avail, TextUtils.TruncateAt.END);
913458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        Paint textPaint;
914458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (solidBackground) {
915458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Text color needs to contrast with solid background.
916458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textPaint = mSolidBackgroundEventPaint;
917458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        } else if (isDeclined) {
918458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Use "declined event" color.
919458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textPaint = mDeclinedEventPaint;
920458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        } else if (allDay) {
921458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Text inside frame is same color as frame.
922458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            mFramedEventPaint.setColor(color);
923458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textPaint = mFramedEventPaint;
924458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        } else {
925458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            // Use generic event text color.
926458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            textPaint = mEventPaint;
927458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        }
928458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        canvas.drawText(text.toString(), textX, textY, textPaint);
92969f9b450fc255203b758f445163f371c22c816c0Andy McFadden        y += mEventHeight;
930458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (allDay) {
931458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden            y += BORDER_SPACE * 2;
932458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        }
93369f9b450fc255203b758f445163f371c22c816c0Andy McFadden
934458a67d9a63836b4b08b24d58f4a91514de97268Andy McFadden        if (showTimes && !allDay) {
93569f9b450fc255203b758f445163f371c22c816c0Andy McFadden            // show start/end time, e.g. "1pm - 2pm"
93669f9b450fc255203b758f445163f371c22c816c0Andy McFadden            textY = y + mExtrasAscentHeight;
93740bcd101b212c9863c3110c05a487a7ae6ebc3caErik            mStringBuilder.setLength(0);
9383f888688c0f2644ad3de032d5d1cf623a7b092fdAndy McFadden            text = DateUtils.formatDateRange(getContext(), mFormatter, event.startMillis,
93940bcd101b212c9863c3110c05a487a7ae6ebc3caErik                    event.endMillis, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL,
9403f888688c0f2644ad3de032d5d1cf623a7b092fdAndy McFadden                    Utils.getTimeZone(getContext(), null)).toString();
94140bcd101b212c9863c3110c05a487a7ae6ebc3caErik            text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END);
942210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik            canvas.drawText(text.toString(), textX, textY, isDeclined ? mEventDeclinedExtrasPaint
943210642aa1f8bec18f7275cddb9a3b50fad3f4218RoboErik                    : mEventExtrasPaint);
94469f9b450fc255203b758f445163f371c22c816c0Andy McFadden            y += mExtrasHeight;
94540bcd101b212c9863c3110c05a487a7ae6ebc3caErik        }
94640bcd101b212c9863c3110c05a487a7ae6ebc3caErik
94769f9b450fc255203b758f445163f371c22c816c0Andy McFadden        y += EVENT_LINE_PADDING;
94869f9b450fc255203b758f445163f371c22c816c0Andy McFadden
94969f9b450fc255203b758f445163f371c22c816c0Andy McFadden        return y;
95040bcd101b212c9863c3110c05a487a7ae6ebc3caErik    }
95140bcd101b212c9863c3110c05a487a7ae6ebc3caErik
95240bcd101b212c9863c3110c05a487a7ae6ebc3caErik    protected void drawMoreEvents(Canvas canvas, int remainingEvents, int x) {
95369f9b450fc255203b758f445163f371c22c816c0Andy McFadden        int y = mHeight - (mExtrasDescent + EVENT_BOTTOM_PADDING);
9543f888688c0f2644ad3de032d5d1cf623a7b092fdAndy McFadden        String text = getContext().getResources().getQuantityString(
95540bcd101b212c9863c3110c05a487a7ae6ebc3caErik                R.plurals.month_more_events, remainingEvents);
956a74b9e2e0c7dce78cf7e343a30d34accea0891ccRoboErik        mEventExtrasPaint.setAntiAlias(true);
95740bcd101b212c9863c3110c05a487a7ae6ebc3caErik        mEventExtrasPaint.setFakeBoldText(true);
95840bcd101b212c9863c3110c05a487a7ae6ebc3caErik        canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint);
95940bcd101b212c9863c3110c05a487a7ae6ebc3caErik        mEventExtrasPaint.setFakeBoldText(false);
96040bcd101b212c9863c3110c05a487a7ae6ebc3caErik    }
96140bcd101b212c9863c3110c05a487a7ae6ebc3caErik
96271b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson    /**
963092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik     * Draws a line showing busy times in each day of week The method draws
964092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik     * non-conflicting times in the event color and times with conflicting
965092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik     * events in the dna conflict color defined in colors.
96671b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson     *
96771b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson     * @param canvas
96871b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson     */
969092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik    protected void drawDNA(Canvas canvas) {
970092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        // Draw event and conflict times
971092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik        if (mDna != null) {
972092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            for (Utils.DNAStrand strand : mDna.values()) {
973092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                if (strand.color == CONFLICT_COLOR || strand.points == null
974092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                        || strand.points.length == 0) {
975092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                    continue;
976092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                }
977092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                mDNATimePaint.setColor(strand.color);
978092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                canvas.drawLines(strand.points, mDNATimePaint);
979092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            }
980092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            // Draw black last to make sure it's on top
981092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            Utils.DNAStrand strand = mDna.get(CONFLICT_COLOR);
982092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            if (strand != null && strand.points != null && strand.points.length != 0) {
983092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                mDNATimePaint.setColor(strand.color);
984092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                canvas.drawLines(strand.points, mDNATimePaint);
98571b9ce3a20ebaff8f7e40fc0e5ec2a388bcc394cIsaac Katzenelson            }
986092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            if (mDayXs == null) {
987092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                return;
988092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            }
989092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            int numDays = mDayXs.length;
990092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            int xOffset = (DNA_ALL_DAY_WIDTH - DNA_WIDTH) / 2;
991092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik            if (strand != null && strand.allDays != null && strand.allDays.length == numDays) {
992092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                for (int i = 0; i < numDays; i++) {
993092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                    // this adds at most 7 draws. We could sort it by color and
994092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                    // build an array instead but this is easier.
995092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                    if (strand.allDays[i] != 0) {
996092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                        mDNAAllDayPaint.setColor(strand.allDays[i]);
997092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                        canvas.drawLine(mDayXs[i] + xOffset, DNA_MARGIN, mDayXs[i] + xOffset,
998092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                                DNA_MARGIN + DNA_ALL_DAY_HEIGHT, mDNAAllDayPaint);
999092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                    }
1000092caec951caa0fabcd51729678e3ddcd6ca2f03RoboErik                }
100182400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson            }
100282400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson        }
100382400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson    }
100482400dd70331df7885dd59b809c4bc0667046320Isaac Katzenelson
10053553621c824afe1f3eacbe8555678005ca1264a1Erik    @Override
10063553621c824afe1f3eacbe8555678005ca1264a1Erik    protected void updateSelectionPositions() {
10073553621c824afe1f3eacbe8555678005ca1264a1Erik        if (mHasSelectedDay) {
10083553621c824afe1f3eacbe8555678005ca1264a1Erik            int selectedPosition = mSelectedDay - mWeekStart;
10093553621c824afe1f3eacbe8555678005ca1264a1Erik            if (selectedPosition < 0) {
10103553621c824afe1f3eacbe8555678005ca1264a1Erik                selectedPosition += 7;
10113553621c824afe1f3eacbe8555678005ca1264a1Erik            }
10123553621c824afe1f3eacbe8555678005ca1264a1Erik            int effectiveWidth = mWidth - mPadding * 2;
1013bf4aa400663a072813c87cf9c8aaee2d07abc945Erik            effectiveWidth -= SPACING_WEEK_NUMBER;
10143553621c824afe1f3eacbe8555678005ca1264a1Erik            mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding;
10153553621c824afe1f3eacbe8555678005ca1264a1Erik            mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding;
1016bf4aa400663a072813c87cf9c8aaee2d07abc945Erik            mSelectedLeft += SPACING_WEEK_NUMBER;
1017bf4aa400663a072813c87cf9c8aaee2d07abc945Erik            mSelectedRight += SPACING_WEEK_NUMBER;
10183553621c824afe1f3eacbe8555678005ca1264a1Erik        }
10193553621c824afe1f3eacbe8555678005ca1264a1Erik    }
10203553621c824afe1f3eacbe8555678005ca1264a1Erik
102159a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    public int getDayIndexFromLocation(float x) {
1022da532a723613001f1175c067aece5f859d079bfcRoboErik        int dayStart = mShowWeekNum ? SPACING_WEEK_NUMBER + mPadding : mPadding;
10233553621c824afe1f3eacbe8555678005ca1264a1Erik        if (x < dayStart || x > mWidth - mPadding) {
102459a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            return -1;
10253553621c824afe1f3eacbe8555678005ca1264a1Erik        }
10263553621c824afe1f3eacbe8555678005ca1264a1Erik        // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
102759a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        return ((int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)));
102859a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    }
102959a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson
103059a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    @Override
103159a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    public Time getDayFromLocation(float x) {
103259a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        int dayPosition = getDayIndexFromLocation(x);
103359a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        if (dayPosition == -1) {
103459a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson            return null;
103559a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        }
10363553621c824afe1f3eacbe8555678005ca1264a1Erik        int day = mFirstJulianDay + dayPosition;
10373553621c824afe1f3eacbe8555678005ca1264a1Erik
10383553621c824afe1f3eacbe8555678005ca1264a1Erik        Time time = new Time(mTimeZone);
10393553621c824afe1f3eacbe8555678005ca1264a1Erik        if (mWeek == 0) {
10403553621c824afe1f3eacbe8555678005ca1264a1Erik            // This week is weird...
10413553621c824afe1f3eacbe8555678005ca1264a1Erik            if (day < Time.EPOCH_JULIAN_DAY) {
10423553621c824afe1f3eacbe8555678005ca1264a1Erik                day++;
10433553621c824afe1f3eacbe8555678005ca1264a1Erik            } else if (day == Time.EPOCH_JULIAN_DAY) {
10443553621c824afe1f3eacbe8555678005ca1264a1Erik                time.set(1, 0, 1970);
10453553621c824afe1f3eacbe8555678005ca1264a1Erik                time.normalize(true);
10463553621c824afe1f3eacbe8555678005ca1264a1Erik                return time;
10473553621c824afe1f3eacbe8555678005ca1264a1Erik            }
10483553621c824afe1f3eacbe8555678005ca1264a1Erik        }
10493553621c824afe1f3eacbe8555678005ca1264a1Erik
10503553621c824afe1f3eacbe8555678005ca1264a1Erik        time.setJulianDay(day);
10513553621c824afe1f3eacbe8555678005ca1264a1Erik        return time;
105282b318634af1b6594e60cfd1f2df4d3845f80587Erik    }
105382b318634af1b6594e60cfd1f2df4d3845f80587Erik
105467f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik    @Override
105567f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik    public boolean onHoverEvent(MotionEvent event) {
1056f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik        Context context = getContext();
1057f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik        // only send accessibility events if accessibility and exploration are
1058f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik        // on.
105993c08b7693d8246305c75dc2416f70321ce9c1a2RoboErik        AccessibilityManager am = (AccessibilityManager) context
106093c08b7693d8246305c75dc2416f70321ce9c1a2RoboErik                .getSystemService(Service.ACCESSIBILITY_SERVICE);
1061f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik        if (!am.isEnabled() || !am.isTouchExplorationEnabled()) {
1062f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik            return super.onHoverEvent(event);
1063f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik        }
106467f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik        if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) {
106567f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik            Time hover = getDayFromLocation(event.getX());
106667f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik            if (hover != null
106767f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                    && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) {
106867f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                Long millis = hover.toMillis(true);
1069f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik                String date = Utils.formatDateRange(context, millis, millis,
107067f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                        DateUtils.FORMAT_SHOW_DATE);
107167f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                AccessibilityEvent accessEvent = AccessibilityEvent
107267f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                        .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
107367f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                accessEvent.getText().add(date);
1074044a43c3c1789ba93d83ae68d799d94dc4e4ecf2Michael Chan                if (mShowDetailsInMonth && mEvents != null) {
107567f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                    int dayStart = SPACING_WEEK_NUMBER + mPadding;
107667f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                    int dayPosition = (int) ((event.getX() - dayStart) * mNumDays / (mWidth
107767f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                            - dayStart - mPadding));
107867f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                    ArrayList<Event> events = mEvents.get(dayPosition);
107967f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                    List<CharSequence> text = accessEvent.getText();
108067f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                    for (Event e : events) {
108167f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                        text.add(e.getTitleAndLocation() + ". ");
108267f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
108367f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                        if (!e.allDay) {
108467f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                            flags |= DateUtils.FORMAT_SHOW_TIME;
1085f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik                            if (DateFormat.is24HourFormat(context)) {
108667f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                                flags |= DateUtils.FORMAT_24HOUR;
108767f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                            }
1088aeebdbcf782a531bc972078cf05e70f21112ecafRoboErik                        } else {
1089aeebdbcf782a531bc972078cf05e70f21112ecafRoboErik                            flags |= DateUtils.FORMAT_UTC;
109067f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                        }
1091f9cb58df1deab65ad426d7997fe3e3fdfeef2850RoboErik                        text.add(Utils.formatDateRange(context, e.startMillis, e.endMillis,
109267f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                                flags) + ". ");
109367f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                    }
109467f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                }
109567f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                sendAccessibilityEventUnchecked(accessEvent);
109667f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik                mLastHoverTime = hover;
109767f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik            }
109867f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik        }
109967f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik        return true;
110067f16905cd1efe0fef4e86ff5ddea7069b80b98dRoboErik    }
110159a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson
110259a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    public void setClickedDay(float xLocation) {
110359a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        mClickedDayIndex = getDayIndexFromLocation(xLocation);
110459a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        invalidate();
110559a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    }
110659a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    public void clearClickedDay() {
110759a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        mClickedDayIndex = -1;
110859a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson        invalidate();
110959a44aa4bb3fac90f7ceab65da191e7fd81905f2Isaac Katzenelson    }
111082b318634af1b6594e60cfd1f2df4d3845f80587Erik}
1111