DayView.java revision 5b872523e28fc778a3fc34e7e9096ca1197c6509
1146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/*
2146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
3146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *
4146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * you may not use this file except in compliance with the License.
6146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * You may obtain a copy of the License at
7146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *
8146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project *
10146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * See the License for the specific language governing permissions and
14146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project * limitations under the License.
15146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project */
16146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
17146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectpackage com.android.calendar;
18146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1983b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chanimport com.android.calendar.CalendarController.EventType;
2083b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chanimport com.android.calendar.CalendarController.ViewType;
2183b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
2246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.Animator;
2346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.AnimatorListenerAdapter;
2446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.ObjectAnimator;
2546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.TypeEvaluator;
2646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.ValueAnimator;
2720b6fb580bf09dbd610a256ffd3165c03053cb70RoboErikimport android.app.AlertDialog;
289ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chanimport android.app.Service;
29146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentResolver;
30146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentUris;
31146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Context;
3220b6fb580bf09dbd610a256ffd3165c03053cb70RoboErikimport android.content.DialogInterface;
33146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.res.Resources;
34146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.res.TypedArray;
35146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.database.Cursor;
36146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Canvas;
37146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Paint;
38e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.graphics.Paint.Align;
39dd95df57c8c5a58a85c4c0effad5652dec14f621Erikimport android.graphics.Paint.Style;
40146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Rect;
41146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Typeface;
42c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawaimport android.graphics.drawable.Drawable;
43146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.net.Uri;
44146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Handler;
45a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Attendees;
46a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Calendars;
47a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Events;
483e7015267340b09d142dfcd3ff8acda257711023Shachar Shemeshimport android.text.Layout.Alignment;
49e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.SpannableStringBuilder;
50f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chanimport android.text.StaticLayout;
513e7015267340b09d142dfcd3ff8acda257711023Shachar Shemeshimport android.text.TextPaint;
52f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chanimport android.text.TextUtils;
53146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.DateFormat;
54146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.DateUtils;
55146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.Time;
56e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.style.StyleSpan;
57146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.util.Log;
58146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ContextMenu;
59dd95df57c8c5a58a85c4c0effad5652dec14f621Erikimport android.view.ContextMenu.ContextMenuInfo;
60ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.GestureDetector;
61146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.Gravity;
62146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.KeyEvent;
63146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.LayoutInflater;
64146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.MenuItem;
65146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.MotionEvent;
66d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chanimport android.view.ScaleGestureDetector;
67146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.View;
68146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ViewConfiguration;
69146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ViewGroup;
70146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.WindowManager;
71259b8f9a21fffd3858218581e9daaf689b6b888fErikimport android.view.accessibility.AccessibilityEvent;
729ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chanimport android.view.accessibility.AccessibilityManager;
7346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.view.animation.AccelerateDecelerateInterpolator;
74ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.animation.Animation;
752c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chanimport android.view.animation.Interpolator;
76ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.animation.TranslateAnimation;
77433ba924de1ac26b923e2beb061812012749e4f3Michael Chanimport android.widget.EdgeEffect;
78146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.ImageView;
793f6328f579b05c02606572c777541b97aa04f9f3Michael Chanimport android.widget.OverScroller;
80146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.PopupWindow;
81146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.TextView;
82ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.widget.ViewSwitcher;
83146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
84146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport java.util.ArrayList;
85c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawaimport java.util.Arrays;
86146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport java.util.Calendar;
87259b8f9a21fffd3858218581e9daaf689b6b888fErikimport java.util.Formatter;
88259b8f9a21fffd3858218581e9daaf689b6b888fErikimport java.util.Locale;
890d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chanimport java.util.regex.Matcher;
900d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chanimport java.util.regex.Pattern;
91146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
92146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/**
93d190418220467c02327dd9b3ae1eae238889720bMichael Chan * View for multi-day view. So far only 1 and 7 day have been tested.
94d190418220467c02327dd9b3ae1eae238889720bMichael Chan */
95d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chanpublic class DayView extends View implements View.OnCreateContextMenuListener,
96cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener
97cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        {
98d190418220467c02327dd9b3ae1eae238889720bMichael Chan    private static String TAG = "DayView";
99d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static boolean DEBUG = false;
1009ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private static boolean DEBUG_SCALING = false;
101259b8f9a21fffd3858218581e9daaf689b6b888fErik    private static final String PERIOD_SPACE = ". ";
102146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
103705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan    private static float mScale = 0; // Used for supporting different screen densities
104666fc5e8ddbac15c951aad738bd818ee307eae42Erik    private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event
1052fda24534978f55711526dbde747aa50d68dec40RoboErik    // Duration of the allday expansion
106ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private static final long ANIMATION_DURATION = 400;
1072fda24534978f55711526dbde747aa50d68dec40RoboErik    // duration of the more allday event text fade
1082fda24534978f55711526dbde747aa50d68dec40RoboErik    private static final long ANIMATION_SECONDARY_DURATION = 200;
1095b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    // duration of the scroll to go to a specified time
1105b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private static final int GOTO_SCROLL_DURATION = 200;
111705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
1124484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_AGENDA = 2;
1134484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_DAY = 3;
1144484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_VIEW = 5;
1154484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_CREATE = 6;
1164484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_EDIT = 7;
1174484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_DELETE = 8;
1184484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan
11911ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan    private static int DEFAULT_CELL_HEIGHT = 64;
120d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int MAX_CELL_HEIGHT = 150;
121d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int MIN_Y_SPAN = 100;
122d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
123146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mOnFlingCalled;
1244f58287a1e02325337e39073a50bbb880094099fRoboErik    private boolean mStartingScroll = false;
125666fc5e8ddbac15c951aad738bd818ee307eae42Erik    /**
126666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * ID of the last event which was displayed with the toast popup.
127666fc5e8ddbac15c951aad738bd818ee307eae42Erik     *
128666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * This is used to prevent popping up multiple quick views for the same event, especially
129666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * during calendar syncs. This becomes valid when an event is selected, either by default
130666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * on starting calendar or by scrolling to an event. It becomes invalid when the user
131666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * explicitly scrolls to an empty time slot, changes views, or deletes the event.
132666fc5e8ddbac15c951aad738bd818ee307eae42Erik     */
133666fc5e8ddbac15c951aad738bd818ee307eae42Erik    private long mLastPopupEventID;
134146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
135ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    protected Context mContext;
136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String[] CALENDARS_PROJECTION = new String[] {
138146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Calendars._ID,          // 0
139ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        Calendars.CALENDAR_ACCESS_LEVEL, // 1
1406d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        Calendars.OWNER_ACCOUNT, // 2
141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int CALENDARS_INDEX_ACCESS_LEVEL = 1;
1436d34cec523930368976bc8d358d0a6de1da3462cMichael Chan    private static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String CALENDARS_WHERE = Calendars._ID + "=%d";
145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_NONE = 0;
147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_ABOVE = 1;
148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_BELOW = 2;
149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_LEFT = 4;
150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_RIGHT = 8;
151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
152a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_NONE = 0;
153a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_DELETE = 1;
154a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_EDIT = 2;
155a505627659f6bfba8869eac8089f7030f2d0d92cErik
156d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan    private static int mHorizontalSnapBackThreshold = 128;
157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ContinueScroll mContinueScroll = new ContinueScroll();
159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
160146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Make this visible within the package for more informative debugging
161146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Time mBaseDate;
162bd0e834aea317236d574f8738fe11a51fdc87968Erik    private Time mCurrentTime;
163bd0e834aea317236d574f8738fe11a51fdc87968Erik    //Update the current time line every five minutes if the window is left open that long
164bd0e834aea317236d574f8738fe11a51fdc87968Erik    private static final int UPDATE_CURRENT_TIME_DELAY = 300000;
165bd0e834aea317236d574f8738fe11a51fdc87968Erik    private UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime();
166bd0e834aea317236d574f8738fe11a51fdc87968Erik    private int mTodayJulianDay;
167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Typeface mBold = Typeface.DEFAULT_BOLD;
169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstJulianDay;
170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mLastJulianDay;
171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mMonthLength;
173487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstVisibleDate;
174487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstVisibleDayOfWeek;
175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int[] mEarliestStartHour;    // indexed by the week day offset
176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean[] mHasAllDayEvent;   // indexed by the week day offset
177259b8f9a21fffd3858218581e9daaf689b6b888fErik    private String mEventCountTemplate;
17820b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik    private CharSequence[] mLongPressItems;
17920b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik    private String mLongPressTitle;
180259b8f9a21fffd3858218581e9daaf689b6b888fErik
181259b8f9a21fffd3858218581e9daaf689b6b888fErik    protected static StringBuilder mStringBuilder = new StringBuilder(50);
182259b8f9a21fffd3858218581e9daaf689b6b888fErik    // TODO recreate formatter when locale changes
183259b8f9a21fffd3858218581e9daaf689b6b888fErik    protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
185731f1cb808b7586b93615b408ab2636081fab0dcErik    private Runnable mTZUpdater = new Runnable() {
186731f1cb808b7586b93615b408ab2636081fab0dcErik        @Override
187731f1cb808b7586b93615b408ab2636081fab0dcErik        public void run() {
188731f1cb808b7586b93615b408ab2636081fab0dcErik            String tz = Utils.getTimeZone(mContext, this);
189731f1cb808b7586b93615b408ab2636081fab0dcErik            mBaseDate.timezone = tz;
190731f1cb808b7586b93615b408ab2636081fab0dcErik            mBaseDate.normalize(true);
191731f1cb808b7586b93615b408ab2636081fab0dcErik            mCurrentTime.switchTimezone(tz);
192731f1cb808b7586b93615b408ab2636081fab0dcErik            invalidate();
193731f1cb808b7586b93615b408ab2636081fab0dcErik        }
194731f1cb808b7586b93615b408ab2636081fab0dcErik    };
195731f1cb808b7586b93615b408ab2636081fab0dcErik
1965b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private TodayAnimatorListener mTodayAnimatorListener = new TodayAnimatorListener();
1975b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
1985b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    class TodayAnimatorListener extends AnimatorListenerAdapter {
1995b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        private volatile Animator mAnimator = null;
2005b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        private volatile boolean mFadingIn = false;
2015b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2025b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        @Override
2035b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void onAnimationEnd(Animator animation) {
2045b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            synchronized (this) {
2055b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mAnimator != animation) {
2065b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    animation.removeAllListeners();
2075b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    animation.cancel();
2085b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    return;
2095b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
2105b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mFadingIn) {
2115b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    if (mTodayAnimator != null) {
2125b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mTodayAnimator.removeAllListeners();
2135b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mTodayAnimator.cancel();
2145b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    }
2155b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator = ObjectAnimator
2165b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                            .ofInt(DayView.this, "animateTodayAlpha", 255, 0);
2175b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator = mTodayAnimator;
2185b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mFadingIn = false;
2195b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.addListener(this);
2205b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.setDuration(600);
2215b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.start();
2225b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                } else {
2235b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimateToday = false;
2245b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimateTodayAlpha = 0;
2255b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator.removeAllListeners();
2265b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator = null;
2275b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator = null;
2285b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    invalidate();
2295b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
2305b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            }
2315b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2325b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2335b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void setAnimator(Animator animation) {
2345b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mAnimator = animation;
2355b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2365b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2375b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void setFadingIn(boolean fadingIn) {
2385b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mFadingIn = fadingIn;
2395b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2405b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2415b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
2425b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
24346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() {
24446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
24546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationStart(Animator animation) {
24646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = true;
24746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
24846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
24946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
25046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationCancel(Animator animation) {
25146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = false;
25246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
25346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
25446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
25546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationEnd(Animator animation) {
25646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = false;
25746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            resetSelectedHour();
25846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            invalidate();
25946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
26046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    };
26146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * This variable helps to avoid unnecessarily reloading events by keeping
264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * track of the start millis parameter used for the most recent loading
265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * of events.  If the next reload matches this, then the events are not
266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * reloaded.  To force a reload, set this to zero (this is set to zero
267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * in the method clearCachedEvents()).
268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
269146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private long mLastReloadMillis;
270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
271146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ArrayList<Event> mEvents = new ArrayList<Event>();
27208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private ArrayList<Event> mAllDayEvents = new ArrayList<Event>();
27308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout[] mLayouts = null;
27408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout[] mAllDayLayouts = null;
275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionDay;        // Julian day
276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionHour;
277146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2782fda24534978f55711526dbde747aa50d68dec40RoboErik    boolean mSelectionAllday;
279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2809ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private int mLastSelectionDay;
2819ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private int mLastSelectionHour;
2829ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private Event mLastSelectedEvent;
2839ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
2843eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    /** Width of a day or non-conflicting event */
285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mCellWidth;
286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
287146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Pre-allocate these objects and re-use them
288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect mRect = new Rect();
289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect mDestRect = new Rect();
290259b8f9a21fffd3858218581e9daaf689b6b888fErik    private Rect mSelectionRect = new Rect();
2912fda24534978f55711526dbde747aa50d68dec40RoboErik    // This encloses the more allDay events icon
2922fda24534978f55711526dbde747aa50d68dec40RoboErik    private Rect mExpandAllDayRect = new Rect();
293e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    // TODO Clean up paint usage
294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Paint mPaint = new Paint();
295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Paint mEventTextPaint = new Paint();
296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Paint mSelectionPaint = new Paint();
29702041687c35441e8a21cb763f1fc3a26525292efDavid Brown    private float[] mLines;
298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
299487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstDayOfWeek; // First day of the week
300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
301146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private PopupWindow mPopup;
302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private View mPopupView;
303146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // The number of milliseconds to show the popup window
305146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int POPUP_DISMISS_DELAY = 3000;
306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private DismissPopup mDismissPopup = new DismissPopup();
307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mRemeasure = true;
309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private final EventLoader mEventLoader;
311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected final EventGeometry mEventGeometry;
312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
313e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float GRID_LINE_LEFT_MARGIN = 0;
314000ba1109e2f4e527e9227fe341598ceeb3fbca6Michael Chan    private static final float GRID_LINE_INNER_WIDTH = 1;
3153eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int DAY_GAP = 1;
317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int HOUR_GAP = 1;
3182fda24534978f55711526dbde747aa50d68dec40RoboErik    // This is the standard height of an allday event with no restrictions
3199c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    private static int SINGLE_ALLDAY_HEIGHT = 34;
3202fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
3212fda24534978f55711526dbde747aa50d68dec40RoboErik    * This is the minimum desired height of a allday event.
3222fda24534978f55711526dbde747aa50d68dec40RoboErik    * When unexpanded, allday events will use this height.
3232fda24534978f55711526dbde747aa50d68dec40RoboErik    * When expanded allDay events will attempt to grow to fit all
3242fda24534978f55711526dbde747aa50d68dec40RoboErik    * events at this height.
3252fda24534978f55711526dbde747aa50d68dec40RoboErik    */
3262fda24534978f55711526dbde747aa50d68dec40RoboErik    private static float MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0F; // in pixels
3272fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
3282fda24534978f55711526dbde747aa50d68dec40RoboErik     * This is how big the unexpanded allday height is allowed to be.
3292fda24534978f55711526dbde747aa50d68dec40RoboErik     * It will get adjusted based on screen size
3302fda24534978f55711526dbde747aa50d68dec40RoboErik     */
3312fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int MAX_UNEXPANDED_ALLDAY_HEIGHT =
3322fda24534978f55711526dbde747aa50d68dec40RoboErik            (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
3332fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
3342fda24534978f55711526dbde747aa50d68dec40RoboErik     * This is the minimum size reserved for displaying regular events.
3352fda24534978f55711526dbde747aa50d68dec40RoboErik     * The expanded allDay region can't expand into this.
3362fda24534978f55711526dbde747aa50d68dec40RoboErik     */
3372fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int MIN_HOURS_HEIGHT = 180;
33825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int ALLDAY_TOP_MARGIN = 1;
3392fda24534978f55711526dbde747aa50d68dec40RoboErik    // The largest a single allDay event will become.
340f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34;
341e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3428f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_TOP_MARGIN = 2;
343e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int HOURS_LEFT_MARGIN = 2;
3448f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_RIGHT_MARGIN = 4;
3458f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
34695f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private static int NEW_EVENT_MARGIN = 4;
347462196d197de326006aaf15f8bb7a8d83b8e2a60Peter Ng    private static int NEW_EVENT_WIDTH = 2;
34895f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private static int NEW_EVENT_MAX_LENGTH = 16;
349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
350bd0e834aea317236d574f8738fe11a51fdc87968Erik    private static int CURRENT_TIME_LINE_HEIGHT = 2;
351bd0e834aea317236d574f8738fe11a51fdc87968Erik    private static int CURRENT_TIME_LINE_BORDER_WIDTH = 1;
352763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int CURRENT_TIME_LINE_SIDE_BUFFER = 4;
353763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int CURRENT_TIME_LINE_TOP_OFFSET = 2;
354bd0e834aea317236d574f8738fe11a51fdc87968Erik
355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MINUTES_PER_HOUR = 60;
356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24;
357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000;
358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000);
359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24;
360146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
36125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static final int DECLINED_ALPHA = 0x66000000;
3622fda24534978f55711526dbde747aa50d68dec40RoboErik    // More events text will transition between invisible and this alpha
3632fda24534978f55711526dbde747aa50d68dec40RoboErik    private static final int MORE_EVENTS_MAX_ALPHA = 0x4C;
364e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0;
365e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5;
366e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6;
367e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_LEFT_MARGIN = 5;
368e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_RIGHT_MARGIN = 4;
369e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_BOTTOM_MARGIN = 3;
3703eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float DAY_HEADER_FONT_SIZE = 14;
3718f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static float DATE_HEADER_FONT_SIZE = 32;
3723eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float NORMAL_FONT_SIZE = 12;
3733eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float EVENT_TEXT_FONT_SIZE = 12;
374e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float HOURS_TEXT_SIZE = 12;
375e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float AMPM_TEXT_SIZE = 9;
3768f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int MIN_HOURS_WIDTH = 96;
377763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int MIN_CELL_WIDTH_FOR_TEXT = 20;
378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int MAX_EVENT_TEXT_LEN = 500;
3792fda24534978f55711526dbde747aa50d68dec40RoboErik    // smallest height to draw an event with
38025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static float MIN_EVENT_HEIGHT = 24.0F; // in pixels
3811ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int CALENDAR_COLOR_SQUARE_SIZE = 10;
38225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_TOP_MARGIN = 1;
383763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int EVENT_RECT_BOTTOM_MARGIN = 0;
384763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int EVENT_RECT_LEFT_MARGIN = 1;
385763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int EVENT_RECT_RIGHT_MARGIN = 0;
38625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_STROKE_WIDTH = 2;
3871ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_TEXT_TOP_MARGIN = 2;
38825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_TEXT_BOTTOM_MARGIN = 2;
38995d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng    private static int EVENT_TEXT_LEFT_MARGIN = 6;
39095d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng    private static int EVENT_TEXT_RIGHT_MARGIN = 6;
391e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1;
3921ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN;
3931ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN;
3941ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
3951ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN;
3962fda24534978f55711526dbde747aa50d68dec40RoboErik    // margins and sizing for the expand allday icon
397cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik    private static int EXPAND_ALL_DAY_BOTTOM_MARGIN = 10;
3982fda24534978f55711526dbde747aa50d68dec40RoboErik    // sizing for "box +n" in allDay events
3992fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int EVENT_SQUARE_WIDTH = 10;
4002fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int EVENT_LINE_PADDING = 4;
401ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson    private static int NEW_EVENT_HINT_FONT_SIZE = 12;
402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static int mPressedColor;
404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static int mEventTextColor;
4052fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int mMoreEventsTextColor;
406f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan
40764b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok    private static int mWeek_saturdayColor;
40864b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok    private static int mWeek_sundayColor;
409d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan    private static int mCalendarDateBannerTextColor;
410d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan    private static int mCalendarAmPmLabel;
411ba902466b19b11d587468e354edc58e542707747Michael Chan    private static int mCalendarGridAreaSelected;
4123eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int mCalendarGridLineInnerHorizontalColor;
4133eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int mCalendarGridLineInnerVerticalColor;
41421bd42039c92232647f54461cd20708224cc7eb6Michael Chan    private static int mFutureBgColor;
415e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int mBgColor;
416ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson    private static int mNewEventHintColor;
4178f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int mCalendarHourLabelColor;
4182fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA;
419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
42095d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan    private float mAnimationDistance = 0;
421146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewStartX;
422146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewStartY;
423146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mMaxViewStartY;
424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewHeight;
425146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewWidth;
42646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    private int mGridAreaHeight = -1;
427d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int mCellHeight = 0; // shared among all DayViews
428d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int mMinCellHeight = 32;
429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mScrollStartY;
430146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mPreviousDirection;
431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
432d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /**
433d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * Vertical distance or span between the two touch points at the start of a
434d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * scaling gesture
435d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     */
436d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private float mStartingSpanY = 0;
437d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /** Height of 1 hour in pixels at the start of a scaling gesture */
438d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private int mCellHeightBeforeScaleGesture;
439d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /** The hour at the center two touch points */
440d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private float mGestureCenterHour = 0;
441d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /**
442d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * Flag to decide whether to handle the up event. Cases where up events
443d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * should be ignored are 1) right after a scale gesture and 2) finger was
444d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * down before app launch
445d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     */
446d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private boolean mHandleActionUp = true;
447d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mHoursTextHeight;
4492fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4502fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the area used for allday events
4512fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4522fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAlldayHeight;
4532fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4542fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the allday event area used during animation
4552fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4562fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAnimateDayHeight = 0;
4572fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4582fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of an individual allday event during animation
4592fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4602fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
4612fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
462cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik     * Whether to use the expand or collapse icon.
4632fda24534978f55711526dbde747aa50d68dec40RoboErik     */
46497326ee1ea9b1bb17e0e2bcfe344352d4bb03001RoboErik    private static boolean mUseExpandIcon = true;
4652fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4662fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the day names/numbers
4672fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int DAY_HEADER_HEIGHT = 45;
469f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    /**
470e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * The height of the day names/numbers for multi-day views
471e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
472e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
473e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
474e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * The height of the day names/numbers when viewing a single day
475e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
476e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
477e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
478f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan     * Max of all day events in a given day in this view.
479f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan     */
4802fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mMaxAlldayEvents;
4812fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4822fda24534978f55711526dbde747aa50d68dec40RoboErik     * A count of the number of allday events that were not drawn for each day
4832fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4842fda24534978f55711526dbde747aa50d68dec40RoboErik    private int[] mSkippedAlldayEvents;
4852fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4862fda24534978f55711526dbde747aa50d68dec40RoboErik     * The number of allDay events at which point we start hiding allDay events.
4872fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4882fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mMaxUnexpandedAlldayEventCount = 4;
4892fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4902fda24534978f55711526dbde747aa50d68dec40RoboErik     * Whether or not to expand the allDay area to fill the screen
4912fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4922fda24534978f55711526dbde747aa50d68dec40RoboErik    private static boolean mShowAllAllDayEvents = false;
493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
494146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected int mNumDays = 7;
495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mNumHours = 10;
4963eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
4973eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    /** Width of the time line (list of hours) to the left. */
498146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mHoursWidth;
499146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mDateStrWidth;
50046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** Top of the scrollable region i.e. below date labels and all day events */
501146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstCell;
50246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** First fully visibile hour */
503146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstHour = -1;
50446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** Distance between the mFirstCell and the top of first fully visible hour. */
505146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstHourOffset;
506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mHourStrs;
507146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mDayStrs;
508146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mDayStrs2Letter;
509146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mIs24HourFormat;
510146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
511146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ArrayList<Event> mSelectedEvents = new ArrayList<Event>();
512146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mComputeSelectedEvents;
5138307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik    private boolean mUpdateToast;
514146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Event mSelectedEvent;
515146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Event mPrevSelectedEvent;
516146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect mPrevBox = new Rect();
517146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected final Resources mResources;
5183eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    protected final Drawable mCurrentTimeLine;
5195b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    protected final Drawable mCurrentTimeAnimateLine;
5203eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    protected final Drawable mTodayHeaderDrawable;
5212fda24534978f55711526dbde747aa50d68dec40RoboErik    protected final Drawable mExpandAlldayDrawable;
522cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik    protected final Drawable mCollapseAlldayDrawable;
523f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan    protected Drawable mAcceptedOrTentativeEventBoxDrawable;
524146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String mAmString;
525146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String mPmString;
526146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private DeleteEventHelper mDeleteEventHelper;
5276d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private static int sCounter = 0;
528146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
529146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ContextMenuHandler mContextMenuHandler = new ContextMenuHandler();
530146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
531d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    ScaleGestureDetector mScaleGestureDetector;
532d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
533146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
534146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The initial state of the touch mode when we enter this view.
535146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
536146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_INITIAL_STATE = 0;
537146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
538146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
539146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates we just received the touch event and we are waiting to see if
540146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * it is a tap or a scroll gesture.
541146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
542146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_DOWN = 1;
543146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
544146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
545146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates the touch gesture is a vertical scroll
546146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
547146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_VSCROLL = 0x20;
548146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
549146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
550146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates the touch gesture is a horizontal scroll
551146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
552146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_HSCROLL = 0x40;
553146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
554146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mTouchMode = TOUCH_MODE_INITIAL_STATE;
555146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
556146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
557146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS.
558146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
559146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_HIDDEN = 0;
560ef2185bf0c7a80154a79788915db04d4f9e7ad4dMichael Chan    private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet
561146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_SELECTED = 2;
562146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_LONGPRESS = 3;
563146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
564146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionMode = SELECTION_HIDDEN;
565146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
566146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mScrolling = false;
567146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
5684f58287a1e02325337e39073a50bbb880094099fRoboErik    private float mInitialScrollX;
5694f58287a1e02325337e39073a50bbb880094099fRoboErik    private float mInitialScrollY;
5702fda24534978f55711526dbde747aa50d68dec40RoboErik
5715b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private boolean mAnimateToday = false;
5725b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private int mAnimateTodayAlpha = 0;
5735b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
5742fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the height of the allday region
5752fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mAlldayAnimator;
5762fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the height of events in the allday region
5772fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mAlldayEventAnimator;
5782fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the transparency of the more events text
5792fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mMoreAlldayEventsAnimator;
5805b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    // Animates the current time marker when Today is pressed
5815b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    ObjectAnimator mTodayAnimator;
5822fda24534978f55711526dbde747aa50d68dec40RoboErik    // whether or not an event is stopping because it was cancelled
5832fda24534978f55711526dbde747aa50d68dec40RoboErik    private boolean mCancellingAnimations = false;
5842fda24534978f55711526dbde747aa50d68dec40RoboErik    // tracks whether a touch originated in the allday area
5852fda24534978f55711526dbde747aa50d68dec40RoboErik    private boolean mTouchStartedInAlldayArea = false;
5862fda24534978f55711526dbde747aa50d68dec40RoboErik
587ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private CalendarController mController;
588ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private ViewSwitcher mViewSwitcher;
589ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private GestureDetector mGestureDetector;
5903f6328f579b05c02606572c777541b97aa04f9f3Michael Chan    private OverScroller mScroller;
591433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private EdgeEffect mEdgeEffectTop;
592433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private EdgeEffect mEdgeEffectBottom;
593433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private boolean mCallEdgeEffectOnAbsorb;
594433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private final int OVERFLING_DISTANCE;
595433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private float mLastVelocity;
596433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
5972c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private ScrollInterpolator mHScrollInterpolator;
598aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private AccessibilityManager mAccessibilityMgr = null;
599aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private boolean mIsAccessibilityEnabled = false;
6009ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private boolean mTouchExplorationEnabled = false;
6019ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private String mCreateNewEventString;
602ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson    private String mNewEventHintString;
603705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
604d190418220467c02327dd9b3ae1eae238889720bMichael Chan    public DayView(Context context, CalendarController controller,
60575d9b567e1fd3b9c6945cd0afe28781f5a19c6c8Michael Chan            ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) {
606ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        super(context);
607aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mContext = context;
608aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        initAccessibilityVariables();
609aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan
6102fda24534978f55711526dbde747aa50d68dec40RoboErik        mResources = context.getResources();
6119ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        mCreateNewEventString = mResources.getString(R.string.event_create);
612ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson        mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint);
61309b1b2e1e7eb9bc291d4e70c993471d3c9ffa799Michael Chan
614e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DATE_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.date_header_text_size);
615e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.day_label_text_size);
616e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        ONE_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.one_day_header_height);
617e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.day_header_bottom_margin);
618e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        EXPAND_ALL_DAY_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.all_day_bottom_margin);
619e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_TEXT_SIZE = (int) mResources.getDimension(R.dimen.hours_text_size);
620e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        AMPM_TEXT_SIZE = (int) mResources.getDimension(R.dimen.ampm_text_size);
621e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        MIN_HOURS_WIDTH = (int) mResources.getDimension(R.dimen.min_hours_width);
622e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_LEFT_MARGIN = (int) mResources.getDimension(R.dimen.hours_left_margin);
623e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_RIGHT_MARGIN = (int) mResources.getDimension(R.dimen.hours_right_margin);
624e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        MULTI_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.day_header_height);
6255ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_TEXT_FONT_SIZE = (int) mResources.getDimension(R.dimen.event_text_size);
626ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson        NEW_EVENT_HINT_FONT_SIZE = (int) mResources.getDimension(R.dimen.new_event_hint_text_size);
6275ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height);
628714c299ea5c05858e273d98efbacf20fedd328f1RoboErik        MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT;
6295ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_TEXT_TOP_MARGIN = (int) mResources.getDimension(R.dimen.event_text_vertical_margin);
6305ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN;
6315ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN;
6325ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN;
63309b1b2e1e7eb9bc291d4e70c993471d3c9ffa799Michael Chan
63495d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_TEXT_LEFT_MARGIN = (int) mResources
63595d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng                .getDimension(R.dimen.event_text_horizontal_margin);
63695d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
63795d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
63895d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
63995d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng
640705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan        if (mScale == 0) {
641e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
6422fda24534978f55711526dbde747aa50d68dec40RoboErik            mScale = mResources.getDisplayMetrics().density;
643705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan            if (mScale != 1) {
644705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                SINGLE_ALLDAY_HEIGHT *= mScale;
645705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                ALLDAY_TOP_MARGIN *= mScale;
646f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale;
647705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
648705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                NORMAL_FONT_SIZE *= mScale;
6498f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan                GRID_LINE_LEFT_MARGIN *= mScale;
6508f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan                HOURS_TOP_MARGIN *= mScale;
651705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                MIN_CELL_WIDTH_FOR_TEXT *= mScale;
6522fda24534978f55711526dbde747aa50d68dec40RoboErik                MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale;
6532fda24534978f55711526dbde747aa50d68dec40RoboErik                mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
654705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
655bd0e834aea317236d574f8738fe11a51fdc87968Erik                CURRENT_TIME_LINE_HEIGHT *= mScale;
656bd0e834aea317236d574f8738fe11a51fdc87968Erik                CURRENT_TIME_LINE_BORDER_WIDTH *= mScale;
657763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik                CURRENT_TIME_LINE_SIDE_BUFFER *= mScale;
658763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik                CURRENT_TIME_LINE_TOP_OFFSET *= mScale;
659bd0e834aea317236d574f8738fe11a51fdc87968Erik
660d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                MIN_Y_SPAN *= mScale;
661d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                MAX_CELL_HEIGHT *= mScale;
662697157470a6f0d8e7b806af9b4fb7fbe0bccae87Erik                DEFAULT_CELL_HEIGHT *= mScale;
6633eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_HEIGHT *= mScale;
6643eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_LEFT_MARGIN *= mScale;
6653eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_RIGHT_MARGIN *= mScale;
6663eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale;
6673eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale;
6683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale;
6693eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                CALENDAR_COLOR_SQUARE_SIZE *= mScale;
67011ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_TOP_MARGIN *= mScale;
67111ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_BOTTOM_MARGIN *= mScale;
67225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale;
67311ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_LEFT_MARGIN *= mScale;
67411ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_RIGHT_MARGIN *= mScale;
67525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                EVENT_RECT_STROKE_WIDTH *= mScale;
6762fda24534978f55711526dbde747aa50d68dec40RoboErik                EVENT_SQUARE_WIDTH *= mScale;
6772fda24534978f55711526dbde747aa50d68dec40RoboErik                EVENT_LINE_PADDING *= mScale;
67895f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                NEW_EVENT_MARGIN *= mScale;
67995f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                NEW_EVENT_WIDTH *= mScale;
68095f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                NEW_EVENT_MAX_LENGTH *= mScale;
681705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan            }
682705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan        }
683e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
684e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_HEIGHT = numDays == 1 ? ONE_DAY_HEADER_HEIGHT : MULTI_DAY_HEADER_HEIGHT;
685705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
6869175d29276c78a2a294ce2fce112432171ef7c7cRoboErik        mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_indicator_holo_light);
6875b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        mCurrentTimeAnimateLine = mResources
6885b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                .getDrawable(R.drawable.timeline_indicator_activated_holo_light);
6893eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light);
690cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik        mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_allday_expand_holo_light);
691cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik        mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_allday_collapse_holo_light);
692ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson        mNewEventHintColor =  mResources.getColor(R.color.new_event_hint_text_color);
693f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        mAcceptedOrTentativeEventBoxDrawable = mResources
694f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan                .getDrawable(R.drawable.panel_month_event_holo_light);
69525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik
696ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mEventLoader = eventLoader;
697146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry = new EventGeometry();
698146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT);
699146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setHourGap(HOUR_GAP);
700e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mEventGeometry.setCellMargin(DAY_GAP);
70120b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        mLongPressItems = new CharSequence[] {
70220b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik            mResources.getString(R.string.new_event_dialog_option)
70320b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        };
70420b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        mLongPressTitle = mResources.getString(R.string.new_event_dialog_label);
705ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mDeleteEventHelper = new DeleteEventHelper(context, null, false /* don't exit when done */);
706666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
707ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mController = controller;
708ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher = viewSwitcher;
709ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mGestureDetector = new GestureDetector(context, new CalendarGestureListener());
710d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mScaleGestureDetector = new ScaleGestureDetector(getContext(), this);
71175d9b567e1fd3b9c6945cd0afe28781f5a19c6c8Michael Chan        mNumDays = numDays;
712d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mCellHeight == 0) {
713d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = Utils.getSharedPreference(mContext,
714d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT);
715d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
7163f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScroller = new OverScroller(context);
7172c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        mHScrollInterpolator = new ScrollInterpolator();
718433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectTop = new EdgeEffect(context);
719433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectBottom = new EdgeEffect(context);
720433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        OVERFLING_DISTANCE = ViewConfiguration.get(context).getScaledOverflingDistance();
721433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
722ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        init(context);
723146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
724146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
725146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void init(Context context) {
726146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setFocusable(true);
727146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
728146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Allow focus in touch mode so that we can do keyboard shortcuts
729146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // even after we've entered touch mode.
730146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setFocusableInTouchMode(true);
731146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setClickable(true);
732146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setOnCreateContextMenuListener(this);
733146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
734487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstDayOfWeek = Utils.getFirstDayOfWeek(context);
735146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
736731f1cb808b7586b93615b408ab2636081fab0dcErik        mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater));
737bd0e834aea317236d574f8738fe11a51fdc87968Erik        long currentTime = System.currentTimeMillis();
738bd0e834aea317236d574f8738fe11a51fdc87968Erik        mCurrentTime.set(currentTime);
739bd0e834aea317236d574f8738fe11a51fdc87968Erik        //The % makes it go off at the next increment of 5 minutes.
740bd0e834aea317236d574f8738fe11a51fdc87968Erik        postDelayed(mUpdateCurrentTime,
741bd0e834aea317236d574f8738fe11a51fdc87968Erik                UPDATE_CURRENT_TIME_DELAY - (currentTime % UPDATE_CURRENT_TIME_DELAY));
742bd0e834aea317236d574f8738fe11a51fdc87968Erik        mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
743bd0e834aea317236d574f8738fe11a51fdc87968Erik
74464b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok        mWeek_saturdayColor = mResources.getColor(R.color.week_saturday);
74564b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok        mWeek_sundayColor = mResources.getColor(R.color.week_sunday);
746d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color);
74721bd42039c92232647f54461cd20708224cc7eb6Michael Chan        mFutureBgColor = mResources.getColor(R.color.calendar_future_bg_color);
748e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mBgColor = mResources.getColor(R.color.calendar_hour_background);
749d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label);
750ba902466b19b11d587468e354edc58e542707747Michael Chan        mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected);
751e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mCalendarGridLineInnerHorizontalColor = mResources
752e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                .getColor(R.color.calendar_grid_line_inner_horizontal_color);
753e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mCalendarGridLineInnerVerticalColor = mResources
754e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                .getColor(R.color.calendar_grid_line_inner_vertical_color);
7558f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label);
756146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPressedColor = mResources.getColor(R.color.pressed);
757146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextColor = mResources.getColor(R.color.calendar_event_text_color);
758e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color);
75911638ad4fb56b978770aebe75deac7a0e6ac60d2Erik
760146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE);
761146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setTextAlign(Paint.Align.LEFT);
762146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setAntiAlias(true);
763146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
764146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color);
765146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mSelectionPaint;
766146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setColor(gridLineColor);
7673937a47bc5a1afb9701e1d0565fae31f5800590dErik        p.setStyle(Style.FILL);
768146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(false);
769146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
770146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p = mPaint;
771146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
772146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
773146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Allocate space for 2 weeks worth of weekday names so that we can
774146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // easily start the week display at any week day.
775146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDayStrs = new String[14];
776146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
777146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Also create an array of 2-letter abbreviations.
778146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDayStrs2Letter = new String[14];
779146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
780146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
781146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int index = i - Calendar.SUNDAY;
782146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // e.g. Tue for Tuesday
783e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM)
784e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    .toUpperCase();
785146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mDayStrs[index + 7] = mDayStrs[index];
786146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // e.g. Tu for Tuesday
787e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT)
788e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    .toUpperCase();
78952b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer
79052b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            // If we don't have 2-letter day strings, fall back to 1-letter.
79152b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            if (mDayStrs2Letter[index].equals(mDayStrs[index])) {
79252b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer                mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST);
79352b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            }
79452b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer
795146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mDayStrs2Letter[index + 7] = mDayStrs2Letter[index];
796146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
797146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
798146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Figure out how much space we need for the 3-letter abbrev names
799146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // in the worst case.
8003eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextSize(DATE_HEADER_FONT_SIZE);
801146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(mBold);
802146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] dateStrs = {" 28", " 30"};
803146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDateStrWidth = computeMaxStringWidth(0, dateStrs, p);
8043eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextSize(DAY_HEADER_FONT_SIZE);
805146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p);
806146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
807e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
808146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(null);
80961a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan        handleOnResume();
810146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
811e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase();
812e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase();
813146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] ampm = {mAmString, mPmString};
814e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(AMPM_TEXT_SIZE);
815e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mHoursWidth = Math.max(HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p)
816e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                + HOURS_RIGHT_MARGIN);
8178f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth);
818146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
819146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        LayoutInflater inflater;
820146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
821146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView = inflater.inflate(R.layout.bubble_event, null);
822146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView.setLayoutParams(new ViewGroup.LayoutParams(
823df279e3f9d5b0f189f812c52aca78d18178e4445Romain Guy                ViewGroup.LayoutParams.MATCH_PARENT,
824146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                ViewGroup.LayoutParams.WRAP_CONTENT));
825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup = new PopupWindow(context);
826146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setContentView(mPopupView);
827146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Resources.Theme dialogTheme = getResources().newTheme();
828146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        dialogTheme.applyStyle(android.R.style.Theme_Dialog, true);
829146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] {
830146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            android.R.attr.windowBackground });
831146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setBackgroundDrawable(ta.getDrawable(0));
832146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ta.recycle();
833146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
834146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Enable touching the popup window
835146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView.setOnClickListener(this);
836cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        // Catch long clicks for creating a new event
837cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        setOnLongClickListener(this);
838146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
839731f1cb808b7586b93615b408ab2636081fab0dcErik        mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater));
840146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = System.currentTimeMillis();
841146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mBaseDate.set(millis);
842146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
843146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEarliestStartHour = new int[mNumDays];
844146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mHasAllDayEvent = new boolean[mNumDays];
84502041687c35441e8a21cb763f1fc3a26525292efDavid Brown
84602041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // mLines is the array of points used with Canvas.drawLines() in
84702041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // drawGridBackground() and drawAllDayEvents().  Its size depends
84802041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // on the max number of lines that can ever be drawn by any single
84902041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // drawLines() call in either of those methods.
85002041687c35441e8a21cb763f1fc3a26525292efDavid Brown        final int maxGridLines = (24 + 1)  // max horizontal lines we might draw
851e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                + (mNumDays + 1); // max vertical lines we might draw
85202041687c35441e8a21cb763f1fc3a26525292efDavid Brown        mLines = new float[maxGridLines * 4];
853146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
854146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
855146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
856146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * This is called when the popup window is pressed.
857146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
858146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onClick(View v) {
859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (v == mPopupView) {
860146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Pretend it was a trackball click because that will always
861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // jump to the "View event" screen.
862146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchViews(true /* trackball */);
863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
864146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
865146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
86661a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan    public void handleOnResume() {
867aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        initAccessibilityVariables();
868ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mIs24HourFormat = DateFormat.is24HourFormat(mContext);
86969ab334d6988eda70ba53618506d23ebf7d7b405Erik        mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm;
87061a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan        mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext);
8719ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        mLastSelectionDay = 0;
8729ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        mLastSelectionHour = 0;
8739ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        mLastSelectedEvent = null;
874f7cf45455ce8cc5588479dcbb5d876f7638f79dcRoboErik        mSelectionMode = SELECTION_HIDDEN;
87569ab334d6988eda70ba53618506d23ebf7d7b405Erik    }
87669ab334d6988eda70ba53618506d23ebf7d7b405Erik
877aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private void initAccessibilityVariables() {
878aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mAccessibilityMgr = (AccessibilityManager) mContext
879aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                .getSystemService(Service.ACCESSIBILITY_SERVICE);
880aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled();
881aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mTouchExplorationEnabled = isTouchExplorationEnabled();
882aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    }
883aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan
884146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
885146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Returns the start of the selected time in milliseconds since the epoch.
886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @return selected time in UTC milliseconds since the epoch.
888146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
889146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    long getSelectedTimeInMillis() {
890146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
891146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
892146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
893146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
894146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
895146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
896146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
897146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time.normalize(true /* ignore isDst */);
898146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
899146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
900146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Time getSelectedTime() {
901146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
902146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
903146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
904146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
905146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
906146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
907146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
908146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.normalize(true /* ignore isDst */);
909146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time;
910146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
911146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
912146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
913146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Returns the start of the selected time in minutes since midnight,
914146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * local time.  The derived class must ensure that this is consistent
915146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * with the return value from getSelectedTimeInMillis().
916146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    int getSelectedMinutesSinceMidnight() {
918146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return mSelectionHour * MINUTES_PER_HOUR;
919146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
92146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    int getFirstVisibleHour() {
92246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        return mFirstHour;
92346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
92446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
92546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    void setFirstVisibleHour(int firstHour) {
92646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mFirstHour = firstHour;
92746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mFirstHourOffset = 0;
92846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
92946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
9305b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public void setSelected(Time time, boolean ignoreTime, boolean animateToday) {
931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mBaseDate.set(time);
932146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionHour = mBaseDate.hour;
933146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = null;
934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevSelectedEvent = null;
935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = mBaseDate.toMillis(false /* use isDst */);
936146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionDay = Time.getJulianDay(millis, mBaseDate.gmtoff);
937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
938146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = true;
939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
94046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int gotoY = Integer.MIN_VALUE;
94146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
94246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (!ignoreTime && mGridAreaHeight != -1) {
94346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int lastHour = 0;
94446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
94546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (mBaseDate.hour < mFirstHour) {
94646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // Above visible region
94746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP);
94846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else {
94946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                lastHour = (mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP)
95046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + mFirstHour;
95146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
95246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mBaseDate.hour >= lastHour) {
95346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // Below visible region
95446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
95546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // target hour + 1 (to give it room to see the event) -
95646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // grid height (to get the y of the top of the visible
95746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // region)
95855a26d7062d5aa1a1b001b7271bb137276ab807eErik                    gotoY = (int) ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f)
95946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                            * (mCellHeight + HOUR_GAP) - mGridAreaHeight);
96046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
96146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
96246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
96346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (DEBUG) {
96446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                Log.e(TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH "
96546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight
96646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + " ymax " + mMaxViewStartY);
96746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
96846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
96946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (gotoY > mMaxViewStartY) {
97046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = mMaxViewStartY;
97146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) {
97246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = 0;
97346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
97446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
97546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
976146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        recalc();
977146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
978146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mRemeasure = true;
979146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
98046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
9815b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        boolean delayAnimateToday = false;
98246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (gotoY != Integer.MIN_VALUE) {
9835b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            ValueAnimator scrollAnim = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY);
9845b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            scrollAnim.setDuration(GOTO_SCROLL_DURATION);
98546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.setInterpolator(new AccelerateDecelerateInterpolator());
98646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.addListener(mAnimatorListener);
98746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.start();
9885b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            delayAnimateToday = true;
9895b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
9905b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        if (animateToday) {
9915b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            synchronized (mTodayAnimatorListener) {
9925b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mTodayAnimator != null) {
9935b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.removeAllListeners();
9945b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.cancel();
9955b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
9965b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha",
9975b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mAnimateTodayAlpha, 255);
9985b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mAnimateToday = true;
9995b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimatorListener.setFadingIn(true);
10005b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimatorListener.setAnimator(mTodayAnimator);
10015b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.addListener(mTodayAnimatorListener);
10025b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.setDuration(150);
10035b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (delayAnimateToday) {
10045b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION);
10055b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
10065b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.start();
10075b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            }
100846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
1009aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        sendAccessibilityEventAsNeeded(false);
101046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
101146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
101246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    public void setViewStartY(int viewStartY) {
101346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (viewStartY > mMaxViewStartY) {
101446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            viewStartY = mMaxViewStartY;
101546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
101646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
101746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mViewStartY = viewStartY;
101846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
101946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        computeFirstHour();
102046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        invalidate();
1021146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1022146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
10235b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public void setAnimateTodayAlpha(int todayAlpha) {
10245b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        mAnimateTodayAlpha = todayAlpha;
10255b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        invalidate();
10265b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
10275b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
1028146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public Time getSelectedDay() {
1029146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
1030146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
1031146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
1032146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1033146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
1034146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
1035146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
1036146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.normalize(true /* ignore isDst */);
1037146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time;
1038146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1039146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1040eaafa2b48be7194a61754604ae37b3d62e9118d8Erik    public void updateTitle() {
1041eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        Time start = new Time(mBaseDate);
1042eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        start.normalize(true);
1043eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        Time end = new Time(start);
1044eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.monthDay += mNumDays - 1;
1045eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        // Move it forward one minute so the formatter doesn't lose a day
1046eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.minute += 1;
1047eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.normalize(true);
1048eaafa2b48be7194a61754604ae37b3d62e9118d8Erik
10499e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        long formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
10509e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        if (mNumDays != 1) {
10519e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            // Don't show day of the month if for multi-day view
10529e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            formatFlags |= DateUtils.FORMAT_NO_MONTH_DAY;
10539e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan
10549e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            // Abbreviate the month if showing multiple months
10559e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            if (start.month != end.month) {
10569e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan                formatFlags |= DateUtils.FORMAT_ABBREV_MONTH;
10579e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            }
10589e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        }
10599e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan
1060f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        mController.sendEvent(this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT,
10619e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan                formatFlags, null, null);
1062eaafa2b48be7194a61754604ae37b3d62e9118d8Erik    }
1063eaafa2b48be7194a61754604ae37b3d62e9118d8Erik
106483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan    /**
1065487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * return a negative number if "time" is comes before the visible time
1066487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * range, a positive number if "time" is after the visible time range, and 0
1067487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * if it is in the visible time range.
106883b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan     */
1069487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    public int compareToVisibleTimeRange(Time time) {
1070487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1071487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedHour = mBaseDate.hour;
1072487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedMinute = mBaseDate.minute;
1073487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedSec = mBaseDate.second;
1074487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1075487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.hour = 0;
1076487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.minute = 0;
1077487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.second = 0;
1078487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1079030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        if (DEBUG) {
1080030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            Log.d(TAG, "Begin " + mBaseDate.toString());
1081030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            Log.d(TAG, "Diff  " + time.toString());
1082030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        }
1083487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
108483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        // Compare beginning of range
108583b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        int diff = Time.compare(time, mBaseDate);
1086487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        if (diff > 0) {
1087487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            // Compare end of range
1088487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.monthDay += mNumDays;
1089487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.normalize(true);
1090487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            diff = Time.compare(time, mBaseDate);
1091487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1092030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            if (DEBUG) Log.d(TAG, "End   " + mBaseDate.toString());
1093487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1094487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.monthDay -= mNumDays;
1095487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.normalize(true);
1096487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            if (diff < 0) {
1097487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                // in visible time
1098487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                diff = 0;
1099487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            } else if (diff == 0) {
1100487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                // Midnight of following day
1101487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                diff = 1;
1102487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            }
110383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        }
110483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1105030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        if (DEBUG) Log.d(TAG, "Diff: " + diff);
110683b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1107487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.hour = savedHour;
1108487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.minute = savedMinute;
1109487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.second = savedSec;
1110487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        return diff;
111183b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan    }
111283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void recalc() {
1114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Set the base date to the beginning of the week if we are displaying
1115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // 7 days at a time.
1116146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays == 7) {
11176d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            adjustToBeginningOfWeek(mBaseDate);
1118146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1119146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1120146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final long start = mBaseDate.toMillis(false /* use isDst */);
1121146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff);
1122146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastJulianDay = mFirstJulianDay + mNumDays - 1;
1123146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1124146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY);
1125487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstVisibleDate = mBaseDate.monthDay;
1126487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstVisibleDayOfWeek = mBaseDate.weekDay;
1127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
11296d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private void adjustToBeginningOfWeek(Time time) {
11306d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        int dayOfWeek = time.weekDay;
11316d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        int diff = dayOfWeek - mFirstDayOfWeek;
11326d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (diff != 0) {
11336d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            if (diff < 0) {
11346d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan                diff += 7;
11356d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            }
11366d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            time.monthDay -= diff;
11376d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            time.normalize(true /* ignore isDst */);
11386d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
11396d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    }
11406d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
1141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected void onSizeChanged(int width, int height, int oldw, int oldh) {
1143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewWidth = width;
1144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewHeight = height;
1145433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectTop.setSize(mViewWidth, mViewHeight);
1146433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectBottom.setSize(mViewWidth, mViewHeight);
1147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int gridAreaWidth = width - mHoursWidth;
1148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays;
1149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1150d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        // This would be about 1 day worth in a 7 day view
1151d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        mHorizontalSnapBackThreshold = width / 7;
1152d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan
1153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = new Paint();
1154e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
1155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mHoursTextHeight = (int) Math.abs(p.ascent());
1156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        remeasure(width, height);
1157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
115946b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa    /**
116046b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     * Measures the space needed for various parts of the view after
116146b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     * loading new events.  This can change if there are all-day events.
116246b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     */
1163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void remeasure(int width, int height) {
11642fda24534978f55711526dbde747aa50d68dec40RoboErik        // Shrink to fit available space but make sure we can display at least two events
11652fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
11662fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6);
11672fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(MAX_UNEXPANDED_ALLDAY_HEIGHT,
11682fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 2);
11692fda24534978f55711526dbde747aa50d68dec40RoboErik        mMaxUnexpandedAlldayEventCount =
11702fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
1171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear the array of earliest start times, and the array
1173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // indicating presence of an all-day event.
1174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++) {
1175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mEarliestStartHour[day] = 25;  // some big number
1176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mHasAllDayEvent[day] = false;
1177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
11792fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxAllDayEvents = mMaxAlldayEvents;
1180c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa
11812fda24534978f55711526dbde747aa50d68dec40RoboErik        // The min is where 24 hours cover the entire visible area
11827939fa59d64c1e6ac95f352e24d761dbacf67547RoboErik        mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, (int) MIN_EVENT_HEIGHT);
11832fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mCellHeight < mMinCellHeight) {
11842fda24534978f55711526dbde747aa50d68dec40RoboErik            mCellHeight = mMinCellHeight;
1185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
11879c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan        // Calculate mAllDayHeight
11883eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mFirstCell = DAY_HEADER_HEIGHT;
1189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int allDayHeight = 0;
1190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (maxAllDayEvents > 0) {
11912fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
1192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is at most one all-day event per day, then use less
1193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // space (but more than the space for a single event).
1194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (maxAllDayEvents == 1) {
1195146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                allDayHeight = SINGLE_ALLDAY_HEIGHT;
11962fda24534978f55711526dbde747aa50d68dec40RoboErik            } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount){
1197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Allow the all-day area to grow in height depending on the
1198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // number of all-day events we need to show, up to a limit.
1199f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
12002fda24534978f55711526dbde747aa50d68dec40RoboErik                if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
12012fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT;
12022fda24534978f55711526dbde747aa50d68dec40RoboErik                }
12032fda24534978f55711526dbde747aa50d68dec40RoboErik            } else {
12042fda24534978f55711526dbde747aa50d68dec40RoboErik                // if we have more than the magic number, check if we're animating
12052fda24534978f55711526dbde747aa50d68dec40RoboErik                // and if not adjust the sizes appropriately
12062fda24534978f55711526dbde747aa50d68dec40RoboErik                if (mAnimateDayHeight != 0) {
12072fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Don't shrink the space past the final allDay space. The animation
12082fda24534978f55711526dbde747aa50d68dec40RoboErik                    // continues to hide the last event so the more events text can
12092fda24534978f55711526dbde747aa50d68dec40RoboErik                    // fade in.
12102fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT);
12112fda24534978f55711526dbde747aa50d68dec40RoboErik                } else {
12122fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Try to fit all the events in
12132fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = (int) (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
12142fda24534978f55711526dbde747aa50d68dec40RoboErik                    // But clip the area depending on which mode we're in
12152fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
1216e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                        allDayHeight = (int) (mMaxUnexpandedAlldayEventCount *
1217e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                                MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
12182fda24534978f55711526dbde747aa50d68dec40RoboErik                    } else if (allDayHeight > maxAllAllDayHeight) {
12192fda24534978f55711526dbde747aa50d68dec40RoboErik                        allDayHeight = maxAllAllDayHeight;
12202fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
1221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
12233eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN;
1224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
12252fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = false;
1226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
12272fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayHeight = allDayHeight;
1228146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1229146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mGridAreaHeight = height - mFirstCell;
1230d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
12312fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the expand icon position
1232e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int allDayIconWidth = mExpandAlldayDrawable.getIntrinsicWidth();
1233e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.left = Math.max((mHoursWidth - allDayIconWidth) / 2,
1234e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                EVENT_ALL_DAY_TEXT_LEFT_MARGIN);
1235e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.right = Math.min(mExpandAllDayRect.left + allDayIconWidth, mHoursWidth
1236e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN);
1237cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik        mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN;
1238e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.top = mExpandAllDayRect.bottom
1239e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                - mExpandAlldayDrawable.getIntrinsicHeight();
1240e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
124146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP);
1242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setHourHeight(mCellHeight);
1243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
124427d671cdba3e8b50219def2c9e1118abe987f908Daisuke Miyakawa        final long minimumDurationMillis = (long)
124527d671cdba3e8b50219def2c9e1118abe987f908Daisuke Miyakawa                (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f));
12462fda24534978f55711526dbde747aa50d68dec40RoboErik        Event.computePositions(mEvents, minimumDurationMillis);
124746b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa
1248c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // Compute the top of our reachable view
1249c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
1250d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (DEBUG) {
1251d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.e(TAG, "mViewStartY: " + mViewStartY);
1252d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY);
1253d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
1254d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mViewStartY > mMaxViewStartY) {
1255d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = mMaxViewStartY;
1256d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            computeFirstHour();
1257d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
1258146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour == -1) {
1260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            initFirstHour();
1261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHourOffset = 0;
1262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // When we change the base date, the number of all-day events may
1265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // change and that changes the cell height.  When we switch dates,
1266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // we use the mFirstHourOffset from the previous view, but that may
1267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // be too large for the new view if the cell height is smaller.
1268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHourOffset >= mCellHeight + HOUR_GAP) {
1269146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHourOffset = mCellHeight + HOUR_GAP - 1;
1270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1271146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset;
1272146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
127346b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa        final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP);
1274666fc5e8ddbac15c951aad738bd818ee307eae42Erik        //When we get new events we don't want to dismiss the popup unless the event changes
1275666fc5e8ddbac15c951aad738bd818ee307eae42Erik        if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) {
1276666fc5e8ddbac15c951aad738bd818ee307eae42Erik            mPopup.dismiss();
1277666fc5e8ddbac15c951aad738bd818ee307eae42Erik        }
1278146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setWidth(eventAreaWidth - 20);
1279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
1280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1281146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1283146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Initialize the state for another view.  The given view is one that has
1284146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * its own bitmap and will use an animation to replace the current view.
1285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The current view and new view are either both Week views or both Day
1286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * views.  They differ in their base date.
1287146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
1288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param view the view to initialize.
1289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1290d190418220467c02327dd9b3ae1eae238889720bMichael Chan    private void initView(DayView view) {
1291146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mSelectionHour = mSelectionHour;
1292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mSelectedEvents.clear();
1293146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mComputeSelectedEvents = true;
1294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mFirstHour = mFirstHour;
1295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mFirstHourOffset = mFirstHourOffset;
1296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.remeasure(getWidth(), getHeight());
12972fda24534978f55711526dbde747aa50d68dec40RoboErik        view.initAllDayHeights();
1298e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
1299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mSelectedEvent = null;
1300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mPrevSelectedEvent = null;
1301487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        view.mFirstDayOfWeek = mFirstDayOfWeek;
1302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (view.mEvents.size() > 0) {
13032fda24534978f55711526dbde747aa50d68dec40RoboErik            view.mSelectionAllday = mSelectionAllday;
1304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
13052fda24534978f55711526dbde747aa50d68dec40RoboErik            view.mSelectionAllday = false;
1306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Redraw the screen so that the selection box will be redrawn.  We may
1309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // have scrolled to a different part of the day in some other view
1310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // so the selection box in this view may no longer be visible.
1311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.recalc();
1312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1315146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Switch to another view based on what was selected (an event or a free
1316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * slot) and how it was selected (by touch or by trackball).
1317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
1318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param trackBallSelection true if the selection was made using the
1319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * trackball.
1320146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void switchViews(boolean trackBallSelection) {
1322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event selectedEvent = mSelectedEvent;
1323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.dismiss();
1325666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
1326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays > 1) {
1327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // This is the Week view.
1328146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // With touch, we always switch to Day/Agenda View
1329146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // With track ball, if we selected a free slot, then create an event.
1330146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected a specific event, switch to EventInfo view.
1331146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (trackBallSelection) {
1332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (selectedEvent == null) {
1333146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Switch to the EditEvent view
1334146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long startMillis = getSelectedTimeInMillis();
1335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
1336edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    long extraLong = 0;
1337edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    if (mSelectionAllday) {
1338edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                        extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
1339edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    }
1340edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    mController.sendEventRelatedEventWithExtra(this, EventType.CREATE_EVENT, -1,
1341edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            startMillis, endMillis, -1, -1, extraLong, -1);
1342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else {
1343aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    if (mIsAccessibilityEnabled) {
1344aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        mAccessibilityMgr.interrupt();
1345aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    }
1346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Switch to the EventInfo view
13479e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
134887f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
134987f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            getSelectedTimeInMillis());
1350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1351146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // This was a touch selection.  If the touch selected a single
1353146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // unambiguous event, then view that event.  Otherwise go to
1354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Day/Agenda view.
1355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectedEvents.size() == 1) {
1356aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    if (mIsAccessibilityEnabled) {
1357aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        mAccessibilityMgr.interrupt();
1358aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    }
13599e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
136087f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
136187f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            getSelectedTimeInMillis());
1362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
1365146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // This is the Day view.
1366146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected a free slot, then create an event.
1367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected an event, then go to the EventInfo view.
1368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (selectedEvent == null) {
1369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Switch to the EditEvent view
1370146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long startMillis = getSelectedTimeInMillis();
1371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
1372edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                long extraLong = 0;
1373edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                if (mSelectionAllday) {
1374edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
1375edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                }
1376edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                mController.sendEventRelatedEventWithExtra(this, EventType.CREATE_EVENT, -1,
1377edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                        startMillis, endMillis, -1, -1, extraLong, -1);
1378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1379aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                if (mIsAccessibilityEnabled) {
1380aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    mAccessibilityMgr.interrupt();
1381aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                }
13829e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
138387f993ff96172f508d3f241f8a425f953c7eb739RoboErik                        selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
138487f993ff96172f508d3f241f8a425f953c7eb739RoboErik                        getSelectedTimeInMillis());
1385146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1388146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1389146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1390146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onKeyUp(int keyCode, KeyEvent event) {
1391146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = false;
1392146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long duration = event.getEventTime() - event.getDownTime();
1393146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (keyCode) {
1395146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            case KeyEvent.KEYCODE_DPAD_CENTER:
1396146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectionMode == SELECTION_HIDDEN) {
1397146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Don't do anything unless the selection is visible.
1398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
1399146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1400146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1401146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectionMode == SELECTION_PRESSED) {
1402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This was the first press when there was nothing selected.
1403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Change the selection from the "pressed" state to the
1404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // the "selected" state.  We treat short-press and
1405146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // long-press the same here because nothing was selected.
1406146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectionMode = SELECTION_SELECTED;
1407146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
1408146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
1409146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Check the duration to determine if this was a short press
1412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (duration < ViewConfiguration.getLongPressTimeout()) {
1413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    switchViews(true /* trackball */);
1414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else {
1415146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectionMode = SELECTION_LONGPRESS;
1416146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
1417146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    performLongClick();
1418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                break;
1420ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//            case KeyEvent.KEYCODE_BACK:
1421ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                if (event.isTracking() && !event.isCanceled()) {
1422ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    mPopup.dismiss();
1423ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    mContext.finish();
1424ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    return true;
1425ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                }
1426ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                break;
1427146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return super.onKeyUp(keyCode, event);
1429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1430146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onKeyDown(int keyCode, KeyEvent event) {
1433146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionMode == SELECTION_HIDDEN) {
1434146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
1435146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_UP
1436146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
1437146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Display the selection box but don't move or select it
1438146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // on this key press.
1439146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionMode = SELECTION_SELECTED;
1440146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1441146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
1442146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
1443146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Display the selection box but don't select it
1444146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // on this key press.
1445146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionMode = SELECTION_PRESSED;
1446146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1447146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
1448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1449146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1450146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1451146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_SELECTED;
1452146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = false;
1453146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean redraw;
1454146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectionDay = mSelectionDay;
1455146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1456146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (keyCode) {
145746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DEL:
145846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // Delete the selected event, if any
145946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                Event selectedEvent = mSelectedEvent;
146046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (selectedEvent == null) {
146146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return false;
146246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
146346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mPopup.dismiss();
146446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mLastPopupEventID = INVALID_EVENT_ID;
1465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
146646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long begin = selectedEvent.startMillis;
146746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long end = selectedEvent.endMillis;
146846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long id = selectedEvent.id;
146946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mDeleteEventHelper.delete(begin, end, id, -1);
1470b10f5762d0e50bac6ba31ec332e7513869b55bf3Michael Chan                return true;
147146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_ENTER:
147246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                switchViews(true /* trackball or keyboard */);
147346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return true;
147446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_BACK:
147546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (event.getRepeatCount() == 0) {
147646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    event.startTracking();
147746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
147846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
147946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onKeyDown(keyCode, event);
148046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_LEFT:
148146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
148246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextLeft;
148346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
148446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
148546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
148646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    selectionDay -= 1;
148746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
148846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
148946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1490146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
149146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_RIGHT:
149246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
149346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextRight;
149446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
149546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
149646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
149746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    selectionDay += 1;
149846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
149946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
150046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1501146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
150246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_UP:
150346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
150446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextUp;
1505146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
150646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
150746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
15082fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (!mSelectionAllday) {
150946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectionHour -= 1;
151046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        adjustHourSelection();
151146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectedEvents.clear();
151246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mComputeSelectedEvents = true;
151346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
151446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
151546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
151646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1517146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
151846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_DOWN:
151946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
152046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextDown;
1521146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
152246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
152346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
15242fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (mSelectionAllday) {
15252fda24534978f55711526dbde747aa50d68dec40RoboErik                        mSelectionAllday = false;
152646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    } else {
152746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectionHour++;
152846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        adjustHourSelection();
152946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectedEvents.clear();
153046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mComputeSelectedEvents = true;
153146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
153246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
153346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
153446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1535146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
153646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            default:
153746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onKeyDown(keyCode, event);
1538146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1539146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1540146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((selectionDay < mFirstJulianDay) || (selectionDay > mLastJulianDay)) {
1541d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView view = (DayView) mViewSwitcher.getNextView();
1542146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Time date = view.mBaseDate;
1543146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.set(mBaseDate);
1544146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (selectionDay < mFirstJulianDay) {
1545146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                date.monthDay -= mNumDays;
1546146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1547146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                date.monthDay += mNumDays;
1548146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1549146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.normalize(true /* ignore isDst */);
1550146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            view.mSelectionDay = selectionDay;
1551146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1552146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            initView(view);
155383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
155483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            Time end = new Time(date);
155583b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            end.monthDay += mNumDays - 1;
15563458a1775920dda53099b7f1913cd4ec4c8b4095Michael Chan            mController.sendEvent(this, EventType.GO_TO, date, end, -1, ViewType.CURRENT);
1557146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
1558146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
155904b288958078f8b29870dcbb2239df448c559610Erik        if (mSelectionDay != selectionDay) {
156004b288958078f8b29870dcbb2239df448c559610Erik            Time date = new Time(mBaseDate);
156104b288958078f8b29870dcbb2239df448c559610Erik            date.setJulianDay(selectionDay);
156204b288958078f8b29870dcbb2239df448c559610Erik            date.hour = mSelectionHour;
156304b288958078f8b29870dcbb2239df448c559610Erik            mController.sendEvent(this, EventType.GO_TO, date, date, -1, ViewType.CURRENT);
156404b288958078f8b29870dcbb2239df448c559610Erik        }
1565146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionDay = selectionDay;
1566146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
1567146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = true;
15688307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        mUpdateToast = true;
1569146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1570146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (redraw) {
1571146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
1572146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
1573146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1574146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1575146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return super.onKeyDown(keyCode, event);
1576146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1577146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
15789ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
15799ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    @Override
15809ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    public boolean onHoverEvent(MotionEvent event) {
15819ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG) {
15829ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            int action = event.getAction();
15839ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            switch (action) {
15849ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                case MotionEvent.ACTION_HOVER_ENTER:
15859ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_HOVER_ENTER");
15869ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    break;
15879ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                case MotionEvent.ACTION_HOVER_MOVE:
15889ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_HOVER_MOVE");
15899ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    break;
15909ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                case MotionEvent.ACTION_HOVER_EXIT:
15919ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_HOVER_EXIT");
15929ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    break;
15939ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                default:
15949ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "Unknown hover event action. " + event);
15959ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
15969ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        }
15979ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
15989ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        // Mouse also generates hover events
15999ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        // Send accessibility events if accessibility and exploration are on.
16009ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (!mTouchExplorationEnabled) {
16019ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            return super.onHoverEvent(event);
1602259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
16039ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) {
16049ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            setSelectionFromPosition((int) event.getX(), (int) event.getY());
16059ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            invalidate();
16069ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        }
16079ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        return true;
1608259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1609259b8f9a21fffd3858218581e9daaf689b6b888fErik
16109ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private boolean isTouchExplorationEnabled() {
1611aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        return mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled();
16129ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    }
1613259b8f9a21fffd3858218581e9daaf689b6b888fErik
1614aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private void sendAccessibilityEventAsNeeded(boolean speakEvents) {
1615aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        if (!mIsAccessibilityEnabled) {
1616be569a176153e0b4b4606a022ab30cf2f5bf719dRoboErik            return;
1617be569a176153e0b4b4606a022ab30cf2f5bf719dRoboErik        }
16189ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        boolean dayChanged = mLastSelectionDay != mSelectionDay;
16199ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        boolean hourChanged = mLastSelectionHour != mSelectionHour;
16209ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (dayChanged || hourChanged || mLastSelectedEvent != mSelectedEvent) {
16219ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            mLastSelectionDay = mSelectionDay;
16229ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            mLastSelectionHour = mSelectionHour;
16239ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            mLastSelectedEvent = mSelectedEvent;
16249ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
16259ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            StringBuilder b = new StringBuilder();
16269ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
16279ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            // Announce only the changes i.e. day or hour or both
16289ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (dayChanged) {
16299ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                b.append(getSelectedTime().format("%A "));
16309ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
16319ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (hourChanged) {
16329ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                b.append(getSelectedTime().format(mIs24HourFormat ? "%k" : "%l%p"));
16339ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
1634aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            if (dayChanged || hourChanged) {
1635aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                b.append(PERIOD_SPACE);
1636aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            }
16379ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
1638aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            if (speakEvents) {
1639aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                if (mEventCountTemplate == null) {
1640aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    mEventCountTemplate = mContext.getString(R.string.template_announce_item_index);
1641aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                }
1642aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan
1643aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                // Read out the relevant event(s)
1644aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                int numEvents = mSelectedEvents.size();
1645aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                if (numEvents > 0) {
1646aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    if (mSelectedEvent == null) {
1647aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        // Read out all the events
1648aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        int i = 1;
1649aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        for (Event calEvent : mSelectedEvents) {
1650aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            if (numEvents > 1) {
1651aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                // Read out x of numEvents if there are more than one event
1652aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                mStringBuilder.setLength(0);
1653aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                b.append(mFormatter.format(mEventCountTemplate, i++, numEvents));
1654aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                b.append(" ");
1655aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            }
1656aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            appendEventAccessibilityString(b, calEvent);
1657aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        }
1658aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    } else {
16599ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                        if (numEvents > 1) {
16609ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            // Read out x of numEvents if there are more than one event
16619ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            mStringBuilder.setLength(0);
1662aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            b.append(mFormatter.format(mEventCountTemplate, mSelectedEvents
1663aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                    .indexOf(mSelectedEvent) + 1, numEvents));
16649ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            b.append(" ");
16659ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                        }
1666aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        appendEventAccessibilityString(b, mSelectedEvent);
1667259b8f9a21fffd3858218581e9daaf689b6b888fErik                    }
16689ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                } else {
1669aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    b.append(mCreateNewEventString);
1670259b8f9a21fffd3858218581e9daaf689b6b888fErik                }
16719ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
16729ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
1673aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            if (dayChanged || hourChanged || speakEvents) {
1674aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                AccessibilityEvent event = AccessibilityEvent
1675aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
1676aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                CharSequence msg = b.toString();
1677aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                event.getText().add(msg);
1678aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                event.setAddedCount(msg.length());
1679aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                sendAccessibilityEventUnchecked(event);
1680aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            }
1681259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1682259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1683259b8f9a21fffd3858218581e9daaf689b6b888fErik
1684259b8f9a21fffd3858218581e9daaf689b6b888fErik    /**
1685259b8f9a21fffd3858218581e9daaf689b6b888fErik     * @param b
1686259b8f9a21fffd3858218581e9daaf689b6b888fErik     * @param calEvent
1687259b8f9a21fffd3858218581e9daaf689b6b888fErik     */
1688259b8f9a21fffd3858218581e9daaf689b6b888fErik    private void appendEventAccessibilityString(StringBuilder b, Event calEvent) {
1689259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(calEvent.getTitleAndLocation());
1690259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(PERIOD_SPACE);
1691259b8f9a21fffd3858218581e9daaf689b6b888fErik        String when;
1692259b8f9a21fffd3858218581e9daaf689b6b888fErik        int flags = DateUtils.FORMAT_SHOW_DATE;
1693259b8f9a21fffd3858218581e9daaf689b6b888fErik        if (calEvent.allDay) {
1694259b8f9a21fffd3858218581e9daaf689b6b888fErik            flags |= DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY;
1695259b8f9a21fffd3858218581e9daaf689b6b888fErik        } else {
1696259b8f9a21fffd3858218581e9daaf689b6b888fErik            flags |= DateUtils.FORMAT_SHOW_TIME;
1697259b8f9a21fffd3858218581e9daaf689b6b888fErik            if (DateFormat.is24HourFormat(mContext)) {
1698259b8f9a21fffd3858218581e9daaf689b6b888fErik                flags |= DateUtils.FORMAT_24HOUR;
1699259b8f9a21fffd3858218581e9daaf689b6b888fErik            }
1700259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1701259b8f9a21fffd3858218581e9daaf689b6b888fErik        when = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags);
1702259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(when);
1703259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(PERIOD_SPACE);
1704259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1705259b8f9a21fffd3858218581e9daaf689b6b888fErik
17066d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private class GotoBroadcaster implements Animation.AnimationListener {
17076d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final int mCounter;
17086d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final Time mStart;
17096d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final Time mEnd;
17106d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17116d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public GotoBroadcaster(Time start, Time end) {
17126d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mCounter = ++sCounter;
17136d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mStart = start;
17146d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mEnd = end;
17156d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
17166d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17176d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
17186d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationEnd(Animation animation) {
1719f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            DayView view = (DayView) mViewSwitcher.getCurrentView();
1720f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view.mViewStartX = 0;
1721f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view = (DayView) mViewSwitcher.getNextView();
1722f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view.mViewStartX = 0;
1723f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan
17246d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            if (mCounter == sCounter) {
1725f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan                mController.sendEvent(this, EventType.GO_TO, mStart, mEnd, null, -1,
1726f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan                        ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null);
17276d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            }
17286d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
17296d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17306d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
17316d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationRepeat(Animation animation) {
17326d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
17336d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17346d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
17356d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationStart(Animation animation) {
17366d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
17376d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    }
17386d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17392c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private View switchViews(boolean forward, float xOffSet, float width, float velocity) {
174095d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        mAnimationDistance = width - xOffSet;
174195d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        if (DEBUG) {
174295d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            Log.d(TAG, "switchViews(" + forward + ") O:" + xOffSet + " Dist:" + mAnimationDistance);
174395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        }
174495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
1745ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float progress = Math.abs(xOffSet) / width;
1746ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (progress > 1.0f) {
1747ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            progress = 1.0f;
1748ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
1749ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1750ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float inFromXValue, inToXValue;
1751ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float outFromXValue, outToXValue;
1752ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (forward) {
1753ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inFromXValue = 1.0f - progress;
1754ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inToXValue = 0.0f;
1755ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outFromXValue = -progress;
1756ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outToXValue = -1.0f;
1757ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        } else {
1758ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inFromXValue = progress - 1.0f;
1759ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inToXValue = 0.0f;
1760ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outFromXValue = progress;
1761ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outToXValue = 1.0f;
1762ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
1763ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
17646d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        final Time start = new Time(mBaseDate.timezone);
17656d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        start.set(mController.getTime());
17666d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (forward) {
17676d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            start.monthDay += mNumDays;
17686d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        } else {
17696d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            start.monthDay -= mNumDays;
17706d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
1771f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        mController.setTime(start.normalize(true));
1772f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan
1773f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        Time newSelected = start;
17746d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17756d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (mNumDays == 7) {
1776f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan            newSelected = new Time(start);
17776d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            adjustToBeginningOfWeek(start);
17786d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
17796d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17806d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        final Time end = new Time(start);
17816d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        end.monthDay += mNumDays - 1;
17826d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
1783ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        // We have to allocate these animation objects each time we switch views
1784ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        // because that is the only way to set the animation parameters.
1785ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        TranslateAnimation inAnimation = new TranslateAnimation(
1786ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, inFromXValue,
1787ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, inToXValue,
1788ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f,
1789ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f);
1790ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1791ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        TranslateAnimation outAnimation = new TranslateAnimation(
1792ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, outFromXValue,
1793ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, outToXValue,
1794ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f,
1795ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f);
1796ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
17972c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        long duration = calculateDuration(width - Math.abs(xOffSet), width, velocity);
1798ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        inAnimation.setDuration(duration);
17992c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        inAnimation.setInterpolator(mHScrollInterpolator);
18002c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        outAnimation.setInterpolator(mHScrollInterpolator);
1801ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        outAnimation.setDuration(duration);
18026d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        outAnimation.setAnimationListener(new GotoBroadcaster(start, end));
1803ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.setInAnimation(inAnimation);
1804ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.setOutAnimation(outAnimation);
1805ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1806d190418220467c02327dd9b3ae1eae238889720bMichael Chan        DayView view = (DayView) mViewSwitcher.getCurrentView();
1807ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.cleanup();
1808ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.showNext();
1809d190418220467c02327dd9b3ae1eae238889720bMichael Chan        view = (DayView) mViewSwitcher.getCurrentView();
18105b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        view.setSelected(newSelected, true, false);
1811ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.requestFocus();
1812ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.reloadEvents();
1813eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        view.updateTitle();
1814e98dd73c67205b01f5348271d9a7a630003ecf52David Brown
1815ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        return view;
1816ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    }
1817ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1818146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // This is called after scrolling stops to move the selected hour
1819146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // to the visible part of the screen.
1820146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void resetSelectedHour() {
1821146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < mFirstHour + 1) {
1822146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = mFirstHour + 1;
1823146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = null;
1824146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvents.clear();
1825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mComputeSelectedEvents = true;
1826146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (mSelectionHour > mFirstHour + mNumHours - 3) {
1827146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = mFirstHour + mNumHours - 3;
1828146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = null;
1829146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvents.clear();
1830146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mComputeSelectedEvents = true;
1831146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1832146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1833146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1834146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void initFirstHour() {
1835d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mFirstHour = mSelectionHour - mNumHours / 5;
1836146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour < 0) {
1837146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHour = 0;
1838146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (mFirstHour + mNumHours > 24) {
1839146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHour = 24 - mNumHours;
1840146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1841146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1842146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1843146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1844146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Recomputes the first full hour that is visible on screen after the
1845146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * screen is scrolled.
1846146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1847146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeFirstHour() {
1848146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Compute the first full hour that is visible on screen
1849146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP);
1850146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY;
1851146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1852146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1853146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void adjustHourSelection() {
1854146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < 0) {
1855146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = 0;
18562fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > 0) {
1857146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
18582fda24534978f55711526dbde747aa50d68dec40RoboErik                mSelectionAllday = true;
1859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1860146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1862146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour > 23) {
1863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = 23;
1864146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1865146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1866146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the selected hour is at least 2 time slots from the top and
1867146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // bottom of the screen, then don't scroll the view.
1868146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < mFirstHour + 1) {
1869146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there are all-days events for the selected day but there
1870146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // are no more normal events earlier in the day, then jump to
1871146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // the all-day event area.
1872146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Exception 1: allow the user to scroll to 8am with the trackball
1873146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // before jumping to the all-day event area.
1874146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Exception 2: if 12am is on screen, then allow the user to select
1875146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // 12am before going up to the all-day event area.
1876146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int daynum = mSelectionDay - mFirstJulianDay;
18772fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > 0 && mEarliestStartHour[daynum] > mSelectionHour
1878146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    && mFirstHour > 0 && mFirstHour < 8) {
1879146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
18802fda24534978f55711526dbde747aa50d68dec40RoboErik                mSelectionAllday = true;
1881146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionHour = mFirstHour + 1;
1882146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1883146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1884146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1885146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mFirstHour > 0) {
1886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mFirstHour -= 1;
1887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY -= (mCellHeight + HOUR_GAP);
1888146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mViewStartY < 0) {
1889146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mViewStartY = 0;
1890146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1891146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1892146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1893146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1894146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1895146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour > mFirstHour + mNumHours - 3) {
1896146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mFirstHour < 24 - mNumHours) {
1897146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mFirstHour += 1;
1898146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY += (mCellHeight + HOUR_GAP);
1899c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                if (mViewStartY > mMaxViewStartY) {
1900c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                    mViewStartY = mMaxViewStartY;
1901146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1902146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1903146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) {
1904c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                mViewStartY = mMaxViewStartY;
1905146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1906146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1907146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1908146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1909146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    void clearCachedEvents() {
1910146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastReloadMillis = 0;
1911146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1912146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1913146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Runnable mCancelCallback = new Runnable() {
1914146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
1915146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            clearCachedEvents();
1916146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
1918146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
191946b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa    /* package */ void reloadEvents() {
1920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Protect against this being called before this view has been
1921146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // initialized.
1922ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        if (mContext == null) {
1923ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//            return;
1924ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        }
1925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1926731f1cb808b7586b93615b408ab2636081fab0dcErik        // Make sure our time zones are up to date
1927731f1cb808b7586b93615b408ab2636081fab0dcErik        mTZUpdater.run();
1928731f1cb808b7586b93615b408ab2636081fab0dcErik
1929146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = null;
1930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevSelectedEvent = null;
1931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
1932146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1933146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // The start date is the beginning of the week at 12am
1934731f1cb808b7586b93615b408ab2636081fab0dcErik        Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater));
1935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.set(mBaseDate);
1936146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.hour = 0;
1937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.minute = 0;
1938146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.second = 0;
1939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = weekStart.normalize(true /* ignore isDst */);
1940146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Avoid reloading events unnecessarily.
1942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (millis == mLastReloadMillis) {
1943146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
1944146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1945146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastReloadMillis = millis;
1946146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1947146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // load events in the background
1948ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        mContext.startProgressSpinner();
1949146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final ArrayList<Event> events = new ArrayList<Event>();
1950b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik        mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, new Runnable() {
1951146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            public void run() {
1952146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mEvents = events;
195308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mAllDayEvents == null) {
195408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayEvents = new ArrayList<Event>();
195508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
195608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayEvents.clear();
195708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
195808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
195908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                // Create a shorter array for all day events
196046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                for (Event e : events) {
196168655b5af2b7e5f872a103858ec58a19042123ceRoboErik                    if (e.drawAsAllday()) {
196208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                        mAllDayEvents.add(e);
196308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    }
196408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
196508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
196608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                // New events, new layouts
196708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mLayouts == null || mLayouts.length < events.size()) {
196808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mLayouts = new StaticLayout[events.size()];
196908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
197008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    Arrays.fill(mLayouts, null);
19713e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh                }
197208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
197308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) {
197408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayLayouts = new StaticLayout[events.size()];
197508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
197608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    Arrays.fill(mAllDayLayouts, null);
197708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
197808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
19792fda24534978f55711526dbde747aa50d68dec40RoboErik                computeEventRelations();
19802fda24534978f55711526dbde747aa50d68dec40RoboErik
1981146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mRemeasure = true;
1982146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mComputeSelectedEvents = true;
1983146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                recalc();
1984ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                mContext.stopProgressSpinner();
1985146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1986146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1987146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }, mCancelCallback);
1988146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1989146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
19902fda24534978f55711526dbde747aa50d68dec40RoboErik    private void computeEventRelations() {
19912fda24534978f55711526dbde747aa50d68dec40RoboErik        // Compute the layout relation between each event before measuring cell
19922fda24534978f55711526dbde747aa50d68dec40RoboErik        // width, as the cell width should be adjusted along with the relation.
19932fda24534978f55711526dbde747aa50d68dec40RoboErik        //
19942fda24534978f55711526dbde747aa50d68dec40RoboErik        // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm)
19952fda24534978f55711526dbde747aa50d68dec40RoboErik        // We should mark them as "overwapped". Though they are not overwapped logically, but
19962fda24534978f55711526dbde747aa50d68dec40RoboErik        // minimum cell height implicitly expands the cell height of A and it should look like
19972fda24534978f55711526dbde747aa50d68dec40RoboErik        // (1:00pm - 1:15pm) after the cell height adjustment.
19982fda24534978f55711526dbde747aa50d68dec40RoboErik
19992fda24534978f55711526dbde747aa50d68dec40RoboErik        // Compute the space needed for the all-day events, if any.
20002fda24534978f55711526dbde747aa50d68dec40RoboErik        // Make a pass over all the events, and keep track of the maximum
20012fda24534978f55711526dbde747aa50d68dec40RoboErik        // number of all-day events in any one day.  Also, keep track of
20022fda24534978f55711526dbde747aa50d68dec40RoboErik        // the earliest event in each day.
20032fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxAllDayEvents = 0;
20042fda24534978f55711526dbde747aa50d68dec40RoboErik        final ArrayList<Event> events = mEvents;
20052fda24534978f55711526dbde747aa50d68dec40RoboErik        final int len = events.size();
20062fda24534978f55711526dbde747aa50d68dec40RoboErik        // Num of all-day-events on each day.
20072fda24534978f55711526dbde747aa50d68dec40RoboErik        final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1];
20082fda24534978f55711526dbde747aa50d68dec40RoboErik        Arrays.fill(eventsCount, 0);
20092fda24534978f55711526dbde747aa50d68dec40RoboErik        for (int ii = 0; ii < len; ii++) {
20102fda24534978f55711526dbde747aa50d68dec40RoboErik            Event event = events.get(ii);
20112fda24534978f55711526dbde747aa50d68dec40RoboErik            if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) {
20122fda24534978f55711526dbde747aa50d68dec40RoboErik                continue;
20132fda24534978f55711526dbde747aa50d68dec40RoboErik            }
20142fda24534978f55711526dbde747aa50d68dec40RoboErik            if (event.drawAsAllday()) {
20152fda24534978f55711526dbde747aa50d68dec40RoboErik                // Count all the events being drawn as allDay events
20162fda24534978f55711526dbde747aa50d68dec40RoboErik                final int firstDay = Math.max(event.startDay, mFirstJulianDay);
20172fda24534978f55711526dbde747aa50d68dec40RoboErik                final int lastDay = Math.min(event.endDay, mLastJulianDay);
20182fda24534978f55711526dbde747aa50d68dec40RoboErik                for (int day = firstDay; day <= lastDay; day++) {
20192fda24534978f55711526dbde747aa50d68dec40RoboErik                    final int count = ++eventsCount[day - mFirstJulianDay];
20202fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (maxAllDayEvents < count) {
20212fda24534978f55711526dbde747aa50d68dec40RoboErik                        maxAllDayEvents = count;
20222fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
20232fda24534978f55711526dbde747aa50d68dec40RoboErik                }
20242fda24534978f55711526dbde747aa50d68dec40RoboErik
20252fda24534978f55711526dbde747aa50d68dec40RoboErik                int daynum = event.startDay - mFirstJulianDay;
20262fda24534978f55711526dbde747aa50d68dec40RoboErik                int durationDays = event.endDay - event.startDay + 1;
20272fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum < 0) {
20282fda24534978f55711526dbde747aa50d68dec40RoboErik                    durationDays += daynum;
20292fda24534978f55711526dbde747aa50d68dec40RoboErik                    daynum = 0;
20302fda24534978f55711526dbde747aa50d68dec40RoboErik                }
20312fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum + durationDays > mNumDays) {
20322fda24534978f55711526dbde747aa50d68dec40RoboErik                    durationDays = mNumDays - daynum;
20332fda24534978f55711526dbde747aa50d68dec40RoboErik                }
20342fda24534978f55711526dbde747aa50d68dec40RoboErik                for (int day = daynum; durationDays > 0; day++, durationDays--) {
20352fda24534978f55711526dbde747aa50d68dec40RoboErik                    mHasAllDayEvent[day] = true;
20362fda24534978f55711526dbde747aa50d68dec40RoboErik                }
20372fda24534978f55711526dbde747aa50d68dec40RoboErik            } else {
20382fda24534978f55711526dbde747aa50d68dec40RoboErik                int daynum = event.startDay - mFirstJulianDay;
20392fda24534978f55711526dbde747aa50d68dec40RoboErik                int hour = event.startTime / 60;
20402fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum >= 0 && hour < mEarliestStartHour[daynum]) {
20412fda24534978f55711526dbde747aa50d68dec40RoboErik                    mEarliestStartHour[daynum] = hour;
20422fda24534978f55711526dbde747aa50d68dec40RoboErik                }
20432fda24534978f55711526dbde747aa50d68dec40RoboErik
20442fda24534978f55711526dbde747aa50d68dec40RoboErik                // Also check the end hour in case the event spans more than
20452fda24534978f55711526dbde747aa50d68dec40RoboErik                // one day.
20462fda24534978f55711526dbde747aa50d68dec40RoboErik                daynum = event.endDay - mFirstJulianDay;
20472fda24534978f55711526dbde747aa50d68dec40RoboErik                hour = event.endTime / 60;
20482fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) {
20492fda24534978f55711526dbde747aa50d68dec40RoboErik                    mEarliestStartHour[daynum] = hour;
20502fda24534978f55711526dbde747aa50d68dec40RoboErik                }
20512fda24534978f55711526dbde747aa50d68dec40RoboErik            }
20522fda24534978f55711526dbde747aa50d68dec40RoboErik        }
20532fda24534978f55711526dbde747aa50d68dec40RoboErik        mMaxAlldayEvents = maxAllDayEvents;
20542fda24534978f55711526dbde747aa50d68dec40RoboErik        initAllDayHeights();
20552fda24534978f55711526dbde747aa50d68dec40RoboErik    }
20562fda24534978f55711526dbde747aa50d68dec40RoboErik
2057146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
2058146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected void onDraw(Canvas canvas) {
2059146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mRemeasure) {
2060146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            remeasure(getWidth(), getHeight());
2061146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mRemeasure = false;
2062146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2063c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.save();
2064146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
20652fda24534978f55711526dbde747aa50d68dec40RoboErik        float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight;
2066c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // offset canvas by the current drag and header position
2067c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.translate(-mViewStartX, yTranslate);
2068c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // clip to everything below the allDay area
2069c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        Rect dest = mDestRect;
2070c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.top = (int) (mFirstCell - yTranslate);
2071c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.bottom = (int) (mViewHeight - yTranslate);
2072c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.left = 0;
2073c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.right = mViewWidth;
2074c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.save();
2075c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.clipRect(dest);
2076c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // Draw the movable part of the view
2077c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        doDraw(canvas);
2078c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // restore to having no clip
2079c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.restore();
2080146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2081146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
2082c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            float xTranslate;
2083146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mViewStartX > 0) {
2084c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                xTranslate = mViewWidth;
2085146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
2086c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                xTranslate = -mViewWidth;
2087146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2088c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // Move the canvas around to prep it for the next view
2089c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // specifically, shift it by a screen and undo the
2090c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // yTranslation which will be redone in the nextView's onDraw().
2091c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(xTranslate, -yTranslate);
2092d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView nextView = (DayView) mViewSwitcher.getNextView();
2093146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2094146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Prevent infinite recursive calls to onDraw().
2095146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE;
2096146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2097146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            nextView.onDraw(canvas);
2098c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // Move it back for this view
2099c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(-xTranslate, 0);
2100c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        } else {
2101c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // If we drew another view we already translated it back
2102c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // If we didn't draw another view we should be at the edge of the
2103c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // screen
2104c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(mViewStartX, -yTranslate);
2105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the fixed areas (that don't scroll) directly to the canvas.
2108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawAfterScroll(canvas);
21098307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        if (mComputeSelectedEvents && mUpdateToast) {
21108307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik            updateEventDetails();
21118307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik            mUpdateToast = false;
21128307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        }
2113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = false;
2114433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
2115433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // Draw overscroll glow
2116433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        if (!mEdgeEffectTop.isFinished()) {
2117433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (DAY_HEADER_HEIGHT != 0) {
2118433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                canvas.translate(0, DAY_HEADER_HEIGHT);
2119433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2120433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mEdgeEffectTop.draw(canvas)) {
2121433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                invalidate();
2122433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2123433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (DAY_HEADER_HEIGHT != 0) {
2124433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                canvas.translate(0, -DAY_HEADER_HEIGHT);
2125433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2126433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
2127433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        if (!mEdgeEffectBottom.isFinished()) {
2128433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            canvas.rotate(180, mViewWidth/2, mViewHeight/2);
2129433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mEdgeEffectBottom.draw(canvas)) {
2130433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                invalidate();
2131433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2132433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
2133146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        canvas.restore();
2134146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2135146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawAfterScroll(Canvas canvas) {
2137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mPaint;
2138146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect r = mRect;
2139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2140e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        drawAllDayHighlights(r, canvas, p);
21412fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents != 0) {
21421ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p);
21438f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            drawUpperLeftCorner(r, canvas, p);
2144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2146c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        drawScrollLine(r, canvas, p);
21473eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        drawDayHeaderLoop(r, canvas, p);
2148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the AM and PM indicators if we're in 12 hour mode
2150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!mIs24HourFormat) {
2151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            drawAmPm(canvas, p);
2152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
215546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    // This isn't really the upper-left corner. It's the square area just
2156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // below the upper-left corner, above the hours and to the left of the
2157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // all-day area.
21588f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) {
21598f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        setupHourTextPaint(p);
21602fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
2161cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            // Draw the allDay expand/collapse icon
2162cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            if (mUseExpandIcon) {
2163cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mExpandAlldayDrawable.setBounds(mExpandAllDayRect);
2164cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mExpandAlldayDrawable.draw(canvas);
2165cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            } else {
2166cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mCollapseAlldayDrawable.setBounds(mExpandAllDayRect);
2167cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mCollapseAlldayDrawable.draw(canvas);
2168cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            }
21692fda24534978f55711526dbde747aa50d68dec40RoboErik        }
21708f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    }
2171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2172c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik    private void drawScrollLine(Rect r, Canvas canvas, Paint p) {
2173e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        final int right = computeDayLeftPosition(mNumDays + 1);
21748f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        final int y = mFirstCell - 1;
21758f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
2176c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        p.setAntiAlias(false);
21778f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setStyle(Style.FILL);
21788f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
21798f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setColor(mCalendarGridLineInnerHorizontalColor);
21808f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
21818f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p);
2182c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        p.setAntiAlias(true);
2183c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik    }
2184c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik
2185e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    // Computes the x position for the left side of the given day
2186e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private int computeDayLeftPosition(int day) {
2187e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int effectiveWidth = mViewWidth - mHoursWidth;
2188e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        return day * effectiveWidth / mNumDays + mHoursWidth;
2189e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2190e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2191e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawAllDayHighlights(Rect r, Canvas canvas, Paint p) {
2192e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // First, color the labels area light gray
2193e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = 0;
2194e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = DAY_HEADER_HEIGHT;
2195e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = 0;
2196e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mViewWidth;
2197e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mBgColor);
2198e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStyle(Style.FILL);
2199e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.drawRect(r, p);
2200e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // and the area that says All day
2201e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = DAY_HEADER_HEIGHT;
2202e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = mFirstCell - 1;
2203e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = 0;
2204e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mHoursWidth;
2205e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.drawRect(r, p);
220621bd42039c92232647f54461cd20708224cc7eb6Michael Chan
220721bd42039c92232647f54461cd20708224cc7eb6Michael Chan        int startIndex = -1;
2208e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int todayIndex = mTodayJulianDay - mFirstJulianDay;
220921bd42039c92232647f54461cd20708224cc7eb6Michael Chan        if (todayIndex < 0) {
221021bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Future
221121bd42039c92232647f54461cd20708224cc7eb6Michael Chan            startIndex = 0;
221221bd42039c92232647f54461cd20708224cc7eb6Michael Chan        } else if (todayIndex >= 1 && todayIndex + 1 < mNumDays) {
221321bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Multiday - tomorrow is visible.
221421bd42039c92232647f54461cd20708224cc7eb6Michael Chan            startIndex = todayIndex + 1;
221521bd42039c92232647f54461cd20708224cc7eb6Michael Chan        }
221621bd42039c92232647f54461cd20708224cc7eb6Michael Chan
221721bd42039c92232647f54461cd20708224cc7eb6Michael Chan        if (startIndex >= 0) {
221821bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Draw the future highlight
2219e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.top = 0;
2220e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.bottom = mFirstCell - 1;
222121bd42039c92232647f54461cd20708224cc7eb6Michael Chan            r.left = computeDayLeftPosition(startIndex) + 1;
222221bd42039c92232647f54461cd20708224cc7eb6Michael Chan            r.right = computeDayLeftPosition(mNumDays + 1);
222321bd42039c92232647f54461cd20708224cc7eb6Michael Chan            p.setColor(mFutureBgColor);
2224e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setStyle(Style.FILL);
2225e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(r, p);
2226e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
222721bd42039c92232647f54461cd20708224cc7eb6Michael Chan
2228b6fb51224b578b26e503fddae2799d03a2aaad47RoboErik        if (mSelectionAllday && mSelectionMode != SELECTION_HIDDEN) {
2229e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            // Draw the selection highlight on the selected all-day area
2230e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.top = DAY_HEADER_HEIGHT + 1;
2231e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.bottom = mRect.top + mAlldayHeight + ALLDAY_TOP_MARGIN - 2;
2232e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            int daynum = mSelectionDay - mFirstJulianDay;
2233e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.left = computeDayLeftPosition(daynum) + 1;
2234e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.right = computeDayLeftPosition(daynum + 1);
2235e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setColor(mCalendarGridAreaSelected);
2236e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(mRect, p);
2237e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2238e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2239e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2240146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) {
2241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the horizontal day background banner
22423eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // p.setColor(mCalendarDateBannerBackground);
22433eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.top = 0;
22443eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.bottom = DAY_HEADER_HEIGHT;
22453eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.left = 0;
22463eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP);
22473eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // canvas.drawRect(r, p);
22483eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        //
2249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Fill the extra space on the right side with the default background
22503eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.left = r.right;
22513eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.right = mViewWidth;
22523eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // p.setColor(mCalendarGridAreaBackground);
22533eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // canvas.drawRect(r, p);
2254e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) {
2255e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            return;
2256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2257146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
22583eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTypeface(mBold);
22593eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextAlign(Paint.Align.RIGHT);
2260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cell = mFirstJulianDay;
2261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] dayNames;
2263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mDateStrWidth < mCellWidth) {
2264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dayNames = mDayStrs;
2265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dayNames = mDayStrs2Letter;
2267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2269d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        p.setAntiAlias(true);
2270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++, cell++) {
22711f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan            int dayOfWeek = day + mFirstVisibleDayOfWeek;
22721f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan            if (dayOfWeek >= 14) {
22731f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan                dayOfWeek -= 14;
2274487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            }
2275487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
22763eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            int color = mCalendarDateBannerTextColor;
2277071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa            if (mNumDays == 1) {
2278071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                if (dayOfWeek == Time.SATURDAY) {
2279071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_saturdayColor;
2280071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                } else if (dayOfWeek == Time.SUNDAY) {
2281071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_sundayColor;
2282071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                }
2283071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa            } else {
2284071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                final int column = day % 7;
2285071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                if (Utils.isSaturday(column, mFirstDayOfWeek)) {
2286071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_saturdayColor;
2287071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                } else if (Utils.isSunday(column, mFirstDayOfWeek)) {
2288071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_sundayColor;
2289071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                }
22903eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            }
22913eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
22923eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setColor(color);
2293e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p);
2294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
22953eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTypeface(null);
2296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2297146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawAmPm(Canvas canvas, Paint p) {
2299d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        p.setColor(mCalendarAmPmLabel);
2300e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(AMPM_TEXT_SIZE);
2301146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(mBold);
2302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
2303e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextAlign(Paint.Align.RIGHT);
2304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String text = mAmString;
2305146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour >= 12) {
2306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            text = mPmString;
2307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP;
23098f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
2310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour < 12 && mFirstHour + mNumHours > 12) {
2312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Also draw the "PM"
2313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            text = mPmString;
2314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP)
2315146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + 2 * mHoursTextHeight + HOUR_GAP;
23168f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
2317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2320e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawCurrentTimeLine(Rect r, final int day, final int top, Canvas canvas,
2321f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            Paint p) {
2322e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1;
2323763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1;
23243eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
2325763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.top = top - CURRENT_TIME_LINE_TOP_OFFSET;
23263eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight();
2327bd0e834aea317236d574f8738fe11a51fdc87968Erik
23283eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mCurrentTimeLine.setBounds(r);
23293eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mCurrentTimeLine.draw(canvas);
23305b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        if (mAnimateToday) {
23315b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mCurrentTimeAnimateLine.setBounds(r);
23325b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha);
23335b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mCurrentTimeAnimateLine.draw(canvas);
23345b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2335bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
2336bd0e834aea317236d574f8738fe11a51fdc87968Erik
2337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void doDraw(Canvas canvas) {
2338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mPaint;
2339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect r = mRect;
2340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2341e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        drawBgColors(r, canvas, p);
2342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawGridBackground(r, canvas, p);
2343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawHours(r, canvas, p);
2344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw each day
2346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cell = mFirstJulianDay;
2347e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
2348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++, cell++) {
234908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // TODO Wow, this needs cleanup. drawEvents loop through all the
235008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // events on every call.
2351e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            drawEvents(cell, day, HOUR_GAP, canvas, p);
235246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // If this is today
235346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (cell == mTodayJulianDay) {
23541ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
23551ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                        + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
23561ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
235746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // And the current time shows up somewhere on the screen
235846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) {
2359e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    drawCurrentTimeLine(r, day, lineY, canvas, p);
2360bd0e834aea317236d574f8738fe11a51fdc87968Erik                }
2361bd0e834aea317236d574f8738fe11a51fdc87968Erik            }
2362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2363e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
236595f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        drawSelectedRect(r, canvas, p);
236695f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    }
23673eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
236895f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private void drawSelectedRect(Rect r, Canvas canvas, Paint p) {
2369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw a highlight on the selected hour (if needed)
23702fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionMode != SELECTION_HIDDEN && !mSelectionAllday) {
23713937a47bc5a1afb9701e1d0565fae31f5800590dErik            int daynum = mSelectionDay - mFirstJulianDay;
2372ba902466b19b11d587468e354edc58e542707747Michael Chan            r.top = mSelectionHour * (mCellHeight + HOUR_GAP);
23731ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top + mCellHeight + HOUR_GAP;
2374d0c06c6c8815d027c595cb70b01478634a84c8e0RoboErik            r.left = computeDayLeftPosition(daynum) + 1;
2375d0c06c6c8815d027c595cb70b01478634a84c8e0RoboErik            r.right = computeDayLeftPosition(daynum + 1) + 1;
2376ba902466b19b11d587468e354edc58e542707747Michael Chan
2377ba902466b19b11d587468e354edc58e542707747Michael Chan            saveSelectionPosition(r.left, r.top, r.right, r.bottom);
23783937a47bc5a1afb9701e1d0565fae31f5800590dErik
237995f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            // Draw the highlight on the grid
23803937a47bc5a1afb9701e1d0565fae31f5800590dErik            p.setColor(mCalendarGridAreaSelected);
23813937a47bc5a1afb9701e1d0565fae31f5800590dErik            r.top += HOUR_GAP;
23823937a47bc5a1afb9701e1d0565fae31f5800590dErik            r.right -= DAY_GAP;
2383e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setAntiAlias(false);
23843937a47bc5a1afb9701e1d0565fae31f5800590dErik            canvas.drawRect(r, p);
238595f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan
2386ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            // Draw a "new event hint" on top of the highlight
2387ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            // For the week view, show a "+", for day view, show "+ New event"
2388ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            p.setColor(mNewEventHintColor);
2389ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            if (mNumDays > 1) {
2390ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setStrokeWidth(NEW_EVENT_WIDTH);
2391ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int width = r.right - r.left;
2392ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int midX = r.left + width / 2;
2393ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int midY = r.top + mCellHeight / 2;
2394ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int length = Math.min(mCellHeight, width) - NEW_EVENT_MARGIN * 2;
2395ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                length = Math.min(length, NEW_EVENT_MAX_LENGTH);
2396ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int verticalPadding = (mCellHeight - length) / 2;
2397ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int horizontalPadding = (width - length) / 2;
2398ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                canvas.drawLine(r.left + horizontalPadding, midY, r.right - horizontalPadding,
2399ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                        midY, p);
2400ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                canvas.drawLine(midX, r.top + verticalPadding, midX, r.bottom - verticalPadding, p);
2401ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            } else {
2402ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setStyle(Paint.Style.FILL);
2403ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setTextSize(NEW_EVENT_HINT_FONT_SIZE);
2404ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setTextAlign(Paint.Align.LEFT);
2405ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
2406ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                canvas.drawText(mNewEventHintString, r.left + EVENT_TEXT_LEFT_MARGIN,
2407ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                        r.top + Math.abs(p.getFontMetrics().ascent) + EVENT_TEXT_TOP_MARGIN , p);
2408ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            }
2409ba902466b19b11d587468e354edc58e542707747Michael Chan        }
241095f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    }
2411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
241295f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private void drawHours(Rect r, Canvas canvas, Paint p) {
24138f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        setupHourTextPaint(p);
2414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
24158f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        int y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN;
2416146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2417146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < 24; i++) {
2418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            String time = mHourStrs[i];
24198f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            canvas.drawText(time, HOURS_LEFT_MARGIN, y, p);
2420146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            y += mCellHeight + HOUR_GAP;
2421146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2422146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2423146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
24248f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private void setupHourTextPaint(Paint p) {
24258f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setColor(mCalendarHourLabelColor);
2426e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
2427f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan        p.setTypeface(Typeface.DEFAULT);
2428e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextAlign(Paint.Align.RIGHT);
24298f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setAntiAlias(true);
24308f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    }
24318f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
2432e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawDayHeader(String dayStr, int day, int cell, Canvas canvas, Paint p) {
2433487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int dateNum = mFirstVisibleDate + day;
2434e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int x;
2435146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (dateNum > mMonthLength) {
2436146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dateNum -= mMonthLength;
2437146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2438e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2439146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2440763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        int todayIndex = mTodayJulianDay - mFirstJulianDay;
24413eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw day of the month
24423eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        String dateNumStr = String.valueOf(dateNum);
24433eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        if (mNumDays > 1) {
24443eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN;
24453eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
24463eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the month
2447e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN;
2448e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setTextAlign(Align.RIGHT);
24493eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DATE_HEADER_FONT_SIZE);
2450763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik
2451763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik            p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT);
24523eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dateNumStr, x, y, p);
24533eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
24543eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the week
2455e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x -= p.measureText(" " + dateNumStr);
24563eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DAY_HEADER_FONT_SIZE);
2457f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(Typeface.DEFAULT);
24583eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dayStr, x, y, p);
2459146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2460e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            float y = ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN;
2461e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setTextAlign(Align.LEFT);
2462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2463f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan
24643eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the week
2465e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN;
24663eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DAY_HEADER_FONT_SIZE);
2467f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(Typeface.DEFAULT);
24683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dayStr, x, y, p);
2469d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan
24703eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the month
24713eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN;
24723eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DATE_HEADER_FONT_SIZE);
2473763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik            p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT);
24743eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dateNumStr, x, y, p);
2475d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        }
2476146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2478146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawGridBackground(Rect r, Canvas canvas, Paint p) {
2479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint.Style savedStyle = p.getStyle();
2480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2481e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        final float stopX = computeDayLeftPosition(mNumDays + 1);
2482000ba1109e2f4e527e9227fe341598ceeb3fbca6Michael Chan        float y = 0;
24833eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float deltaY = mCellHeight + HOUR_GAP;
248402041687c35441e8a21cb763f1fc3a26525292efDavid Brown        int linesIndex = 0;
24853eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float startY = 0;
24863eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP);
24871ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        float x = mHoursWidth;
2488146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
24893eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner horizontal grid lines
24903eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setColor(mCalendarGridLineInnerHorizontalColor);
24913eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
2492e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
24933eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        y = 0;
249402041687c35441e8a21cb763f1fc3a26525292efDavid Brown        linesIndex = 0;
24953eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        for (int hour = 0; hour <= 24; hour++) {
24968f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
249702041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = y;
249802041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopX;
249902041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = y;
25003eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            y += deltaY;
25013eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
250202041687c35441e8a21cb763f1fc3a26525292efDavid Brown        if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) {
250302041687c35441e8a21cb763f1fc3a26525292efDavid Brown            canvas.drawLines(mLines, 0, linesIndex, p);
250402041687c35441e8a21cb763f1fc3a26525292efDavid Brown            linesIndex = 0;
250502041687c35441e8a21cb763f1fc3a26525292efDavid Brown            p.setColor(mCalendarGridLineInnerVerticalColor);
250602041687c35441e8a21cb763f1fc3a26525292efDavid Brown        }
25073eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
25083eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner vertical grid lines
2509e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        for (int day = 0; day <= mNumDays; day++) {
2510e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day);
251102041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
251202041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = startY;
251302041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
251402041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopY;
25153eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
251602041687c35441e8a21cb763f1fc3a26525292efDavid Brown        canvas.drawLines(mLines, 0, linesIndex, p);
25173eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
2518146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Restore the saved style.
2519146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setStyle(savedStyle);
2520146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
2521146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2522146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2523e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
2524e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param r
2525e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param canvas
2526e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param p
2527e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
2528e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawBgColors(Rect r, Canvas canvas, Paint p) {
2529e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int todayIndex = mTodayJulianDay - mFirstJulianDay;
2530e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // Draw the hours background color
2531e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = mDestRect.top;
2532e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = mDestRect.bottom;
2533e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = 0;
2534e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mHoursWidth;
2535e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mBgColor);
2536e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStyle(Style.FILL);
2537e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
2538e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.drawRect(r, p);
253921bd42039c92232647f54461cd20708224cc7eb6Michael Chan
254021bd42039c92232647f54461cd20708224cc7eb6Michael Chan        // Draw background for grid area
2541e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mNumDays == 1 && todayIndex == 0) {
2542e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            // Draw a white background for the time later than current time
2543e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
2544e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
2545e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (lineY < mViewStartY + mViewHeight) {
2546e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                lineY = Math.max(lineY, mViewStartY);
2547e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.left = mHoursWidth;
2548e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.right = mViewWidth;
2549e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.top = lineY;
2550e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.bottom = mViewStartY + mViewHeight;
255121bd42039c92232647f54461cd20708224cc7eb6Michael Chan                p.setColor(mFutureBgColor);
2552e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                canvas.drawRect(r, p);
2553e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
2554e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        } else if (todayIndex >= 0 && todayIndex < mNumDays) {
255521bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Draw today with a white background for the time later than current time
255621bd42039c92232647f54461cd20708224cc7eb6Michael Chan            int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
255721bd42039c92232647f54461cd20708224cc7eb6Michael Chan                    + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
255821bd42039c92232647f54461cd20708224cc7eb6Michael Chan            if (lineY < mViewStartY + mViewHeight) {
255921bd42039c92232647f54461cd20708224cc7eb6Michael Chan                lineY = Math.max(lineY, mViewStartY);
256021bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.left = computeDayLeftPosition(todayIndex) + 1;
256121bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.right = computeDayLeftPosition(todayIndex + 1);
256221bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.top = lineY;
256321bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.bottom = mViewStartY + mViewHeight;
256421bd42039c92232647f54461cd20708224cc7eb6Michael Chan                p.setColor(mFutureBgColor);
256521bd42039c92232647f54461cd20708224cc7eb6Michael Chan                canvas.drawRect(r, p);
256621bd42039c92232647f54461cd20708224cc7eb6Michael Chan            }
256721bd42039c92232647f54461cd20708224cc7eb6Michael Chan
256821bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Paint Tomorrow and later days with future color
256921bd42039c92232647f54461cd20708224cc7eb6Michael Chan            if (todayIndex + 1 < mNumDays) {
257021bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.left = computeDayLeftPosition(todayIndex + 1) + 1;
257121bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.right = computeDayLeftPosition(mNumDays + 1);
257221bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.top = mDestRect.top;
257321bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.bottom = mDestRect.bottom;
257421bd42039c92232647f54461cd20708224cc7eb6Michael Chan                p.setColor(mFutureBgColor);
257521bd42039c92232647f54461cd20708224cc7eb6Michael Chan                canvas.drawRect(r, p);
257621bd42039c92232647f54461cd20708224cc7eb6Michael Chan            }
257721bd42039c92232647f54461cd20708224cc7eb6Michael Chan        } else if (todayIndex < 0) {
257821bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Future
257921bd42039c92232647f54461cd20708224cc7eb6Michael Chan            r.left = computeDayLeftPosition(0) + 1;
258021bd42039c92232647f54461cd20708224cc7eb6Michael Chan            r.right = computeDayLeftPosition(mNumDays + 1);
2581e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.top = mDestRect.top;
2582e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.bottom = mDestRect.bottom;
258321bd42039c92232647f54461cd20708224cc7eb6Michael Chan            p.setColor(mFutureBgColor);
2584e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(r, p);
2585e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2586e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2587e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2588e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2589146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Event getSelectedEvent() {
2590146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == null) {
2591146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // There is no event at the selected hour, so create a new event.
2592146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return getNewEvent(mSelectionDay, getSelectedTimeInMillis(),
2593146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    getSelectedMinutesSinceMidnight());
2594146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2595146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return mSelectedEvent;
2596146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2597146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2598146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    boolean isEventSelected() {
2599146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return (mSelectedEvent != null);
2600146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2601146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2602146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Event getNewEvent() {
2603146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return getNewEvent(mSelectionDay, getSelectedTimeInMillis(),
2604146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                getSelectedMinutesSinceMidnight());
2605146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2606146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2607146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    static Event getNewEvent(int julianDay, long utcMillis,
2608146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int minutesSinceMidnight) {
2609146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event event = Event.newInstance();
2610146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startDay = julianDay;
2611146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endDay = julianDay;
2612146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startMillis = utcMillis;
2613146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endMillis = event.startMillis + MILLIS_PER_HOUR;
2614146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startTime = minutesSinceMidnight;
2615146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endTime = event.startTime + MINUTES_PER_HOUR;
2616146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return event;
2617146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2618146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2619146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) {
2620146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        float maxWidthF = 0.0f;
2621146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2622146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = strings.length;
2623146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < len; i++) {
2624146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            float width = p.measureText(strings[i]);
2625146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            maxWidthF = Math.max(width, maxWidthF);
2626146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2627146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int maxWidth = (int) (maxWidthF + 0.5);
2628146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (maxWidth < currentMax) {
2629146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            maxWidth = currentMax;
2630146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2631146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return maxWidth;
2632146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2633146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2634146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void saveSelectionPosition(float left, float top, float right, float bottom) {
2635146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.left = (int) left;
2636146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.right = (int) right;
2637146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.top = (int) top;
2638146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.bottom = (int) bottom;
2639146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2640146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2641146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect getCurrentSelectionPosition() {
2642146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect box = new Rect();
2643146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        box.top = mSelectionHour * (mCellHeight + HOUR_GAP);
2644146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        box.bottom = box.top + mCellHeight + HOUR_GAP;
2645146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int daynum = mSelectionDay - mFirstJulianDay;
2646e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        box.left = computeDayLeftPosition(daynum) + 1;
2647e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        box.right = computeDayLeftPosition(daynum + 1);
2648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return box;
2649146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2650146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
26511ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void setupTextRect(Rect r) {
26521ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom <= r.top || r.right <= r.left) {
26531ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top;
26541ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right = r.left;
265536a4668f603c432d08ad156cd7c731f9838c0212Michael Chan            return;
265636a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
265736a4668f603c432d08ad156cd7c731f9838c0212Michael Chan
26581ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) {
26591ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.top += EVENT_TEXT_TOP_MARGIN;
26601ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom -= EVENT_TEXT_BOTTOM_MARGIN;
266136a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
26621ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) {
26631ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.left += EVENT_TEXT_LEFT_MARGIN;
26641ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right -= EVENT_TEXT_RIGHT_MARGIN;
266536a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
26669c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    }
26679c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan
26681ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void setupAllDayTextRect(Rect r) {
26691ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom <= r.top || r.right <= r.left) {
26701ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top;
26711ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right = r.left;
267236a4668f603c432d08ad156cd7c731f9838c0212Michael Chan            return;
267336a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
267436a4668f603c432d08ad156cd7c731f9838c0212Michael Chan
26751ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) {
26761ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN;
26771ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN;
267836a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
26791ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) {
26801ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
26811ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN;
268236a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
26839c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    }
26849c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan
26853e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh    /**
26863e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh     * Return the layout for a numbered event. Create it if not already existing
26873e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh     */
268808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint,
268908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            Rect r) {
269008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        if (i < 0 || i >= layouts.length) {
26910a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan            return null;
26920a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan        }
26930a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan
269408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        StaticLayout layout = layouts[i];
2695f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        // Check if we have already initialized the StaticLayout and that
2696f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        // the width hasn't changed (due to vertical resizing which causes
2697f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        // re-layout of events at min height)
26981ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (layout == null || r.width() != layout.getWidth()) {
2699e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            SpannableStringBuilder bob = new SpannableStringBuilder();
2700e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (event.title != null) {
2701e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN));
2702e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0);
2703e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(' ');
2704e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
2705e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (event.location != null) {
2706e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(drawTextSanitizer(event.location.toString(),
2707e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                        MAX_EVENT_TEXT_LEN - bob.length()));
2708e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
27093e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
271025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            switch (event.selfAttendeeStatus) {
271125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_INVITED:
271225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    paint.setColor(event.color);
271325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    break;
271425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_DECLINED:
271525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_NONE: // Your own events
271625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_ACCEPTED:
271725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_TENTATIVE:
271825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                default:
271925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    paint.setColor(mEventTextColor);
272025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    break;
2721f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            }
2722f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan
27233e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh            // Leave a one pixel boundary on the left and right of the rectangle for the event
2724e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            layout = new StaticLayout(bob, 0, bob.length(), new TextPaint(paint), r.width(),
27251ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                    Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width());
27263e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
272708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            layouts[i] = layout;
27283e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        }
27293e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
27303e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        return layout;
27313e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh    }
27323e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
27331ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) {
27343e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
2735146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTextSize(NORMAL_FONT_SIZE);
2736146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTextAlign(Paint.Align.LEFT);
2737146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint eventTextPaint = mEventTextPaint;
2738146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
27393eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float startY = DAY_HEADER_HEIGHT;
27402fda24534978f55711526dbde747aa50d68dec40RoboErik        final float stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN;
2741e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        float x = 0;
274202041687c35441e8a21cb763f1fc3a26525292efDavid Brown        int linesIndex = 0;
27433eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
27443eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner vertical grid lines
27453eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setColor(mCalendarGridLineInnerVerticalColor);
27461ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        x = mHoursWidth;
27473eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
27481ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        // Line bounding the top of the all day area
27498f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
27501ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = startY;
2751e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mLines[linesIndex++] = computeDayLeftPosition(mNumDays + 1);
27521ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = startY;
27531ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
2754e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        for (int day = 0; day <= mNumDays; day++) {
2755e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day);
275602041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
275702041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = startY;
275802041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
275902041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopY;
27603eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
2761e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
276202041687c35441e8a21cb763f1fc3a26525292efDavid Brown        canvas.drawLines(mLines, 0, linesIndex, p);
2763146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setStyle(Style.FILL);
2764146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
27653eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
2766146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int lastDay = firstDay + numDays - 1;
276708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        final ArrayList<Event> events = mAllDayEvents;
2768146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
27692fda24534978f55711526dbde747aa50d68dec40RoboErik        // Whether or not we should draw the more events text
27702fda24534978f55711526dbde747aa50d68dec40RoboErik        boolean hasMoreEvents = false;
27712fda24534978f55711526dbde747aa50d68dec40RoboErik        // size of the allDay area
27722fda24534978f55711526dbde747aa50d68dec40RoboErik        float drawHeight = mAlldayHeight;
27732fda24534978f55711526dbde747aa50d68dec40RoboErik        // max number of events being drawn in one day of the allday area
27742fda24534978f55711526dbde747aa50d68dec40RoboErik        float numRectangles = mMaxAlldayEvents;
27752fda24534978f55711526dbde747aa50d68dec40RoboErik        // Where to cut off drawn allday events
27762fda24534978f55711526dbde747aa50d68dec40RoboErik        int allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN;
27772fda24534978f55711526dbde747aa50d68dec40RoboErik        // The number of events that weren't drawn in each day
27782fda24534978f55711526dbde747aa50d68dec40RoboErik        mSkippedAlldayEvents = new int[numDays];
27792fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents &&
27802fda24534978f55711526dbde747aa50d68dec40RoboErik                mAnimateDayHeight == 0) {
27812fda24534978f55711526dbde747aa50d68dec40RoboErik            // We draw one fewer event than will fit so that more events text
27822fda24534978f55711526dbde747aa50d68dec40RoboErik            // can be drawn
27832fda24534978f55711526dbde747aa50d68dec40RoboErik            numRectangles = mMaxUnexpandedAlldayEventCount - 1;
27842fda24534978f55711526dbde747aa50d68dec40RoboErik            // We also clip the events above the more events text
27852fda24534978f55711526dbde747aa50d68dec40RoboErik            allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
27862fda24534978f55711526dbde747aa50d68dec40RoboErik            hasMoreEvents = true;
27872fda24534978f55711526dbde747aa50d68dec40RoboErik        } else if (mAnimateDayHeight != 0) {
27882fda24534978f55711526dbde747aa50d68dec40RoboErik            // clip at the end of the animating space
27892fda24534978f55711526dbde747aa50d68dec40RoboErik            allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN;
27902fda24534978f55711526dbde747aa50d68dec40RoboErik        }
2791146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
2792146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
2793146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startDay = event.startDay;
2794146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endDay = event.endDay;
2795c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (startDay > lastDay || endDay < firstDay) {
2796146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
2797c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2798c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (startDay < firstDay) {
2799146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startDay = firstDay;
2800c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2801c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (endDay > lastDay) {
2802146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endDay = lastDay;
2803c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2804146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startIndex = startDay - firstDay;
2805146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endIndex = endDay - firstDay;
28062fda24534978f55711526dbde747aa50d68dec40RoboErik            float height = mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount ? mAnimateDayEventHeight :
28072fda24534978f55711526dbde747aa50d68dec40RoboErik                    drawHeight / numRectangles;
2808146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2809146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Prevent a single event from getting too big
2810f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
2811f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
2812146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2813146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2814146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Leave a one-pixel space between the vertical day lines and the
2815146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // event rectangle.
2816e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            event.left = computeDayLeftPosition(startIndex);
2817e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP;
2818146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            event.top = y + height * event.getColumn();
281925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN;
28202fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
28212fda24534978f55711526dbde747aa50d68dec40RoboErik                // check if we should skip this event. We skip if it starts
28222fda24534978f55711526dbde747aa50d68dec40RoboErik                // after the clip bound or ends after the skip bound and we're
28232fda24534978f55711526dbde747aa50d68dec40RoboErik                // not animating.
28242fda24534978f55711526dbde747aa50d68dec40RoboErik                if (event.top >= allDayEventClip) {
28252fda24534978f55711526dbde747aa50d68dec40RoboErik                    incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
28262fda24534978f55711526dbde747aa50d68dec40RoboErik                    continue;
28272fda24534978f55711526dbde747aa50d68dec40RoboErik                } else if (event.bottom > allDayEventClip) {
28282fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (hasMoreEvents) {
28292fda24534978f55711526dbde747aa50d68dec40RoboErik                        incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
28302fda24534978f55711526dbde747aa50d68dec40RoboErik                        continue;
28312fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
28322fda24534978f55711526dbde747aa50d68dec40RoboErik                    event.bottom = allDayEventClip;
28332fda24534978f55711526dbde747aa50d68dec40RoboErik                }
28342fda24534978f55711526dbde747aa50d68dec40RoboErik            }
283525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            Rect r = drawEventRect(event, canvas, p, eventTextPaint, (int) event.top,
283625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    (int) event.bottom);
28371ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            setupAllDayTextRect(r);
283808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r);
283908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            drawEventText(layout, r, canvas, r.top, r.bottom);
2840146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2841146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Check if this all-day event intersects the selected day
28422fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mSelectionAllday && mComputeSelectedEvents) {
2843146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (startDay <= mSelectionDay && endDay >= mSelectionDay) {
2844146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectedEvents.add(event);
2845146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2846146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2847146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
28482fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) {
28492fda24534978f55711526dbde747aa50d68dec40RoboErik            // If the more allday text should be visible, draw it.
28502fda24534978f55711526dbde747aa50d68dec40RoboErik            p.setColor(mMoreAlldayEventsTextAlpha << 24 & mMoreEventsTextColor);
28512fda24534978f55711526dbde747aa50d68dec40RoboErik            for (int i = 0; i < mSkippedAlldayEvents.length; i++) {
28522fda24534978f55711526dbde747aa50d68dec40RoboErik                if (mSkippedAlldayEvents[i] > 0) {
28532fda24534978f55711526dbde747aa50d68dec40RoboErik                    drawMoreAlldayEvents(canvas, mSkippedAlldayEvents[i], i, p);
28542fda24534978f55711526dbde747aa50d68dec40RoboErik                }
28552fda24534978f55711526dbde747aa50d68dec40RoboErik            }
28562fda24534978f55711526dbde747aa50d68dec40RoboErik        }
2857146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
28582fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionAllday) {
2859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Compute the neighbors for the list of all-day events that
2860146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // intersect the selected day.
2861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeAllDayNeighbors();
2862146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Set the selection position to zero so that when we move down
2864146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // to the normal event area, we will highlight the topmost event.
2865146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            saveSelectionPosition(0f, 0f, 0f, 0f);
2866146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2867146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2868146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
28692fda24534978f55711526dbde747aa50d68dec40RoboErik    // Helper method for counting the number of allday events skipped on each day
28702fda24534978f55711526dbde747aa50d68dec40RoboErik    private void incrementSkipCount(int[] counts, int startIndex, int endIndex) {
28712fda24534978f55711526dbde747aa50d68dec40RoboErik        if (counts == null || startIndex < 0 || endIndex > counts.length) {
28722fda24534978f55711526dbde747aa50d68dec40RoboErik            return;
28732fda24534978f55711526dbde747aa50d68dec40RoboErik        }
28742fda24534978f55711526dbde747aa50d68dec40RoboErik        for (int i = startIndex; i <= endIndex; i++) {
28752fda24534978f55711526dbde747aa50d68dec40RoboErik            counts[i]++;
28762fda24534978f55711526dbde747aa50d68dec40RoboErik        }
28772fda24534978f55711526dbde747aa50d68dec40RoboErik    }
28782fda24534978f55711526dbde747aa50d68dec40RoboErik
28792fda24534978f55711526dbde747aa50d68dec40RoboErik    // Draws the "box +n" text for hidden allday events
28802fda24534978f55711526dbde747aa50d68dec40RoboErik    protected void drawMoreAlldayEvents(Canvas canvas, int remainingEvents, int day, Paint p) {
2881e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
2882e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int y = (int) (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - .5f
2883e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                * EVENT_SQUARE_WIDTH + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN);
2884763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        Rect r = mRect;
2885763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.top = y;
2886763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.left = x;
2887763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.bottom = y + EVENT_SQUARE_WIDTH;
2888763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.right = x + EVENT_SQUARE_WIDTH;
2889e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mMoreEventsTextColor);
2890e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
2891763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        p.setStyle(Style.STROKE);
2892e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
2893763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        canvas.drawRect(r, p);
2894e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2895763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        p.setStyle(Style.FILL);
2896763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        p.setTextSize(EVENT_TEXT_FONT_SIZE);
28972fda24534978f55711526dbde747aa50d68dec40RoboErik        String text = mResources.getQuantityString(R.plurals.month_more_events, remainingEvents);
28982fda24534978f55711526dbde747aa50d68dec40RoboErik        y += EVENT_SQUARE_WIDTH;
28992fda24534978f55711526dbde747aa50d68dec40RoboErik        x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING;
29002fda24534978f55711526dbde747aa50d68dec40RoboErik        canvas.drawText(String.format(text, remainingEvents), x, y, p);
29012fda24534978f55711526dbde747aa50d68dec40RoboErik    }
29022fda24534978f55711526dbde747aa50d68dec40RoboErik
2903146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeAllDayNeighbors() {
2904146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = mSelectedEvents.size();
2905146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (len == 0 || mSelectedEvent != null) {
2906146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
2907146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2908146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2909146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear all the links
2910146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
2911146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
2912146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = null;
2913146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = null;
2914146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = null;
2915146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = null;
2916146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2918146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // For each event in the selected event list "mSelectedEvents", find
291946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        // its neighbors in the up and down directions. This could be done
2920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // more efficiently by sorting on the Event.getColumn() field, but
2921146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // the list is expected to be very small.
2922146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2923146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Find the event in the same row as the previously selected all-day
2924146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // event, if any.
2925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int startPosition = -1;
292668655b5af2b7e5f872a103858ec58a19042123ceRoboErik        if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) {
2927146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            startPosition = mPrevSelectedEvent.getColumn();
2928146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2929146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int maxPosition = -1;
2930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event startEvent = null;
2931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event maxPositionEvent = null;
2932146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
2933146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
2934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int position = ev.getColumn();
2935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (position == startPosition) {
2936146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEvent = ev;
2937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (position > maxPosition) {
2938146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                maxPositionEvent = ev;
2939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                maxPosition = position;
2940146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int jj = 0; jj < len; jj++) {
2942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (jj == ii) {
2943146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
2944146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2945146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event neighbor = mSelectedEvents.get(jj);
2946146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborPosition = neighbor.getColumn();
2947146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighborPosition == position - 1) {
2948146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    ev.nextUp = neighbor;
2949146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighborPosition == position + 1) {
2950146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    ev.nextDown = neighbor;
2951146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2952146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2953146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2954146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (startEvent != null) {
2955146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = startEvent;
2956146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2957146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = maxPositionEvent;
2958146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2959146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2960146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2961e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawEvents(int date, int dayIndex, int top, Canvas canvas, Paint p) {
2962146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint eventTextPaint = mEventTextPaint;
2963763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        int left = computeDayLeftPosition(dayIndex) + 1;
2964763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        int cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1;
2965146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cellHeight = mCellHeight;
2966146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2967146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Use the selected hour as the selection region
2968259b8f9a21fffd3858218581e9daaf689b6b888fErik        Rect selectionArea = mSelectionRect;
2969146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP);
2970146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.bottom = selectionArea.top + cellHeight;
2971146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.left = left;
2972146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.right = selectionArea.left + cellWidth;
2973146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
297408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        final ArrayList<Event> events = mEvents;
2975146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
2976146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        EventGeometry geometry = mEventGeometry;
2977146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
29782fda24534978f55711526dbde747aa50d68dec40RoboErik        final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight;
2979146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
2980146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
2981146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
2982146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
2983146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2984146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
298508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // Don't draw it if it is not visible
298608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            if (event.bottom < mViewStartY || event.top > viewEndY) {
298708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                continue;
298808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            }
298908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
29902fda24534978f55711526dbde747aa50d68dec40RoboErik            if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents
2991146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    && geometry.eventIntersectsSelection(event, selectionArea)) {
2992146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectedEvents.add(event);
2993146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2994146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
299525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            Rect r = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY);
29961ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            setupTextRect(r);
299708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
299808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // Don't draw text if it is not visible
299908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            if (r.top > viewEndY || r.bottom < mViewStartY) {
300008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                continue;
300108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            }
300208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r);
300308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // TODO: not sure why we are 4 pixels off
300446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            drawEventText(layout, r, canvas, mViewStartY + 4, mViewStartY + mViewHeight
30052fda24534978f55711526dbde747aa50d68dec40RoboErik                    - DAY_HEADER_HEIGHT - mAlldayHeight);
3006146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3007146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
30082fda24534978f55711526dbde747aa50d68dec40RoboErik        if (date == mSelectionDay && !mSelectionAllday && isFocused()
3009146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                && mSelectionMode != SELECTION_HIDDEN) {
3010146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeNeighbors();
3011146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3012146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3013146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3014146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Computes the "nearest" neighbor event in four directions (left, right,
3015146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // up, down) for each of the events in the mSelectedEvents array.
3016146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeNeighbors() {
3017146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = mSelectedEvents.size();
3018146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (len == 0 || mSelectedEvent != null) {
3019146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3020146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3021146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3022146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear all the links
3023146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
3024146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
3025146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = null;
3026146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = null;
3027146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = null;
3028146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = null;
3029146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3030146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3031146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event startEvent = mSelectedEvents.get(0);
303246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int startEventDistance1 = 100000; // any large number
303346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int startEventDistance2 = 100000; // any large number
3034146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevLocation = FROM_NONE;
3035146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevTop;
3036146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevBottom;
3037146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevLeft;
3038146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevRight;
3039146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevCenter = 0;
3040146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect box = getCurrentSelectionPosition();
3041146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mPrevSelectedEvent != null) {
3042146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevTop = (int) mPrevSelectedEvent.top;
3043146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevBottom = (int) mPrevSelectedEvent.bottom;
3044146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLeft = (int) mPrevSelectedEvent.left;
3045146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevRight = (int) mPrevSelectedEvent.right;
3046146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Check if the previously selected event intersects the previous
304746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // selection box. (The previously selected event may be from a
3048146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // much older selection box.)
3049146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (prevTop >= mPrevBox.bottom || prevBottom <= mPrevBox.top
3050146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || prevRight <= mPrevBox.left || prevLeft >= mPrevBox.right) {
3051146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
3052146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevTop = mPrevBox.top;
3053146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevBottom = mPrevBox.bottom;
3054146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevLeft = mPrevBox.left;
3055146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevRight = mPrevBox.right;
3056146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
3057146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Clip the top and bottom to the previous selection box.
3058146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (prevTop < mPrevBox.top) {
3059146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    prevTop = mPrevBox.top;
3060146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3061146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (prevBottom > mPrevBox.bottom) {
3062146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    prevBottom = mPrevBox.bottom;
3063146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3064146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3065146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3066146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Just use the previously drawn selection box
3067146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevTop = mPrevBox.top;
3068146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevBottom = mPrevBox.bottom;
3069146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLeft = mPrevBox.left;
3070146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevRight = mPrevBox.right;
3071146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3072146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3073146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Figure out where we came from and compute the center of that area.
3074146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (prevLeft >= box.right) {
3075146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was to the right of us.
3076146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_RIGHT;
3077146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevTop + prevBottom) / 2;
3078146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevRight <= box.left) {
3079146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was to the left of us.
3080146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_LEFT;
3081146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevTop + prevBottom) / 2;
3082146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevBottom <= box.top) {
3083146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was above us.
3084146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_ABOVE;
3085146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevLeft + prevRight) / 2;
3086146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevTop >= box.bottom) {
3087146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was below us.
3088146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_BELOW;
3089146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevLeft + prevRight) / 2;
3090146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3092146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // For each event in the selected event list "mSelectedEvents", search
3093146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // all the other events in that list for the nearest neighbor in 4
3094146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // directions.
3095146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
3096146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
3097146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3098146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startTime = ev.startTime;
3099146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endTime = ev.endTime;
3100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int left = (int) ev.left;
3101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int right = (int) ev.right;
3102146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int top = (int) ev.top;
3103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (top < box.top) {
3104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                top = box.top;
3105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int bottom = (int) ev.bottom;
3107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (bottom > box.bottom) {
3108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                bottom = box.bottom;
3109146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
311021bd42039c92232647f54461cd20708224cc7eb6Michael Chan//            if (false) {
311121bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL
311221bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                        | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
311321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                if (DateFormat.is24HourFormat(mContext)) {
311421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                    flags |= DateUtils.FORMAT_24HOUR;
311521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                }
311621bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                String timeRange = DateUtils.formatDateRange(mContext, ev.startMillis,
311721bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                        ev.endMillis, flags);
311821bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                Log.i("Cal", "left: " + left + " right: " + right + " top: " + top + " bottom: "
311921bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                        + bottom + " ev: " + timeRange + " " + ev.title);
312021bd42039c92232647f54461cd20708224cc7eb6Michael Chan//            }
312146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int upDistanceMin = 10000; // any large number
312246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int downDistanceMin = 10000; // any large number
312346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int leftDistanceMin = 10000; // any large number
312446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int rightDistanceMin = 10000; // any large number
3125146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event upEvent = null;
3126146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event downEvent = null;
3127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event leftEvent = null;
3128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event rightEvent = null;
3129146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3130146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Pick the starting event closest to the previously selected event,
313146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // if any. distance1 takes precedence over distance2.
3132146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int distance1 = 0;
3133146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int distance2 = 0;
3134146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (prevLocation == FROM_ABOVE) {
3135146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (left >= prevCenter) {
3136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = left - prevCenter;
3137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (right <= prevCenter) {
3138146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - right;
3139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = top - prevBottom;
3141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_BELOW) {
3142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (left >= prevCenter) {
3143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = left - prevCenter;
3144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (right <= prevCenter) {
3145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - right;
3146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = prevTop - bottom;
3148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_LEFT) {
3149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (bottom <= prevCenter) {
3150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - bottom;
3151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (top >= prevCenter) {
3152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = top - prevCenter;
3153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = left - prevRight;
3155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_RIGHT) {
3156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (bottom <= prevCenter) {
3157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - bottom;
3158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (top >= prevCenter) {
3159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = top - prevCenter;
3160146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3161146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = prevLeft - right;
3162146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (distance1 < startEventDistance1
3164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || (distance1 == startEventDistance1 && distance2 < startEventDistance2)) {
3165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEvent = ev;
3166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEventDistance1 = distance1;
3167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEventDistance2 = distance2;
3168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // For each neighbor, figure out if it is above or below or left
3171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // or right of me and compute the distance.
3172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int jj = 0; jj < len; jj++) {
3173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (jj == ii) {
3174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
3175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event neighbor = mSelectedEvents.get(jj);
3177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborLeft = (int) neighbor.left;
3178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborRight = (int) neighbor.right;
3179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighbor.endTime <= startTime) {
3180146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely above me.
3181146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // If we overlap the same column, then compute the distance.
3182146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborLeft < right && neighborRight > left) {
3183146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int distance = startTime - neighbor.endTime;
3184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (distance < upDistanceMin) {
3185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            upDistanceMin = distance;
3186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            upEvent = neighbor;
3187146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else if (distance == upDistanceMin) {
3188146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int center = (left + right) / 2;
3189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentDistance = 0;
3190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentLeft = (int) upEvent.left;
3191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentRight = (int) upEvent.right;
3192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (currentRight <= center) {
3193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = center - currentRight;
3194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (currentLeft >= center) {
3195146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = currentLeft - center;
3196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int neighborDistance = 0;
3199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborRight <= center) {
3200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = center - neighborRight;
3201146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (neighborLeft >= center) {
3202146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = neighborLeft - center;
3203146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3204146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborDistance < currentDistance) {
3205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                upDistanceMin = distance;
3206146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                upEvent = neighbor;
3207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3208146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3209146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3210146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighbor.startTime >= endTime) {
3211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely below me.
3212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // If we overlap the same column, then compute the distance.
3213146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborLeft < right && neighborRight > left) {
3214146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int distance = neighbor.startTime - endTime;
3215146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (distance < downDistanceMin) {
3216146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            downDistanceMin = distance;
3217146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            downEvent = neighbor;
3218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else if (distance == downDistanceMin) {
3219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int center = (left + right) / 2;
3220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentDistance = 0;
3221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentLeft = (int) downEvent.left;
3222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentRight = (int) downEvent.right;
3223146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (currentRight <= center) {
3224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = center - currentRight;
3225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (currentLeft >= center) {
3226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = currentLeft - center;
3227146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3228146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3229146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int neighborDistance = 0;
3230146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborRight <= center) {
3231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = center - neighborRight;
3232146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (neighborLeft >= center) {
3233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = neighborLeft - center;
3234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3235146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborDistance < currentDistance) {
3236146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                downDistanceMin = distance;
3237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                downEvent = neighbor;
3238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3239146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3240146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighborLeft >= right) {
3244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely to the right of me.
3245146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Take the closest neighbor in the y direction.
3246146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int center = (top + bottom) / 2;
3247146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int distance = 0;
3248146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborBottom = (int) neighbor.bottom;
3249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborTop = (int) neighbor.top;
3250146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborBottom <= center) {
3251146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = center - neighborBottom;
3252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (neighborTop >= center) {
3253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = neighborTop - center;
3254146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3255146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (distance < rightDistanceMin) {
3256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        rightDistanceMin = distance;
3257146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        rightEvent = neighbor;
3258146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (distance == rightDistanceMin) {
3259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Pick the closest in the x direction
3260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int neighborDistance = neighborLeft - right;
3261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int currentDistance = (int) rightEvent.left - right;
3262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (neighborDistance < currentDistance) {
3263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            rightDistanceMin = distance;
3264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            rightEvent = neighbor;
3265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighborRight <= left) {
3268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely to the left of me.
3269146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Take the closest neighbor in the y direction.
3270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int center = (top + bottom) / 2;
3271146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int distance = 0;
3272146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborBottom = (int) neighbor.bottom;
3273146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborTop = (int) neighbor.top;
3274146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborBottom <= center) {
3275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = center - neighborBottom;
3276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (neighborTop >= center) {
3277146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = neighborTop - center;
3278146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (distance < leftDistanceMin) {
3280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        leftDistanceMin = distance;
3281146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        leftEvent = neighbor;
3282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (distance == leftDistanceMin) {
3283146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Pick the closest in the x direction
3284146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int neighborDistance = left - neighborRight;
3285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int currentDistance = left - (int) leftEvent.right;
3286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (neighborDistance < currentDistance) {
3287146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            leftDistanceMin = distance;
3288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            leftEvent = neighbor;
3289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3290146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3291146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3293146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = upEvent;
3294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = downEvent;
3295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = leftEvent;
3296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = rightEvent;
3297146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = startEvent;
3299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
330125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint,
330225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            int visibleTop, int visibleBot) {
330311ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw the Event Rect
33041ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        Rect r = mRect;
330525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN, visibleTop);
330625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN, visibleBot);
33071ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
330825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.right = (int) event.right;
33091ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
3310aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        int color = event.color;
3311f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        switch (event.selfAttendeeStatus) {
3312f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_INVITED:
331325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                p.setStyle(Style.STROKE);
331425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                break;
3315f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_DECLINED:
33163c40e07972c989719723e24db6d5d8812200823bRoboErik                color = Utils.getDeclinedColorFromColor(color);
3317f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_NONE: // Your own events
3318f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_ACCEPTED:
3319f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_TENTATIVE:
3320f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            default:
332125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                p.setStyle(Style.FILL_AND_STROKE);
3322f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan                break;
3323f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        }
3324e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
3325e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
3326e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
3327aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        int floorHalfStroke = (int) Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f);
3328aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        int ceilHalfStroke = (int) Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f);
3329aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop);
3330aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke,
3331aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                visibleBot);
3332aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.left += floorHalfStroke;
3333aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.right -= ceilHalfStroke;
3334aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
3335aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        p.setColor(color);
3336aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        canvas.drawRect(r, p);
3337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
33381ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        p.setStyle(Style.FILL);
3339e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If this event is selected, then use the selection color
3341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == event) {
33421ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            boolean paintIt = false;
3343aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            color = 0;
3344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectionMode == SELECTION_PRESSED) {
3345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Also, remember the last selected event that we drew
3346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = event;
334778f58c119af15ed85a727e16cddd793ced4c809dMichael Chan                color = mPressedColor;
33481ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                paintIt = true;
3349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mSelectionMode == SELECTION_SELECTED) {
3350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Also, remember the last selected event that we drew
3351146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = event;
335278f58c119af15ed85a727e16cddd793ced4c809dMichael Chan                color = mPressedColor;
33531ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                paintIt = true;
33541ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            }
33551ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
33561ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            if (paintIt) {
33571ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                p.setColor(color);
33581ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                canvas.drawRect(r, p);
3359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3360e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
3361e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setAntiAlias(true);
3362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
336411ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw cal color square border
336525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.top = (int) event.top + CALENDAR_COLOR_SQUARE_V_OFFSET;
336625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.left = (int) event.left + CALENDAR_COLOR_SQUARE_H_OFFSET;
336725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.bottom = r.top + CALENDAR_COLOR_SQUARE_SIZE + 1;
336825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.right = r.left + CALENDAR_COLOR_SQUARE_SIZE + 1;
336925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // p.setColor(0xFFFFFFFF);
337025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // canvas.drawRect(r, p);
33718af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project
337211ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw cal color
337325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.top++;
337425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.left++;
337525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.bottom--;
337625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.right--;
337725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // p.setColor(event.color);
337825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // canvas.drawRect(r, p);
3379e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
33803eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Setup rect for drawEventText which follows
33811ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.top = (int) event.top + EVENT_RECT_TOP_MARGIN;
33821ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN;
33831ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
33841ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN;
33851ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        return r;
3386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
33880d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    private Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],");
33890d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan
33900d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // Sanitize a string before passing it to drawText or else we get little
33910d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // squares. For newlines and tabs before a comma, delete the character.
33920d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // Otherwise, just replace them with a space.
3393f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    private String drawTextSanitizer(String string, int maxEventTextLen) {
33940d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan        Matcher m = drawTextSanitizerFilter.matcher(string);
3395f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        string = m.replaceAll(",");
3396f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan
3397f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        int len = string.length();
3398f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        if (len > maxEventTextLen) {
3399f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            string = string.substring(0, maxEventTextLen);
3400f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            len = maxEventTextLen;
3401f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        }
3402f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan
3403f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        return string.replace('\n', ' ');
34040d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    }
34050d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan
340608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top,
340708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            int bottom) {
34081ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging
34091ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
34101ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        int width = rect.right - rect.left;
34111ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        int height = rect.bottom - rect.top;
3412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the rectangle is too small for text, then return
34140a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan        if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) {
3415f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            return;
3416f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        }
3417f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan
341808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        int totalLineHeight = 0;
3419f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        int lineCount = eventLayout.getLineCount();
3420f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        for (int i = 0; i < lineCount; i++) {
3421f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            int lineBottom = eventLayout.getLineBottom(i);
3422f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            if (lineBottom <= height) {
342308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                totalLineHeight = lineBottom;
342408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            } else {
342508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                break;
3426f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            }
3427f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        }
3428f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan
342908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight < top) {
3430146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
34333e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        // Use a StaticLayout to format the string.
34343e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        canvas.save();
34351ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        canvas.translate(rect.left, rect.top);
34361ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.left = 0;
34371ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.right = width;
34381ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.top = 0;
343908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        rect.bottom = totalLineHeight;
344008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
344108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // There's a bug somewhere. If this rect is outside of a previous
344208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // cliprect, this becomes a no-op. What happens is that the text draw
344308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // past the event rect. The current fix is to not draw the staticLayout
344408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // at all if it is completely out of bound.
34451ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        canvas.clipRect(rect);
34463e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        eventLayout.draw(canvas);
34473e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        canvas.restore();
3448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3449146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
34501ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    // This is to replace p.setStyle(Style.STROKE); canvas.drawRect() since it
34511ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    // doesn't work well with hardware acceleration
345221bd42039c92232647f54461cd20708224cc7eb6Michael Chan//    private void drawEmptyRect(Canvas canvas, Rect r, int color) {
345321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        int linesIndex = 0;
345421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
345521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
345621bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
345721bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
345821bd42039c92232647f54461cd20708224cc7eb6Michael Chan//
345921bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
346021bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
346121bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
346221bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
346321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//
346421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
346521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
346621bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
346721bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
346821bd42039c92232647f54461cd20708224cc7eb6Michael Chan//
346921bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
347021bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
347121bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
347221bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
347321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mPaint.setColor(color);
347421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        canvas.drawLines(mLines, 0, linesIndex, mPaint);
347521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//    }
34761ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
3477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void updateEventDetails() {
3478146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == null || mSelectionMode == SELECTION_HIDDEN
3479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                || mSelectionMode == SELECTION_LONGPRESS) {
3480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPopup.dismiss();
3481146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3482146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3483666fc5e8ddbac15c951aad738bd818ee307eae42Erik        if (mLastPopupEventID == mSelectedEvent.id) {
3484666fc5e8ddbac15c951aad738bd818ee307eae42Erik            return;
3485666fc5e8ddbac15c951aad738bd818ee307eae42Erik        }
3486666fc5e8ddbac15c951aad738bd818ee307eae42Erik
3487666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = mSelectedEvent.id;
3488146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3489146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Remove any outstanding callbacks to dismiss the popup.
3490146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        getHandler().removeCallbacks(mDismissPopup);
3491146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3492146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event event = mSelectedEvent;
3493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView titleView = (TextView) mPopupView.findViewById(R.id.event_title);
3494146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        titleView.setText(event.title);
3495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3496146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ImageView imageView = (ImageView) mPopupView.findViewById(R.id.reminder_icon);
3497146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView.setVisibility(event.hasAlarm ? View.VISIBLE : View.GONE);
3498146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3499146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView = (ImageView) mPopupView.findViewById(R.id.repeat_icon);
3500146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView.setVisibility(event.isRepeating ? View.VISIBLE : View.GONE);
3501146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3502146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int flags;
3503146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (event.allDay) {
350446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            flags = DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_DATE
350546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL;
3506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3507146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE
3508146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL
3509146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
3510146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3511ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (DateFormat.is24HourFormat(mContext)) {
3512146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            flags |= DateUtils.FORMAT_24HOUR;
3513146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
351446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        String timeRange = Utils.formatDateRange(mContext, event.startMillis, event.endMillis,
351546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                flags);
3516146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView timeView = (TextView) mPopupView.findViewById(R.id.time);
3517146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        timeView.setText(timeRange);
3518146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3519146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView whereView = (TextView) mPopupView.findViewById(R.id.where);
3520146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final boolean empty = TextUtils.isEmpty(event.location);
3521146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        whereView.setVisibility(empty ? View.GONE : View.VISIBLE);
3522146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!empty) whereView.setText(event.location);
3523146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3524146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.LEFT, mHoursWidth, 5);
3525146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        postDelayed(mDismissPopup, POPUP_DISMISS_DELAY);
3526146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3527146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3528146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // The following routines are called from the parent activity when certain
3529146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // touch events occur.
35304484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doDown(MotionEvent ev) {
3531146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mTouchMode = TOUCH_MODE_DOWN;
3532146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewStartX = 0;
3533146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mOnFlingCalled = false;
3534146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        getHandler().removeCallbacks(mContinueScroll);
3535146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3536146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
35372fda24534978f55711526dbde747aa50d68dec40RoboErik    // Kicks off all the animations when the expand allday area is tapped
35382fda24534978f55711526dbde747aa50d68dec40RoboErik    private void doExpandAllDayClick() {
35392fda24534978f55711526dbde747aa50d68dec40RoboErik        mShowAllAllDayEvents = !mShowAllAllDayEvents;
35402fda24534978f55711526dbde747aa50d68dec40RoboErik
35412fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator.setFrameDelay(0);
35422fda24534978f55711526dbde747aa50d68dec40RoboErik
35432fda24534978f55711526dbde747aa50d68dec40RoboErik        // Determine the starting height
35442fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAnimateDayHeight == 0) {
35452fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayHeight = mShowAllAllDayEvents ?
35462fda24534978f55711526dbde747aa50d68dec40RoboErik                    mAlldayHeight - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT : mAlldayHeight;
35472fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35482fda24534978f55711526dbde747aa50d68dec40RoboErik        // Cancel current animations
35492fda24534978f55711526dbde747aa50d68dec40RoboErik        mCancellingAnimations = true;
35502fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayAnimator != null) {
35512fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayAnimator.cancel();
35522fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35532fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayEventAnimator != null) {
35542fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator.cancel();
35552fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35562fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMoreAlldayEventsAnimator != null) {
35572fda24534978f55711526dbde747aa50d68dec40RoboErik            mMoreAlldayEventsAnimator.cancel();
35582fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35592fda24534978f55711526dbde747aa50d68dec40RoboErik        mCancellingAnimations = false;
35602fda24534978f55711526dbde747aa50d68dec40RoboErik        // get new animators
35612fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator = getAllDayAnimator();
35622fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayEventAnimator = getAllDayEventAnimator();
35632fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(this,
35642fda24534978f55711526dbde747aa50d68dec40RoboErik                    "moreAllDayEventsTextAlpha",
35652fda24534978f55711526dbde747aa50d68dec40RoboErik                    mShowAllAllDayEvents ? MORE_EVENTS_MAX_ALPHA : 0,
35662fda24534978f55711526dbde747aa50d68dec40RoboErik                    mShowAllAllDayEvents ? 0 : MORE_EVENTS_MAX_ALPHA);
35672fda24534978f55711526dbde747aa50d68dec40RoboErik
35682fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up delays and start the animators
35692fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator.setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
35702fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator.start();
35712fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.setStartDelay(mShowAllAllDayEvents ? 0 : ANIMATION_DURATION);
35722fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION);
35732fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.start();
35742fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayEventAnimator != null) {
35752fda24534978f55711526dbde747aa50d68dec40RoboErik            // This is the only animator that can return null, so check it
35762fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator
35772fda24534978f55711526dbde747aa50d68dec40RoboErik                    .setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
35782fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator.start();
35792fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35802fda24534978f55711526dbde747aa50d68dec40RoboErik    }
35812fda24534978f55711526dbde747aa50d68dec40RoboErik
35822fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
35832fda24534978f55711526dbde747aa50d68dec40RoboErik     * Figures out the initial heights for allDay events and space when
35842fda24534978f55711526dbde747aa50d68dec40RoboErik     * a view is being set up.
35852fda24534978f55711526dbde747aa50d68dec40RoboErik     */
35862fda24534978f55711526dbde747aa50d68dec40RoboErik    public void initAllDayHeights() {
35872fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) {
35882fda24534978f55711526dbde747aa50d68dec40RoboErik            return;
35892fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35902fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mShowAllAllDayEvents) {
35912fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
35922fda24534978f55711526dbde747aa50d68dec40RoboErik            maxADHeight = Math.min(maxADHeight,
35932fda24534978f55711526dbde747aa50d68dec40RoboErik                    (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
35942fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents;
35952fda24534978f55711526dbde747aa50d68dec40RoboErik        } else {
35962fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayEventHeight = (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
35972fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35982fda24534978f55711526dbde747aa50d68dec40RoboErik    }
35992fda24534978f55711526dbde747aa50d68dec40RoboErik
36002fda24534978f55711526dbde747aa50d68dec40RoboErik    // Sets up an animator for changing the height of allday events
36012fda24534978f55711526dbde747aa50d68dec40RoboErik    private ObjectAnimator getAllDayEventAnimator() {
36022fda24534978f55711526dbde747aa50d68dec40RoboErik        // First calculate the absolute max height
36032fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
36042fda24534978f55711526dbde747aa50d68dec40RoboErik        // Now expand to fit but not beyond the absolute max
36052fda24534978f55711526dbde747aa50d68dec40RoboErik        maxADHeight =
36062fda24534978f55711526dbde747aa50d68dec40RoboErik                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
36072fda24534978f55711526dbde747aa50d68dec40RoboErik        // calculate the height of individual events in order to fit
36082fda24534978f55711526dbde747aa50d68dec40RoboErik        int fitHeight = maxADHeight / mMaxAlldayEvents;
36092fda24534978f55711526dbde747aa50d68dec40RoboErik        int currentHeight = mAnimateDayEventHeight;
36102fda24534978f55711526dbde747aa50d68dec40RoboErik        int desiredHeight =
36112fda24534978f55711526dbde747aa50d68dec40RoboErik                mShowAllAllDayEvents ? fitHeight : (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
36122fda24534978f55711526dbde747aa50d68dec40RoboErik        // if there's nothing to animate just return
36132fda24534978f55711526dbde747aa50d68dec40RoboErik        if (currentHeight == desiredHeight) {
36142fda24534978f55711526dbde747aa50d68dec40RoboErik            return null;
36152fda24534978f55711526dbde747aa50d68dec40RoboErik        }
36162fda24534978f55711526dbde747aa50d68dec40RoboErik
36172fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the animator with the calculated values
36182fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayEventHeight",
36192fda24534978f55711526dbde747aa50d68dec40RoboErik                currentHeight, desiredHeight);
36202fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.setDuration(ANIMATION_DURATION);
36212fda24534978f55711526dbde747aa50d68dec40RoboErik        return animator;
36222fda24534978f55711526dbde747aa50d68dec40RoboErik    }
36232fda24534978f55711526dbde747aa50d68dec40RoboErik
36242fda24534978f55711526dbde747aa50d68dec40RoboErik    // Sets up an animator for changing the height of the allday area
36252fda24534978f55711526dbde747aa50d68dec40RoboErik    private ObjectAnimator getAllDayAnimator() {
36262fda24534978f55711526dbde747aa50d68dec40RoboErik        // Calculate the absolute max height
36272fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
36282fda24534978f55711526dbde747aa50d68dec40RoboErik        // Find the desired height but don't exceed abs max
36292fda24534978f55711526dbde747aa50d68dec40RoboErik        maxADHeight =
36302fda24534978f55711526dbde747aa50d68dec40RoboErik                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
36312fda24534978f55711526dbde747aa50d68dec40RoboErik        // calculate the current and desired heights
36322fda24534978f55711526dbde747aa50d68dec40RoboErik        int currentHeight = mAnimateDayHeight != 0 ? mAnimateDayHeight : mAlldayHeight;
36332fda24534978f55711526dbde747aa50d68dec40RoboErik        int desiredHeight = mShowAllAllDayEvents ? maxADHeight :
36342fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1);
36352fda24534978f55711526dbde747aa50d68dec40RoboErik
36362fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the animator with the calculated values
36372fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayHeight",
36382fda24534978f55711526dbde747aa50d68dec40RoboErik                currentHeight, desiredHeight);
36392fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.setDuration(ANIMATION_DURATION);
36402fda24534978f55711526dbde747aa50d68dec40RoboErik
36412fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.addListener(new AnimatorListenerAdapter() {
36422fda24534978f55711526dbde747aa50d68dec40RoboErik            @Override
36432fda24534978f55711526dbde747aa50d68dec40RoboErik            public void onAnimationEnd(Animator animation) {
36442fda24534978f55711526dbde747aa50d68dec40RoboErik                if (!mCancellingAnimations) {
36452fda24534978f55711526dbde747aa50d68dec40RoboErik                    // when finished, set this to 0 to signify not animating
36462fda24534978f55711526dbde747aa50d68dec40RoboErik                    mAnimateDayHeight = 0;
3647cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                    mUseExpandIcon = !mShowAllAllDayEvents;
36482fda24534978f55711526dbde747aa50d68dec40RoboErik                }
36492fda24534978f55711526dbde747aa50d68dec40RoboErik                mRemeasure = true;
36502fda24534978f55711526dbde747aa50d68dec40RoboErik                invalidate();
36512fda24534978f55711526dbde747aa50d68dec40RoboErik            }
36522fda24534978f55711526dbde747aa50d68dec40RoboErik        });
36532fda24534978f55711526dbde747aa50d68dec40RoboErik        return animator;
36542fda24534978f55711526dbde747aa50d68dec40RoboErik    }
36552fda24534978f55711526dbde747aa50d68dec40RoboErik
36562fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the 'box +n' alpha text used by the animator
36572fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setMoreAllDayEventsTextAlpha(int alpha) {
36582fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsTextAlpha = alpha;
36592fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
36602fda24534978f55711526dbde747aa50d68dec40RoboErik    }
36612fda24534978f55711526dbde747aa50d68dec40RoboErik
36622fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the height of the allday area used by the animator
36632fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setAnimateDayHeight(int height) {
36642fda24534978f55711526dbde747aa50d68dec40RoboErik        mAnimateDayHeight = height;
36652fda24534978f55711526dbde747aa50d68dec40RoboErik        mRemeasure = true;
36662fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
36672fda24534978f55711526dbde747aa50d68dec40RoboErik    }
36682fda24534978f55711526dbde747aa50d68dec40RoboErik
36692fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the height of allday events used by the animator
36702fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setAnimateDayEventHeight(int height) {
36712fda24534978f55711526dbde747aa50d68dec40RoboErik        mAnimateDayEventHeight = height;
36722fda24534978f55711526dbde747aa50d68dec40RoboErik        mRemeasure = true;
36732fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
36742fda24534978f55711526dbde747aa50d68dec40RoboErik    }
36752fda24534978f55711526dbde747aa50d68dec40RoboErik
36764484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doSingleTapUp(MotionEvent ev) {
3677bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        if (!mHandleActionUp || mScrolling) {
3678d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            return;
3679d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3680d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3681146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int x = (int) ev.getX();
3682146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int y = (int) ev.getY();
3683146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectedDay = mSelectionDay;
3684146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectedHour = mSelectionHour;
3685146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
36862fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
36872fda24534978f55711526dbde747aa50d68dec40RoboErik            // check if the tap was in the allday expansion area
36884ab51bf9fc2f3e6915705f39688e8244fc74028fRoboErik            int bottom = mFirstCell;
36892fda24534978f55711526dbde747aa50d68dec40RoboErik            if((x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight)
36902fda24534978f55711526dbde747aa50d68dec40RoboErik                    || (!mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom &&
36912fda24534978f55711526dbde747aa50d68dec40RoboErik                            y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)) {
36922fda24534978f55711526dbde747aa50d68dec40RoboErik                doExpandAllDayClick();
36932fda24534978f55711526dbde747aa50d68dec40RoboErik                return;
36942fda24534978f55711526dbde747aa50d68dec40RoboErik            }
36952fda24534978f55711526dbde747aa50d68dec40RoboErik        }
36962fda24534978f55711526dbde747aa50d68dec40RoboErik
3697146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean validPosition = setSelectionFromPosition(x, y);
3698146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!validPosition) {
3699360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            if (y < DAY_HEADER_HEIGHT) {
3700360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                Time selectedTime = new Time(mBaseDate);
3701360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.setJulianDay(mSelectionDay);
3702360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.hour = mSelectionHour;
3703360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.normalize(true /* ignore isDst */);
3704360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                mController.sendEvent(this, EventType.GO_TO, null, null, selectedTime, -1,
3705360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                        ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null);
3706360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            }
3707146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3708146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3709146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3710b6fb51224b578b26e503fddae2799d03a2aaad47RoboErik        boolean hasSelection = mSelectionMode != SELECTION_HIDDEN;
371195f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        boolean pressedSelected = (hasSelection || mTouchExplorationEnabled)
371295f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                && selectedDay == mSelectionDay && selectedHour == mSelectionHour;
3713146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
371495f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        if (pressedSelected) {
3715344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            // If the tap is on an already selected hour slot, then create a new
3716344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            // event
3717edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            long extraLong = 0;
3718edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            if (mSelectionAllday) {
3719edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
3720edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            }
372195f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            mSelectionMode = SELECTION_SELECTED;
3722edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            mController.sendEventRelatedEventWithExtra(this, EventType.CREATE_EVENT, -1,
3723edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    getSelectedTimeInMillis(), 0, (int) ev.getRawX(), (int) ev.getRawY(),
3724edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    extraLong, -1);
372595f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        } else if (mSelectedEvent != null) {
372695f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            // If the tap is on an event, launch the "View event" view
372795f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            if (mIsAccessibilityEnabled) {
372895f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                mAccessibilityMgr.interrupt();
372995f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            }
373095f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan
373195f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            mSelectionMode = SELECTION_HIDDEN;
37320aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson
37330aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            int yLocation =
37340aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson                (int)((mSelectedEvent.top + mSelectedEvent.bottom)/2);
37350aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            // Y location is affected by the position of the event in the scrolling
37360aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            // view (mViewStartY) and the presence of all day events (mFirstCell)
37370aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            if (!mSelectedEvent.allDay) {
37380aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson                yLocation += (mFirstCell - mViewStartY);
37390aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            }
374095f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mSelectedEvent.id,
37410aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson                    mSelectedEvent.startMillis, mSelectedEvent.endMillis,
37420aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson                    (int)((mSelectedEvent.left + mSelectedEvent.right)/2),
37430aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson                    yLocation, getSelectedTimeInMillis());
3744344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan        } else {
374595f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            // Select time
3746344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            Time startTime = new Time(mBaseDate);
3747344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.setJulianDay(mSelectionDay);
3748344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.hour = mSelectionHour;
3749344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.normalize(true /* ignore isDst */);
375083b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
375183b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            Time endTime = new Time(startTime);
375283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            endTime.hour++;
375383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
375495f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            mSelectionMode = SELECTION_SELECTED;
375546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mController.sendEvent(this, EventType.GO_TO, startTime, endTime, -1, ViewType.CURRENT,
375646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    CalendarController.EXTRA_GOTO_TIME, null, null);
3757146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
375895f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        invalidate();
3759146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3760146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
37614484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doLongPress(MotionEvent ev) {
3762bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        if (mScrolling) {
3763bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan            return;
3764bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        }
3765bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan
3766d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        // Scale gesture in progress
3767d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mStartingSpanY != 0) {
3768d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            return;
3769d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3770d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
377180bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        int x = (int) ev.getX();
377280bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        int y = (int) ev.getY();
377380bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan
377480bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        boolean validPosition = setSelectionFromPosition(x, y);
377580bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        if (!validPosition) {
377680bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan            // return if the touch wasn't on an area of concern
377780bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan            return;
377880bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        }
377980bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan
3780146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_LONGPRESS;
3781146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
3782146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        performLongClick();
3783146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3784146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
37854484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) {
378695d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        cancelAnimation();
37874f58287a1e02325337e39073a50bbb880094099fRoboErik        if (mStartingScroll) {
37884f58287a1e02325337e39073a50bbb880094099fRoboErik            mInitialScrollX = 0;
37894f58287a1e02325337e39073a50bbb880094099fRoboErik            mInitialScrollY = 0;
37904f58287a1e02325337e39073a50bbb880094099fRoboErik            mStartingScroll = false;
37914f58287a1e02325337e39073a50bbb880094099fRoboErik        }
37926d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
37934f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollX += deltaX;
37944f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollY += deltaY;
37954f58287a1e02325337e39073a50bbb880094099fRoboErik        int distanceX = (int) mInitialScrollX;
37964f58287a1e02325337e39073a50bbb880094099fRoboErik        int distanceY = (int) mInitialScrollY;
3797146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3798146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If we haven't figured out the predominant scroll direction yet,
3799146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // then do it now.
3800146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mTouchMode == TOUCH_MODE_DOWN) {
3801146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int absDistanceX = Math.abs(distanceX);
3802146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int absDistanceY = Math.abs(distanceY);
3803146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mScrollStartY = mViewStartY;
3804146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPreviousDirection = 0;
3805146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3806f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            if (absDistanceX > absDistanceY) {
3807146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mTouchMode = TOUCH_MODE_HSCROLL;
3808146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartX = distanceX;
3809146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                initNextView(-mViewStartX);
3810146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
3811146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mTouchMode = TOUCH_MODE_VSCROLL;
3812146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3813146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
3814146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // We are already scrolling horizontally, so check if we
3815146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // changed the direction of scrolling so that the other week
3816146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // is now visible.
3817146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartX = distanceX;
3818146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (distanceX != 0) {
3819146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int direction = (distanceX > 0) ? 1 : -1;
3820146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (direction != mPreviousDirection) {
3821146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // The user has switched the direction of scrolling
3822146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // so re-init the next view
3823146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    initNextView(-mViewStartX);
3824146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mPreviousDirection = direction;
3825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3826146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3827146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3828146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3829146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) {
3830146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartY = mScrollStartY + distanceY;
3831433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
3832433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            // If dragging while already at the end, do a glow
3833433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            final int pulledToY = (int) (mScrollStartY + deltaY);
3834433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (pulledToY < 0) {
3835433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectTop.onPull(deltaY / mViewHeight);
3836433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (!mEdgeEffectBottom.isFinished()) {
3837433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectBottom.onRelease();
3838433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
3839433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            } else if (pulledToY > mMaxViewStartY) {
3840433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectBottom.onPull(deltaY / mViewHeight);
3841433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (!mEdgeEffectTop.isFinished()) {
3842433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectTop.onRelease();
3843433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
3844433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
3845433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
3846146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mViewStartY < 0) {
3847146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = 0;
3848146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mViewStartY > mMaxViewStartY) {
3849146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = mMaxViewStartY;
3850146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3851146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeFirstHour();
3852146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3853146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3854146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = true;
3855146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3856d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        mSelectionMode = SELECTION_HIDDEN;
3857146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
3858146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3860f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    private void cancelAnimation() {
3861f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        Animation in = mViewSwitcher.getInAnimation();
3862f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        if (in != null) {
386395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            // cancel() doesn't terminate cleanly.
386495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            in.scaleCurrentDuration(0);
38656d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
3866f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        Animation out = mViewSwitcher.getOutAnimation();
3867f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        if (out != null) {
386895d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            // cancel() doesn't terminate cleanly.
386995d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            out.scaleCurrentDuration(0);
3870f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        }
3871f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    }
3872f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan
3873f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
3874f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        cancelAnimation();
38756d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
3876146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_HIDDEN;
3877146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mOnFlingCalled = true;
387890a68379c01137781249156742cfc9ba21684b83Michael Chan
387990a68379c01137781249156742cfc9ba21684b83Michael Chan        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
3880030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            // Horizontal fling.
388111638ad4fb56b978770aebe75deac7a0e6ac60d2Erik            // initNextView(deltaX);
388290a68379c01137781249156742cfc9ba21684b83Michael Chan            mTouchMode = TOUCH_MODE_INITIAL_STATE;
388390a68379c01137781249156742cfc9ba21684b83Michael Chan            if (DEBUG) Log.d(TAG, "doFling: velocityX " + velocityX);
388490a68379c01137781249156742cfc9ba21684b83Michael Chan            int deltaX = (int) e2.getX() - (int) e1.getX();
38852c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            switchViews(deltaX < 0, mViewStartX, mViewWidth, velocityX);
3886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartX = 0;
3887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3888146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3889146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
389090a68379c01137781249156742cfc9ba21684b83Michael Chan        if ((mTouchMode & TOUCH_MODE_VSCROLL) == 0) {
389190a68379c01137781249156742cfc9ba21684b83Michael Chan            if (DEBUG) Log.d(TAG, "doFling: no fling");
389290a68379c01137781249156742cfc9ba21684b83Michael Chan            return;
389390a68379c01137781249156742cfc9ba21684b83Michael Chan        }
389490a68379c01137781249156742cfc9ba21684b83Michael Chan
3895030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        // Vertical fling.
389690a68379c01137781249156742cfc9ba21684b83Michael Chan        mTouchMode = TOUCH_MODE_INITIAL_STATE;
3897030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        mViewStartX = 0;
3898030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown
38993f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        if (DEBUG) {
39003f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            Log.d(TAG, "doFling: mViewStartY" + mViewStartY + " velocityY " + velocityY);
39013f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        }
39023f6328f579b05c02606572c777541b97aa04f9f3Michael Chan
3903146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Continue scrolling vertically
39043f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScrolling = true;
39053f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScroller.fling(0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */,
3906433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                (int) -velocityY, 0 /* minX */, 0 /* maxX */, 0 /* minY */,
3907433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE);
3908433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
3909433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // When flinging down, show a glow when it hits the end only if it
3910433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // wasn't started at the top
3911433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        if (velocityY > 0 && mViewStartY != 0) {
3912433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            mCallEdgeEffectOnAbsorb = true;
3913433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
3914433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // When flinging up, show a glow when it hits the end only if it wasn't
3915433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // started at the bottom
3916433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        else if (velocityY < 0 && mViewStartY != mMaxViewStartY) {
3917433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            mCallEdgeEffectOnAbsorb = true;
3918433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
3919146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        post(mContinueScroll);
3920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3921146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3922146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean initNextView(int deltaX) {
3923146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Change the view to the previous day or week
3924d190418220467c02327dd9b3ae1eae238889720bMichael Chan        DayView view = (DayView) mViewSwitcher.getNextView();
3925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time date = view.mBaseDate;
3926146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        date.set(mBaseDate);
3927146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean switchForward;
3928146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (deltaX > 0) {
3929146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.monthDay -= mNumDays;
3930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            view.mSelectionDay = mSelectionDay - mNumDays;
3931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchForward = false;
3932146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3933146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.monthDay += mNumDays;
3934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            view.mSelectionDay = mSelectionDay + mNumDays;
3935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchForward = true;
3936146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        date.normalize(true /* ignore isDst */);
3938146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        initView(view);
3939ad36a3c3cde7a2ec6d3a35d2529d46f03bd8d59dMichael Chan        view.layout(getLeft(), getTop(), getRight(), getBottom());
3940146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.reloadEvents();
3941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return switchForward;
3942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3943146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3944d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
3945d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public boolean onScaleBegin(ScaleGestureDetector detector) {
3946d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mHandleActionUp = false;
39472fda24534978f55711526dbde747aa50d68dec40RoboErik        float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
3948d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP);
3949d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3950d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY()));
3951d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mCellHeightBeforeScaleGesture = mCellHeight;
3952d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
39539ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG_SCALING) {
3954d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
39556f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan            Log.d(TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour
39566f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan                    + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY
39576f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan                    + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY());
3958d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3959d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3960d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        return true;
3961d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
3962d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3963d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
3964d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public boolean onScale(ScaleGestureDetector detector) {
39656f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan        float spanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY()));
3966d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3967d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY);
3968d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3969d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mCellHeight < mMinCellHeight) {
3970d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            // If mStartingSpanY is too small, even a small increase in the
3971d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT
39726f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan            mStartingSpanY = spanY;
3973d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = mMinCellHeight;
3974d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeightBeforeScaleGesture = mMinCellHeight;
3975d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        } else if (mCellHeight > MAX_CELL_HEIGHT) {
3976d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mStartingSpanY = spanY;
3977d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = MAX_CELL_HEIGHT;
3978d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT;
3979d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3980d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
39812fda24534978f55711526dbde747aa50d68dec40RoboErik        int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
3982d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels;
3983d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
3984d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
39859ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG_SCALING) {
3986d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
39876f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan            Log.d(TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: "
3988d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:"
3989d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + mCellHeight + " SpanY:" + detector.getCurrentSpanY());
3990d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3991d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3992d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mViewStartY < 0) {
3993d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = 0;
3994d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
3995d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    / (float) (mCellHeight + DAY_GAP);
3996d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        } else if (mViewStartY > mMaxViewStartY) {
3997d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = mMaxViewStartY;
3998d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
3999d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    / (float) (mCellHeight + DAY_GAP);
4000d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
4001d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        computeFirstHour();
4002d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4003d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mRemeasure = true;
4004d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        invalidate();
4005d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        return true;
4006d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
4007d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4008d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
4009d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public void onScaleEnd(ScaleGestureDetector detector) {
40104f58287a1e02325337e39073a50bbb880094099fRoboErik        mScrollStartY = mViewStartY;
40114f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollY = 0;
40124f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollX = 0;
4013d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mStartingSpanY = 0;
4014d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
4015d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4016146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
4017146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onTouchEvent(MotionEvent ev) {
4018146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int action = ev.getAction();
40199ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount());
4020146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
40211ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan        if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) {
40221ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan            mScaleGestureDetector.onTouchEvent(ev);
40231ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan            if (mScaleGestureDetector.isInProgress()) {
40241ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan                return true;
40251ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan            }
4026d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
4027d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4028146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (action) {
402946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_DOWN:
40304f58287a1e02325337e39073a50bbb880094099fRoboErik                mStartingScroll = true;
40319ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) {
40329ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_DOWN ev.getDownTime = " + ev.getDownTime() + " Cnt="
40339ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            + ev.getPointerCount());
40349ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                }
40359ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
40362fda24534978f55711526dbde747aa50d68dec40RoboErik                int bottom = mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
40372fda24534978f55711526dbde747aa50d68dec40RoboErik                if (ev.getY() < bottom) {
40382fda24534978f55711526dbde747aa50d68dec40RoboErik                    mTouchStartedInAlldayArea = true;
40392fda24534978f55711526dbde747aa50d68dec40RoboErik                } else {
40402fda24534978f55711526dbde747aa50d68dec40RoboErik                    mTouchStartedInAlldayArea = false;
40412fda24534978f55711526dbde747aa50d68dec40RoboErik                }
4042d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                mHandleActionUp = true;
404346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
4044d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                return true;
404546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
404646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_MOVE:
40479ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) Log.e(TAG, "ACTION_MOVE Cnt=" + ev.getPointerCount() + DayView.this);
404846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
4049146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
405046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
405146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_UP:
40529ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) Log.e(TAG, "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp);
4053433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectTop.onRelease();
4054433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectBottom.onRelease();
40554f58287a1e02325337e39073a50bbb880094099fRoboErik                mStartingScroll = false;
405646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
405746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (!mHandleActionUp) {
405846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mHandleActionUp = true;
40597abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project                    return true;
406046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
406146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mOnFlingCalled) {
406246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
406346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
406446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
406546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mTouchMode = TOUCH_MODE_INITIAL_STATE;
406646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) {
406746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // The user has gone beyond the threshold so switch views
406846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views");
40692c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                        switchViews(mViewStartX > 0, mViewStartX, mViewWidth, 0);
407046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mViewStartX = 0;
407146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        return true;
407246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    } else {
407346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // Not beyond the threshold so invalidate which will cause
407446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // the view to snap back. Also call recalc() to ensure
407546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // that we have the correct starting date and title.
407646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back");
407746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        recalc();
407846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        invalidate();
407946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mViewStartX = 0;
408046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
408146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
408246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
408346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // If we were scrolling, then reset the selected hour so that it
408446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // is visible.
408546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mScrolling) {
408646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mScrolling = false;
408746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    resetSelectedHour();
4088146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
4089146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
409046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return true;
4091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
409246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // This case isn't expected to happen.
409346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_CANCEL:
409446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (DEBUG) Log.e(TAG, "ACTION_CANCEL");
409546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
4096146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mScrolling = false;
4097146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                resetSelectedHour();
4098146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
409946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
410046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            default:
41019ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) Log.e(TAG, "Not MotionEvent " + ev.toString());
410246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mGestureDetector.onTouchEvent(ev)) {
410346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
410446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
410546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onTouchEvent(ev);
4106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4109146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
4110146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        MenuItem item;
4111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4112146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the trackball is held down, then the context menu pops up and
411346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        // we never get onKeyUp() for the long-press. So check for it here
4114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // and change the selection to the long-press state.
4115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionMode != SELECTION_LONGPRESS) {
4116146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionMode = SELECTION_LONGPRESS;
4117146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
4118146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4119146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4120e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        final long startMillis = getSelectedTimeInMillis();
4121146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int flags = DateUtils.FORMAT_SHOW_TIME
4122146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                | DateUtils.FORMAT_CAP_NOON_MIDNIGHT
4123146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                | DateUtils.FORMAT_SHOW_WEEKDAY;
4124c5f92d7bdeb1950ace783cc022aa41a4b4f00cd6Erik        final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags);
4125146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        menu.setHeaderTitle(title);
4126e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numSelectedEvents = mSelectedEvents.size();
4128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays == 1) {
4129146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Day view.
4130146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4131146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is a selected event, then allow it to be viewed and
4132146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // edited.
4133146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (numSelectedEvents >= 1) {
41344484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view);
4135146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_info_details);
4137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4138ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                int accessLevel = getEventAccessLevel(mContext, mSelectedEvent);
4139a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel == ACCESS_LEVEL_EDIT) {
41404484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit);
4141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_edit);
4143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setAlphabeticShortcut('e');
4144a505627659f6bfba8869eac8089f7030f2d0d92cErik                }
4145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4146a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel >= ACCESS_LEVEL_DELETE) {
41474484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete);
4148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_delete);
4150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
41524484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
4153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_add);
4155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setAlphabeticShortcut('n');
4156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
4157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Otherwise, if the user long-pressed on a blank hour, allow
415846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // them to create an event. They can also do this by tapping.
41594484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
4160146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4161146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_add);
4162146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setAlphabeticShortcut('n');
4163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
4165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Week view.
4166e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is a selected event, then allow it to be viewed and
4168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // edited.
4169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (numSelectedEvents >= 1) {
41704484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view);
4171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_info_details);
4173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4174ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                int accessLevel = getEventAccessLevel(mContext, mSelectedEvent);
4175a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel == ACCESS_LEVEL_EDIT) {
41764484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit);
4177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_edit);
4179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setAlphabeticShortcut('e');
4180a505627659f6bfba8869eac8089f7030f2d0d92cErik                }
4181146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4182a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel >= ACCESS_LEVEL_DELETE) {
41834484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete);
4184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_delete);
4186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
41874484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            }
4188146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
41894484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
41904484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setOnMenuItemClickListener(mContextMenuHandler);
41914484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setIcon(android.R.drawable.ic_menu_add);
41924484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setAlphabeticShortcut('n');
4193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
41944484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item = menu.add(0, MENU_DAY, 0, R.string.show_day_view);
41954484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setOnMenuItemClickListener(mContextMenuHandler);
41964484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setIcon(android.R.drawable.ic_menu_day);
41974484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setAlphabeticShortcut('d');
4198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.dismiss();
4201146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4202146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4203146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private class ContextMenuHandler implements MenuItem.OnMenuItemClickListener {
4204146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public boolean onMenuItemClick(MenuItem item) {
4205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switch (item.getItemId()) {
42064484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_VIEW: {
4207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
4208a7694eead7cbb293990bc89c3906e17bad36194cErik                        mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT_DETAILS,
42099e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                                mSelectedEvent.id, mSelectedEvent.startMillis,
421087f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                mSelectedEvent.endMillis, 0, 0, -1);
4211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4213146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
42144484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_EDIT: {
4215146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
42169e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                        mController.sendEventRelatedEvent(this, EventType.EDIT_EVENT,
42179e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                                mSelectedEvent.id, mSelectedEvent.startMillis,
421887f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                mSelectedEvent.endMillis, 0, 0, -1);
4219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
42224484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_DAY: {
4223487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                    mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1,
42249e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                            ViewType.DAY);
4225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
42274484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_AGENDA: {
4228487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                    mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1,
42299e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                            ViewType.AGENDA);
4230146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
42324484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_CREATE: {
4233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long startMillis = getSelectedTimeInMillis();
4234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
42359e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1,
423687f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            startMillis, endMillis, 0, 0, -1);
4237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
42394484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_DELETE: {
4240146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
4241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        Event selectedEvent = mSelectedEvent;
4242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long begin = selectedEvent.startMillis;
4243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long end = selectedEvent.endMillis;
4244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long id = selectedEvent.id;
42459e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                        mController.sendEventRelatedEvent(this, EventType.DELETE_EVENT, id, begin,
424687f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                end, 0, 0, -1);
4247146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4248146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4250146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                default: {
4251146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    return false;
4252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4254146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
4255146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4257146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4258a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static int getEventAccessLevel(Context context, Event e) {
4259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ContentResolver cr = context.getContentResolver();
4260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4261ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        int accessLevel = Calendars.CAL_ACCESS_NONE;
4262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Get the calendar id for this event
4264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Cursor cursor = cr.query(ContentUris.withAppendedId(Events.CONTENT_URI, e.id),
4265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                new String[] { Events.CALENDAR_ID },
4266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* selection */,
4267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* selectionArgs */,
4268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* sort */);
4269e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4270e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        if (cursor == null) {
4271a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
4272146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4273e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4274e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        if (cursor.getCount() == 0) {
4275e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan            cursor.close();
4276a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
4277e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        }
4278e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cursor.moveToFirst();
4280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long calId = cursor.getLong(0);
4281697bd6a8cec36cc75c72603674e57e4c6fe42dbfKen Shirriff        cursor.close();
4282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4283146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Uri uri = Calendars.CONTENT_URI;
4284146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String where = String.format(CALENDARS_WHERE, calId);
4285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cursor = cr.query(uri, CALENDARS_PROJECTION, where, null, null);
4286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
42876d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        String calendarOwnerAccount = null;
4288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (cursor != null) {
4289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            cursor.moveToFirst();
4290ced0eb67ef49c3203f72520c296e73d9cb4f654bMichael Chan            accessLevel = cursor.getInt(CALENDARS_INDEX_ACCESS_LEVEL);
42916d34cec523930368976bc8d358d0a6de1da3462cMichael Chan            calendarOwnerAccount = cursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
4292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            cursor.close();
4293146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4294e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4295ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        if (accessLevel < Calendars.CAL_ACCESS_CONTRIBUTOR) {
4296a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
42976d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        }
4298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
42996d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        if (e.guestsCanModify) {
4300a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_EDIT;
4301a505627659f6bfba8869eac8089f7030f2d0d92cErik        }
4302a505627659f6bfba8869eac8089f7030f2d0d92cErik
430346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (!TextUtils.isEmpty(calendarOwnerAccount)
430446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                && calendarOwnerAccount.equalsIgnoreCase(e.organizer)) {
4305a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_EDIT;
4306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4308a505627659f6bfba8869eac8089f7030f2d0d92cErik        return ACCESS_LEVEL_DELETE;
4309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
4312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position.
4313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * If the touch position is not within the displayed grid, then this
4314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * method returns false.
4315146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
4316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param x the x position of the touch
4317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param y the y position of the touch
4318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @return true if the touch position is valid
4319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
4320360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan    private boolean setSelectionFromPosition(int x, final int y) {
4321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (x < mHoursWidth) {
4322360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            x = mHoursWidth;
4323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4325146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP);
4326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (day >= mNumDays) {
4327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            day = mNumDays - 1;
4328146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4329146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        day += mFirstJulianDay;
4330360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        mSelectionDay = day;
4331360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan
4332360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        if (y < DAY_HEADER_HEIGHT) {
4333aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            sendAccessibilityEventAsNeeded(false);
4334360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            return false;
4335360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        }
433646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
433746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mSelectionHour = mFirstHour; /* First fully visible hour */
433846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
433946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (y < mFirstCell) {
43402fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = true;
4341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
434246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // y is now offset from top of the scrollable region
434355a26d7062d5aa1a1b001b7271bb137276ab807eErik            int adjustedY = y - mFirstCell;
434446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
434555a26d7062d5aa1a1b001b7271bb137276ab807eErik            if (adjustedY < mFirstHourOffset) {
434646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                --mSelectionHour; /* In the partially visible hour */
434746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else {
434855a26d7062d5aa1a1b001b7271bb137276ab807eErik                mSelectionHour += (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP);
434946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
435046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
43512fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = false;
4352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4353360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan
4354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        findSelectedEvent(x, y);
435546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
435646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//        Log.i("Cal", "setSelectionFromPosition( " + x + ", " + y + " ) day: " + day + " hour: "
435746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                + mSelectionHour + " mFirstCell: " + mFirstCell + " mFirstHourOffset: "
435846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                + mFirstHourOffset);
4359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//        if (mSelectedEvent != null) {
436046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//            Log.i("Cal", "  num events: " + mSelectedEvents.size() + " event: "
436146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                    + mSelectedEvent.title);
4362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//            for (Event ev : mSelectedEvents) {
4363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL
4364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                        | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
436546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                String timeRange = formatDateRange(mContext, ev.startMillis, ev.endMillis, flags);
4366146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//
4367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                Log.i("Cal", "  " + timeRange + " " + ev.title);
4368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//            }
4369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//        }
4370aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        sendAccessibilityEventAsNeeded(true);
4371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return true;
4372146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4373146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void findSelectedEvent(int x, int y) {
4375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int date = mSelectionDay;
4376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cellWidth = mCellWidth;
4377b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik        ArrayList<Event> events = mEvents;
4378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
4379e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay);
4380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int top = 0;
4381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = null;
4382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4383146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
43842fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionAllday) {
4385146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            float yDistance;
438646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            float minYdistance = 10000.0f; // any large number
4387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event closestEvent = null;
43882fda24534978f55711526dbde747aa50d68dec40RoboErik            float drawHeight = mAlldayHeight;
43893eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
43902fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount;
43912fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
43922fda24534978f55711526dbde747aa50d68dec40RoboErik                // Leave a gap for the 'box +n' text
43932fda24534978f55711526dbde747aa50d68dec40RoboErik                maxUnexpandedColumn--;
43942fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4395b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik            events = mAllDayEvents;
4396b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik            numEvents = events.size();
4397146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int i = 0; i < numEvents; i++) {
4398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event event = events.get(i);
43992fda24534978f55711526dbde747aa50d68dec40RoboErik                if (!event.drawAsAllday() ||
44002fda24534978f55711526dbde747aa50d68dec40RoboErik                        (!mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn)) {
44012fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Don't check non-allday events or events that aren't shown
4402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
4403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4405146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) {
4406b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik                    float numRectangles = mShowAllAllDayEvents ? mMaxAlldayEvents
4407b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik                            : mMaxUnexpandedAlldayEventCount;
4408146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float height = drawHeight / numRectangles;
4409f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                    if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
4410f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                        height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
4411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float eventTop = yOffset + height * event.getColumn();
4413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float eventBottom = eventTop + height;
4414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (eventTop < y && eventBottom > y) {
4415146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // If the touch is inside the event rectangle, then
4416146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // add the event.
4417146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        mSelectedEvents.add(event);
4418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        closestEvent = event;
4419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        break;
4420146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else {
4421146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Find the closest event
4422146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (eventTop >= y) {
4423146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            yDistance = eventTop - y;
4424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else {
4425146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            yDistance = y - eventBottom;
4426146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
4427146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (yDistance < minYdistance) {
4428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            minYdistance = yDistance;
4429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            closestEvent = event;
4430146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
4431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4433146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4434146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = closestEvent;
4435146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
4436146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4437146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4438146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Adjust y for the scrollable bitmap
4439146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        y += mViewStartY - mFirstCell;
4440146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4441146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Use a region around (x,y) for the selection region
4442146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect region = mRect;
4443146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.left = x - 10;
4444146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.right = x + 10;
4445146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.top = y - 10;
4446146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.bottom = y + 10;
4447146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        EventGeometry geometry = mEventGeometry;
4449146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4450146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
4451146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
4452146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Compute the event rectangle.
4453146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
4454146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
4455146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4456146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4457146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If the event intersects the selection region, then add it to
4458146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // mSelectedEvents.
4459146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (geometry.eventIntersectsSelection(event, region)) {
4460146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectedEvents.add(event);
4461146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4463146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If there are any events in the selected region, then assign the
4465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // closest one to mSelectedEvent.
4466146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvents.size() > 0) {
4467146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int len = mSelectedEvents.size();
4468146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event closestEvent = null;
446946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            float minDist = mViewWidth + mViewHeight; // some large distance
4470146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int index = 0; index < len; index++) {
4471146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event ev = mSelectedEvents.get(index);
4472146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                float dist = geometry.pointToEvent(x, y, ev);
4473146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (dist < minDist) {
4474146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    minDist = dist;
4475146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    closestEvent = ev;
4476146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4478146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = closestEvent;
4479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Keep the selected hour and day consistent with the selected
448146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // event. They could be different if we touched on an empty hour
448246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // slot very close to an event in the previous hour slot. In
4483146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // that case we will select the nearby event.
4484146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startDay = mSelectedEvent.startDay;
4485146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endDay = mSelectedEvent.endDay;
4486146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectionDay < startDay) {
4487146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionDay = startDay;
4488146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mSelectionDay > endDay) {
4489146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionDay = endDay;
4490146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4491146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4492146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startHour = mSelectedEvent.startTime / 60;
4493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endHour;
4494146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectedEvent.startTime < mSelectedEvent.endTime) {
4495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endHour = (mSelectedEvent.endTime - 1) / 60;
4496146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
4497146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endHour = mSelectedEvent.endTime / 60;
4498146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4499146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4500349dd91e8475a72eb68a5eae4482ce7e82c01d16RoboErik            if (mSelectionHour < startHour && mSelectionDay == startDay) {
4501146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionHour = startHour;
4502349dd91e8475a72eb68a5eae4482ce7e82c01d16RoboErik            } else if (mSelectionHour > endHour && mSelectionDay == endDay) {
4503146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionHour = endHour;
4504146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4505146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4507146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4508146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Encapsulates the code to continue the scrolling after the
450946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    // finger is lifted. Instead of stopping the scroll immediately,
4510146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // the scroll continues to "free spin" and gradually slows down.
4511146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private class ContinueScroll implements Runnable {
4512146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
45133f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            mScrolling = mScrolling && mScroller.computeScrollOffset();
45143f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            if (!mScrolling) {
45153f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                resetSelectedHour();
45163f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                invalidate();
45173f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                return;
4518146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4519146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
45203f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            mViewStartY = mScroller.getCurrY();
4521146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4522433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mCallEdgeEffectOnAbsorb) {
4523433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (mViewStartY < 0) {
4524433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectTop.onAbsorb((int) mLastVelocity);
4525433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mCallEdgeEffectOnAbsorb = false;
4526433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                } else if (mViewStartY > mMaxViewStartY) {
4527433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectBottom.onAbsorb((int) mLastVelocity);
4528433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mCallEdgeEffectOnAbsorb = false;
4529433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
4530433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mLastVelocity = mScroller.getCurrVelocity();
4531433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
4532433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
4533433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mScrollStartY == 0 || mScrollStartY == mMaxViewStartY) {
4534433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                // Allow overscroll/springback only on a fling,
4535433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                // not a pull/fling from the end
4536433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (mViewStartY < 0) {
4537433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mViewStartY = 0;
4538433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                } else if (mViewStartY > mMaxViewStartY) {
4539433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mViewStartY = mMaxViewStartY;
4540433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
4541146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4542146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4543146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeFirstHour();
45443f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            post(this);
4545146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
4546146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4547146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4548146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4549146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
4550bd0e834aea317236d574f8738fe11a51fdc87968Erik     * Cleanup the pop-up and timers.
4551146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
4552146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void cleanup() {
4553146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Protect against null-pointer exceptions
4554146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mPopup != null) {
4555146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPopup.dismiss();
4556146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4557666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
4558146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Handler handler = getHandler();
4559146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (handler != null) {
4560146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            handler.removeCallbacks(mDismissPopup);
4561bd0e834aea317236d574f8738fe11a51fdc87968Erik            handler.removeCallbacks(mUpdateCurrentTime);
4562146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4563e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4564d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT,
4565d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight);
4566d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
45677abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project        // Turn off redraw
45687abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project        mRemeasure = false;
4569146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4570146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4571bd0e834aea317236d574f8738fe11a51fdc87968Erik    /**
4572bd0e834aea317236d574f8738fe11a51fdc87968Erik     * Restart the update timer
4573bd0e834aea317236d574f8738fe11a51fdc87968Erik     */
4574bd0e834aea317236d574f8738fe11a51fdc87968Erik    public void restartCurrentTimeUpdates() {
4575bd0e834aea317236d574f8738fe11a51fdc87968Erik        post(mUpdateCurrentTime);
4576bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
4577bd0e834aea317236d574f8738fe11a51fdc87968Erik
457846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    @Override
457946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    protected void onDetachedFromWindow() {
4580146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cleanup();
4581146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        super.onDetachedFromWindow();
4582146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4583146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4584146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    class DismissPopup implements Runnable {
4585146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
4586146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Protect against null-pointer exceptions
4587146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mPopup != null) {
4588146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPopup.dismiss();
4589146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4590146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4591146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4592bd0e834aea317236d574f8738fe11a51fdc87968Erik
4593bd0e834aea317236d574f8738fe11a51fdc87968Erik    class UpdateCurrentTime implements Runnable {
4594bd0e834aea317236d574f8738fe11a51fdc87968Erik        public void run() {
4595bd0e834aea317236d574f8738fe11a51fdc87968Erik            long currentTime = System.currentTimeMillis();
4596bd0e834aea317236d574f8738fe11a51fdc87968Erik            mCurrentTime.set(currentTime);
4597bd0e834aea317236d574f8738fe11a51fdc87968Erik            //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.)
4598bd0e834aea317236d574f8738fe11a51fdc87968Erik            postDelayed(mUpdateCurrentTime,
4599bd0e834aea317236d574f8738fe11a51fdc87968Erik                    UPDATE_CURRENT_TIME_DELAY - (currentTime % UPDATE_CURRENT_TIME_DELAY));
4600bd0e834aea317236d574f8738fe11a51fdc87968Erik            mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
4601bd0e834aea317236d574f8738fe11a51fdc87968Erik            invalidate();
4602bd0e834aea317236d574f8738fe11a51fdc87968Erik        }
4603bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
4604ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4605ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
4606ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4607ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onSingleTapUp(MotionEvent ev) {
46089ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp");
4609d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doSingleTapUp(ev);
4610ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4611ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4612ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4613ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4614ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public void onLongPress(MotionEvent ev) {
46159ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress");
4616d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doLongPress(ev);
4617ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4618ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4619ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4620ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
46219ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onScroll");
46222fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mTouchStartedInAlldayArea) {
46230e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                if (Math.abs(distanceX) < Math.abs(distanceY)) {
46240e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                    return false;
46250e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                }
46260e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                // don't scroll vertically if this started in the allday area
46270e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                distanceY = 0;
46282fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4629d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doScroll(e1, e2, distanceX, distanceY);
4630ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4631ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4632ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4633ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4634ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
46359ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onFling");
46369ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
46372fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mTouchStartedInAlldayArea) {
46380e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                if (Math.abs(velocityX) < Math.abs(velocityY)) {
46390e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                    return false;
46400e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                }
46410e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                // don't fling vertically if this started in the allday area
46420e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                velocityY = 0;
46432fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4644d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doFling(e1, e2, velocityX, velocityY);
4645ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4646ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4647ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4648ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4649ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onDown(MotionEvent ev) {
46509ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onDown");
4651d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doDown(ev);
4652ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4653ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4654ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    }
4655cd405a28e6821b116dcf3c531805d0c90b6cc08cErik
4656cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    @Override
4657cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    public boolean onLongClick(View v) {
465843e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        int flags = DateUtils.FORMAT_SHOW_WEEKDAY;
465943e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        long time = getSelectedTimeInMillis();
466043e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        if (!mSelectionAllday) {
466143e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik            flags |= DateUtils.FORMAT_SHOW_TIME;
466243e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        }
466343e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        if (DateFormat.is24HourFormat(mContext)) {
466443e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik            flags |= DateUtils.FORMAT_24HOUR;
466543e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        }
466643e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        mLongPressTitle = Utils.formatDateRange(mContext, time, time, flags);
466720b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        new AlertDialog.Builder(mContext).setTitle(mLongPressTitle)
466820b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                .setItems(mLongPressItems, new DialogInterface.OnClickListener() {
466920b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                    @Override
467020b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                    public void onClick(DialogInterface dialog, int which) {
467120b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                        if (which == 0) {
4672edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            long extraLong = 0;
4673edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            if (mSelectionAllday) {
4674edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                                extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
4675edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            }
4676edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            mController.sendEventRelatedEventWithExtra(this,
4677edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                                    EventType.CREATE_EVENT, -1, getSelectedTimeInMillis(), 0, -1,
4678edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                                    -1, extraLong, -1);
467920b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                        }
468020b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                    }
468173bb3ae73b067e7c926d3ac660d059b2a06309b2RoboErik                }).show().setCanceledOnTouchOutside(true);
4682cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        return true;
4683cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    }
46842c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
46852c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    // The rest of this file was borrowed from Launcher2 - PagedView.java
46862c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private static final int MINIMUM_SNAP_VELOCITY = 2200;
46872c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
468895d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan    private class ScrollInterpolator implements Interpolator {
46892c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        public ScrollInterpolator() {
46902c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
46912c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
46922c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        public float getInterpolation(float t) {
46932c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            t -= 1.0f;
469495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            t = t * t * t * t * t + 1;
469595d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
469695d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            if ((1 - t) * mAnimationDistance < 1) {
469795d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan                cancelAnimation();
469895d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            }
469995d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
470095d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            return t;
47012c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
47022c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
47032c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
47042c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private long calculateDuration(float delta, float width, float velocity) {
47052c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        /*
47062c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * Here we compute a "distance" that will be used in the computation of
47072c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * the overall snap duration. This is a function of the actual distance
47082c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * that needs to be traveled; we keep this value close to half screen
47092c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * size in order to reduce the variance in snap duration as a function
47102c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * of the distance the page needs to travel.
47112c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         */
47122c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        final float halfScreenSize = width / 2;
47132c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distanceRatio = delta / width;
47142c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio);
47152c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration;
47162c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
47172c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        velocity = Math.abs(velocity);
47182c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
47192c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
47202c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        /*
47212c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * we want the page's snap velocity to approximately match the velocity
47222c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * at which the user flings, so we scale the duration by a value near to
47232c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to
47242c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * make it a little slower.
47252c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         */
47262c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        long duration = 6 * Math.round(1000 * Math.abs(distance / velocity));
47272c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        if (DEBUG) {
47282c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            Log.e(TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:"
47292c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + distanceRatio + " distance:" + distance + " velocity:" + velocity
47302c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + " duration:" + duration + " distanceInfluenceForSnapDuration:"
47312c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + distanceInfluenceForSnapDuration);
47322c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
47332c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        return duration;
47342c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
47352c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
47362c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    /*
47372c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * We want the duration of the page snap animation to be influenced by the
47382c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * distance that the screen has to travel, however, we don't want this
47392c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * duration to be effected in a purely linear fashion. Instead, we use this
47402c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * method to moderate the effect that the distance of travel has on the
47412c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * overall snap duration.
47422c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     */
47432c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private float distanceInfluenceForSnapDuration(float f) {
47442c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        f -= 0.5f; // center the values about 0.
47452c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        f *= 0.3f * Math.PI / 2.0f;
47462c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        return (float) Math.sin(f);
47472c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
4748146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project}
4749