DayView.java revision b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1
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;
27146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentResolver;
28146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentUris;
29146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Context;
30146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.res.Resources;
31146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.res.TypedArray;
32146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.database.Cursor;
3325996100d6ea61888ebe54d7708a9a371bb12a55RoboErikimport android.graphics.BitmapFactory;
34146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Canvas;
3525996100d6ea61888ebe54d7708a9a371bb12a55RoboErikimport android.graphics.Color;
36146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Paint;
37e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.graphics.Paint.Align;
3825996100d6ea61888ebe54d7708a9a371bb12a55RoboErikimport android.graphics.PorterDuff;
39dd95df57c8c5a58a85c4c0effad5652dec14f621Erikimport android.graphics.Paint.Style;
4025996100d6ea61888ebe54d7708a9a371bb12a55RoboErikimport android.graphics.Shader.TileMode;
41146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Rect;
42146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Typeface;
4325996100d6ea61888ebe54d7708a9a371bb12a55RoboErikimport android.graphics.drawable.BitmapDrawable;
44c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawaimport android.graphics.drawable.Drawable;
45146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.net.Uri;
46146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Handler;
47a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Attendees;
48a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Calendars;
49a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Events;
503e7015267340b09d142dfcd3ff8acda257711023Shachar Shemeshimport android.text.Layout.Alignment;
51e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.Spannable;
52e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.SpannableString;
53e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.SpannableStringBuilder;
54f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chanimport android.text.StaticLayout;
553e7015267340b09d142dfcd3ff8acda257711023Shachar Shemeshimport android.text.TextPaint;
56f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chanimport android.text.TextUtils;
57146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.DateFormat;
58146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.DateUtils;
59146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.Time;
60e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.style.StyleSpan;
61146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.util.Log;
62146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ContextMenu;
63dd95df57c8c5a58a85c4c0effad5652dec14f621Erikimport android.view.ContextMenu.ContextMenuInfo;
64ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.GestureDetector;
65146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.Gravity;
66146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.KeyEvent;
67146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.LayoutInflater;
68146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.MenuItem;
69146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.MotionEvent;
70d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chanimport android.view.ScaleGestureDetector;
71146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.View;
72146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ViewConfiguration;
73146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ViewGroup;
74146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.WindowManager;
75259b8f9a21fffd3858218581e9daaf689b6b888fErikimport android.view.accessibility.AccessibilityEvent;
7646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.view.animation.AccelerateDecelerateInterpolator;
77ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.animation.Animation;
782c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chanimport android.view.animation.Interpolator;
79ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.animation.TranslateAnimation;
80146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.ImageView;
813f6328f579b05c02606572c777541b97aa04f9f3Michael Chanimport android.widget.OverScroller;
82146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.PopupWindow;
83146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.TextView;
84ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.widget.ViewSwitcher;
85146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
86146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport java.util.ArrayList;
87c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawaimport java.util.Arrays;
88146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport java.util.Calendar;
89259b8f9a21fffd3858218581e9daaf689b6b888fErikimport java.util.Formatter;
90b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErikimport java.util.Iterator;
91259b8f9a21fffd3858218581e9daaf689b6b888fErikimport java.util.Locale;
920d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chanimport java.util.regex.Matcher;
930d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chanimport java.util.regex.Pattern;
94146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
95146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/**
96d190418220467c02327dd9b3ae1eae238889720bMichael Chan * View for multi-day view. So far only 1 and 7 day have been tested.
97d190418220467c02327dd9b3ae1eae238889720bMichael Chan */
98d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chanpublic class DayView extends View implements View.OnCreateContextMenuListener,
99cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener
100cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        {
101d190418220467c02327dd9b3ae1eae238889720bMichael Chan    private static String TAG = "DayView";
102d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static boolean DEBUG = false;
103259b8f9a21fffd3858218581e9daaf689b6b888fErik    private static final String HOUR_FORMAT_12H = "%A %I%p";
104259b8f9a21fffd3858218581e9daaf689b6b888fErik    private static final String HOUR_FORMAT_24H = "%A %H";
105259b8f9a21fffd3858218581e9daaf689b6b888fErik    private static final String PERIOD_SPACE = ". ";
106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
107705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan    private static float mScale = 0; // Used for supporting different screen densities
108666fc5e8ddbac15c951aad738bd818ee307eae42Erik    private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event
1092fda24534978f55711526dbde747aa50d68dec40RoboErik    // Duration of the allday expansion
110ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private static final long ANIMATION_DURATION = 400;
1112fda24534978f55711526dbde747aa50d68dec40RoboErik    // duration of the more allday event text fade
1122fda24534978f55711526dbde747aa50d68dec40RoboErik    private static final long ANIMATION_SECONDARY_DURATION = 200;
113705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
1144484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_AGENDA = 2;
1154484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_DAY = 3;
1164484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_VIEW = 5;
1174484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_CREATE = 6;
1184484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_EDIT = 7;
1194484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_DELETE = 8;
1204484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan
12111ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan    private static int DEFAULT_CELL_HEIGHT = 64;
122d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int MAX_CELL_HEIGHT = 150;
123d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int MIN_Y_SPAN = 100;
124d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
125146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mOnFlingCalled;
1264f58287a1e02325337e39073a50bbb880094099fRoboErik    private boolean mStartingScroll = false;
127666fc5e8ddbac15c951aad738bd818ee307eae42Erik    /**
128666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * ID of the last event which was displayed with the toast popup.
129666fc5e8ddbac15c951aad738bd818ee307eae42Erik     *
130666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * This is used to prevent popping up multiple quick views for the same event, especially
131666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * during calendar syncs. This becomes valid when an event is selected, either by default
132666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * on starting calendar or by scrolling to an event. It becomes invalid when the user
133666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * explicitly scrolls to an empty time slot, changes views, or deletes the event.
134666fc5e8ddbac15c951aad738bd818ee307eae42Erik     */
135666fc5e8ddbac15c951aad738bd818ee307eae42Erik    private long mLastPopupEventID;
136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
137ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    protected Context mContext;
138146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String[] CALENDARS_PROJECTION = new String[] {
140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Calendars._ID,          // 0
141ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        Calendars.CALENDAR_ACCESS_LEVEL, // 1
1426d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        Calendars.OWNER_ACCOUNT, // 2
143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int CALENDARS_INDEX_ACCESS_LEVEL = 1;
1456d34cec523930368976bc8d358d0a6de1da3462cMichael Chan    private static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String CALENDARS_WHERE = Calendars._ID + "=%d";
147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String[] ATTENDEES_PROJECTION = new String[] {
149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Attendees._ID,                      // 0
150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Attendees.ATTENDEE_RELATIONSHIP,    // 1
151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int ATTENDEES_INDEX_RELATIONSHIP = 1;
153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=%d";
154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_NONE = 0;
156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_ABOVE = 1;
157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_BELOW = 2;
158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_LEFT = 4;
159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_RIGHT = 8;
160146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
161a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_NONE = 0;
162a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_DELETE = 1;
163a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_EDIT = 2;
164a505627659f6bfba8869eac8089f7030f2d0d92cErik
165d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan    private static int mHorizontalSnapBackThreshold = 128;
166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ContinueScroll mContinueScroll = new ContinueScroll();
168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Make this visible within the package for more informative debugging
170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Time mBaseDate;
171bd0e834aea317236d574f8738fe11a51fdc87968Erik    private Time mCurrentTime;
172bd0e834aea317236d574f8738fe11a51fdc87968Erik    //Update the current time line every five minutes if the window is left open that long
173bd0e834aea317236d574f8738fe11a51fdc87968Erik    private static final int UPDATE_CURRENT_TIME_DELAY = 300000;
174bd0e834aea317236d574f8738fe11a51fdc87968Erik    private UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime();
175bd0e834aea317236d574f8738fe11a51fdc87968Erik    private int mTodayJulianDay;
176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Typeface mBold = Typeface.DEFAULT_BOLD;
178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstJulianDay;
179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mLastJulianDay;
180146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
181146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mMonthLength;
182487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstVisibleDate;
183487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstVisibleDayOfWeek;
184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int[] mEarliestStartHour;    // indexed by the week day offset
185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean[] mHasAllDayEvent;   // indexed by the week day offset
1868f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private String mAllDayString;
187259b8f9a21fffd3858218581e9daaf689b6b888fErik    private String mEventCountTemplate;
188259b8f9a21fffd3858218581e9daaf689b6b888fErik
189259b8f9a21fffd3858218581e9daaf689b6b888fErik    protected static StringBuilder mStringBuilder = new StringBuilder(50);
190259b8f9a21fffd3858218581e9daaf689b6b888fErik    // TODO recreate formatter when locale changes
191259b8f9a21fffd3858218581e9daaf689b6b888fErik    protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
193731f1cb808b7586b93615b408ab2636081fab0dcErik    private Runnable mTZUpdater = new Runnable() {
194731f1cb808b7586b93615b408ab2636081fab0dcErik        @Override
195731f1cb808b7586b93615b408ab2636081fab0dcErik        public void run() {
196731f1cb808b7586b93615b408ab2636081fab0dcErik            String tz = Utils.getTimeZone(mContext, this);
197731f1cb808b7586b93615b408ab2636081fab0dcErik            mBaseDate.timezone = tz;
198731f1cb808b7586b93615b408ab2636081fab0dcErik            mBaseDate.normalize(true);
199731f1cb808b7586b93615b408ab2636081fab0dcErik            mCurrentTime.switchTimezone(tz);
200731f1cb808b7586b93615b408ab2636081fab0dcErik            invalidate();
201731f1cb808b7586b93615b408ab2636081fab0dcErik        }
202731f1cb808b7586b93615b408ab2636081fab0dcErik    };
203731f1cb808b7586b93615b408ab2636081fab0dcErik
20446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() {
20546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
20646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationStart(Animator animation) {
20746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = true;
20846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
20946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
21046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
21146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationCancel(Animator animation) {
21246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = false;
21346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
21446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
21546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
21646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationEnd(Animator animation) {
21746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = false;
21846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            resetSelectedHour();
21946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            invalidate();
22046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
22146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    };
22246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
223146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * This variable helps to avoid unnecessarily reloading events by keeping
225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * track of the start millis parameter used for the most recent loading
226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * of events.  If the next reload matches this, then the events are not
227146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * reloaded.  To force a reload, set this to zero (this is set to zero
228146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * in the method clearCachedEvents()).
229146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
230146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private long mLastReloadMillis;
231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
232146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ArrayList<Event> mEvents = new ArrayList<Event>();
23308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private ArrayList<Event> mAllDayEvents = new ArrayList<Event>();
23408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout[] mLayouts = null;
23508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout[] mAllDayLayouts = null;
236e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private StaticLayout mAllDayTextLayout = null;
237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionDay;        // Julian day
238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionHour;
239146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2402fda24534978f55711526dbde747aa50d68dec40RoboErik    boolean mSelectionAllday;
241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2423eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    /** Width of a day or non-conflicting event */
243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mCellWidth;
244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
245146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Pre-allocate these objects and re-use them
246146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect mRect = new Rect();
247146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect mDestRect = new Rect();
248259b8f9a21fffd3858218581e9daaf689b6b888fErik    private Rect mSelectionRect = new Rect();
2492fda24534978f55711526dbde747aa50d68dec40RoboErik    // This encloses the more allDay events icon
2502fda24534978f55711526dbde747aa50d68dec40RoboErik    private Rect mExpandAllDayRect = new Rect();
251e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    // TODO Clean up paint usage
252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Paint mPaint = new Paint();
253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Paint mEventTextPaint = new Paint();
254146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Paint mSelectionPaint = new Paint();
25502041687c35441e8a21cb763f1fc3a26525292efDavid Brown    private float[] mLines;
256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
257487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstDayOfWeek; // First day of the week
258146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private PopupWindow mPopup;
260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private View mPopupView;
261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // The number of milliseconds to show the popup window
263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int POPUP_DISMISS_DELAY = 3000;
264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private DismissPopup mDismissPopup = new DismissPopup();
265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mRemeasure = true;
267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private final EventLoader mEventLoader;
269146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected final EventGeometry mEventGeometry;
270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
271e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float GRID_LINE_LEFT_MARGIN = 0;
272000ba1109e2f4e527e9227fe341598ceeb3fbca6Michael Chan    private static final float GRID_LINE_INNER_WIDTH = 1;
2733eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
274146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int DAY_GAP = 1;
275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int HOUR_GAP = 1;
2762fda24534978f55711526dbde747aa50d68dec40RoboErik    // This is the standard height of an allday event with no restrictions
2779c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    private static int SINGLE_ALLDAY_HEIGHT = 34;
2782fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
2792fda24534978f55711526dbde747aa50d68dec40RoboErik    * This is the minimum desired height of a allday event.
2802fda24534978f55711526dbde747aa50d68dec40RoboErik    * When unexpanded, allday events will use this height.
2812fda24534978f55711526dbde747aa50d68dec40RoboErik    * When expanded allDay events will attempt to grow to fit all
2822fda24534978f55711526dbde747aa50d68dec40RoboErik    * events at this height.
2832fda24534978f55711526dbde747aa50d68dec40RoboErik    */
2842fda24534978f55711526dbde747aa50d68dec40RoboErik    private static float MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0F; // in pixels
2852fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
2862fda24534978f55711526dbde747aa50d68dec40RoboErik     * This is how big the unexpanded allday height is allowed to be.
2872fda24534978f55711526dbde747aa50d68dec40RoboErik     * It will get adjusted based on screen size
2882fda24534978f55711526dbde747aa50d68dec40RoboErik     */
2892fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int MAX_UNEXPANDED_ALLDAY_HEIGHT =
2902fda24534978f55711526dbde747aa50d68dec40RoboErik            (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
2912fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
2922fda24534978f55711526dbde747aa50d68dec40RoboErik     * This is the minimum size reserved for displaying regular events.
2932fda24534978f55711526dbde747aa50d68dec40RoboErik     * The expanded allDay region can't expand into this.
2942fda24534978f55711526dbde747aa50d68dec40RoboErik     */
2952fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int MIN_HOURS_HEIGHT = 180;
29625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int ALLDAY_TOP_MARGIN = 1;
2972fda24534978f55711526dbde747aa50d68dec40RoboErik    // The largest a single allDay event will become.
298f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34;
299e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3008f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_TOP_MARGIN = 2;
301e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int HOURS_LEFT_MARGIN = 2;
3028f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_RIGHT_MARGIN = 4;
3038f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
305bd0e834aea317236d574f8738fe11a51fdc87968Erik    private static int CURRENT_TIME_LINE_HEIGHT = 2;
306bd0e834aea317236d574f8738fe11a51fdc87968Erik    private static int CURRENT_TIME_LINE_BORDER_WIDTH = 1;
3071ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static final int CURRENT_TIME_LINE_SIDE_BUFFER = 2;
308bd0e834aea317236d574f8738fe11a51fdc87968Erik
309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MINUTES_PER_HOUR = 60;
310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24;
311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000;
312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000);
313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24;
314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
31525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static final int DECLINED_ALPHA = 0x66000000;
316f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan    private static final int DATE_HEADER_ALPHA = 0x26000000;
317f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan    private static final int DATE_HEADER_TODAY_ALPHA = 0x99000000;
3182fda24534978f55711526dbde747aa50d68dec40RoboErik    // More events text will transition between invisible and this alpha
3192fda24534978f55711526dbde747aa50d68dec40RoboErik    private static final int MORE_EVENTS_MAX_ALPHA = 0x4C;
320e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0;
321e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5;
322e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6;
323e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_LEFT_MARGIN = 5;
324e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_RIGHT_MARGIN = 4;
325e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_BOTTOM_MARGIN = 3;
3263eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float DAY_HEADER_FONT_SIZE = 14;
3278f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static float DATE_HEADER_FONT_SIZE = 32;
3283eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float NORMAL_FONT_SIZE = 12;
3293eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float EVENT_TEXT_FONT_SIZE = 12;
330e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float HOURS_TEXT_SIZE = 12;
331e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float ALLDAY_TEXT_SIZE = 12;
332e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float AMPM_TEXT_SIZE = 9;
3338f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int MIN_HOURS_WIDTH = 96;
334705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan    private static int MIN_CELL_WIDTH_FOR_TEXT = 27;
335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int MAX_EVENT_TEXT_LEN = 500;
3362fda24534978f55711526dbde747aa50d68dec40RoboErik    // smallest height to draw an event with
33725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static float MIN_EVENT_HEIGHT = 24.0F; // in pixels
3381ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int CALENDAR_COLOR_SQUARE_SIZE = 10;
33925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_TOP_MARGIN = 1;
34025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_BOTTOM_MARGIN = 1;
34125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_LEFT_MARGIN = 2;
34225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_RIGHT_MARGIN = 1;
34325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_STROKE_WIDTH = 2;
3441ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_TEXT_TOP_MARGIN = 2;
34525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_TEXT_BOTTOM_MARGIN = 2;
34625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_TEXT_LEFT_MARGIN = 2;
34725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_TEXT_RIGHT_MARGIN = 2;
348e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1;
3491ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN;
3501ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN;
3511ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
3521ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN;
3532fda24534978f55711526dbde747aa50d68dec40RoboErik    // margins and sizing for the expand allday icon
354cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik    private static int EXPAND_ALL_DAY_BOTTOM_MARGIN = 10;
3552fda24534978f55711526dbde747aa50d68dec40RoboErik    // sizing for "box +n" in allDay events
3562fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int EVENT_SQUARE_WIDTH = 10;
3572fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int EVENT_LINE_PADDING = 4;
358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static int mPressedColor;
360146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static int mEventTextColor;
3612fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int mMoreEventsTextColor;
362f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan
36364b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok    private static int mWeek_saturdayColor;
36464b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok    private static int mWeek_sundayColor;
365d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan    private static int mCalendarDateBannerTextColor;
366d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan    private static int mCalendarAmPmLabel;
367ba902466b19b11d587468e354edc58e542707747Michael Chan    private static int mCalendarGridAreaSelected;
3683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int mCalendarGridLineInnerHorizontalColor;
3693eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int mCalendarGridLineInnerVerticalColor;
370e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int mTodayBgColor;
371e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int mBgColor;
3728f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int mCalendarHourLabelColor;
3732fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA;
374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
37595d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan    private float mAnimationDistance = 0;
376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewStartX;
377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewStartY;
378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mMaxViewStartY;
379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewHeight;
380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewWidth;
38146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    private int mGridAreaHeight = -1;
382d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int mCellHeight = 0; // shared among all DayViews
383d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int mMinCellHeight = 32;
384146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mScrollStartY;
385146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mPreviousDirection;
386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
387d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /**
388d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * Vertical distance or span between the two touch points at the start of a
389d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * scaling gesture
390d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     */
391d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private float mStartingSpanY = 0;
392d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /** Height of 1 hour in pixels at the start of a scaling gesture */
393d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private int mCellHeightBeforeScaleGesture;
394d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /** The hour at the center two touch points */
395d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private float mGestureCenterHour = 0;
396d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /**
397d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * Flag to decide whether to handle the up event. Cases where up events
398d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * should be ignored are 1) right after a scale gesture and 2) finger was
399d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * down before app launch
400d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     */
401d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private boolean mHandleActionUp = true;
402d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mHoursTextHeight;
4042fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4052fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the area used for allday events
4062fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4072fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAlldayHeight;
4082fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4092fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the allday event area used during animation
4102fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4112fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAnimateDayHeight = 0;
4122fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4132fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of an individual allday event during animation
4142fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4152fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
4162fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
417cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik     * Whether to use the expand or collapse icon.
4182fda24534978f55711526dbde747aa50d68dec40RoboErik     */
41997326ee1ea9b1bb17e0e2bcfe344352d4bb03001RoboErik    private static boolean mUseExpandIcon = true;
4202fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4212fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the day names/numbers
4222fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4233eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int DAY_HEADER_HEIGHT = 45;
424f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    /**
425e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * The height of the day names/numbers for multi-day views
426e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
427e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
428e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
429e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * The height of the day names/numbers when viewing a single day
430e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
431e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
432e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
433f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan     * Max of all day events in a given day in this view.
434f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan     */
4352fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mMaxAlldayEvents;
4362fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4372fda24534978f55711526dbde747aa50d68dec40RoboErik     * A count of the number of allday events that were not drawn for each day
4382fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4392fda24534978f55711526dbde747aa50d68dec40RoboErik    private int[] mSkippedAlldayEvents;
4402fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4412fda24534978f55711526dbde747aa50d68dec40RoboErik     * The number of allDay events at which point we start hiding allDay events.
4422fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4432fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mMaxUnexpandedAlldayEventCount = 4;
4442fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4452fda24534978f55711526dbde747aa50d68dec40RoboErik     * Whether or not to expand the allDay area to fill the screen
4462fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4472fda24534978f55711526dbde747aa50d68dec40RoboErik    private static boolean mShowAllAllDayEvents = false;
448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
449146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected int mNumDays = 7;
450146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mNumHours = 10;
4513eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
4523eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    /** Width of the time line (list of hours) to the left. */
453146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mHoursWidth;
454146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mDateStrWidth;
45546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** Top of the scrollable region i.e. below date labels and all day events */
456146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstCell;
45746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** First fully visibile hour */
458146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstHour = -1;
45946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** Distance between the mFirstCell and the top of first fully visible hour. */
460146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstHourOffset;
461146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mHourStrs;
462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mDayStrs;
463146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mDayStrs2Letter;
464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mIs24HourFormat;
465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
466146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ArrayList<Event> mSelectedEvents = new ArrayList<Event>();
467146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mComputeSelectedEvents;
4688307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik    private boolean mUpdateToast;
469146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Event mSelectedEvent;
470146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Event mPrevSelectedEvent;
471146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect mPrevBox = new Rect();
472146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected final Resources mResources;
4733eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    protected final Drawable mCurrentTimeLine;
4743eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    protected final Drawable mTodayHeaderDrawable;
4752fda24534978f55711526dbde747aa50d68dec40RoboErik    protected final Drawable mExpandAlldayDrawable;
476cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik    protected final Drawable mCollapseAlldayDrawable;
477f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan    protected Drawable mAcceptedOrTentativeEventBoxDrawable;
47825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    protected BitmapDrawable mDeclinedBgDrawable;
479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String mAmString;
480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String mPmString;
481146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private DeleteEventHelper mDeleteEventHelper;
4826d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private static int sCounter = 0;
483146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
484146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ContextMenuHandler mContextMenuHandler = new ContextMenuHandler();
485146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
486d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    ScaleGestureDetector mScaleGestureDetector;
487d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
488146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
489146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The initial state of the touch mode when we enter this view.
490146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
491146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_INITIAL_STATE = 0;
492146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
494146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates we just received the touch event and we are waiting to see if
495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * it is a tap or a scroll gesture.
496146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
497146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_DOWN = 1;
498146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
499146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
500146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates the touch gesture is a vertical scroll
501146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
502146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_VSCROLL = 0x20;
503146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
504146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
505146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates the touch gesture is a horizontal scroll
506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
507146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_HSCROLL = 0x40;
508146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
509146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mTouchMode = TOUCH_MODE_INITIAL_STATE;
510146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
511146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
512146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS.
513146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
514146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_HIDDEN = 0;
515ef2185bf0c7a80154a79788915db04d4f9e7ad4dMichael Chan    private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet
516146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_SELECTED = 2;
517146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_LONGPRESS = 3;
518146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
519146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionMode = SELECTION_HIDDEN;
520146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
521146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mScrolling = false;
522146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
5234f58287a1e02325337e39073a50bbb880094099fRoboErik    private float mInitialScrollX;
5244f58287a1e02325337e39073a50bbb880094099fRoboErik    private float mInitialScrollY;
5252fda24534978f55711526dbde747aa50d68dec40RoboErik
5262fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the height of the allday region
5272fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mAlldayAnimator;
5282fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the height of events in the allday region
5292fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mAlldayEventAnimator;
5302fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the transparency of the more events text
5312fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mMoreAlldayEventsAnimator;
5322fda24534978f55711526dbde747aa50d68dec40RoboErik    // whether or not an event is stopping because it was cancelled
5332fda24534978f55711526dbde747aa50d68dec40RoboErik    private boolean mCancellingAnimations = false;
5342fda24534978f55711526dbde747aa50d68dec40RoboErik    // tracks whether a touch originated in the allday area
5352fda24534978f55711526dbde747aa50d68dec40RoboErik    private boolean mTouchStartedInAlldayArea = false;
5362fda24534978f55711526dbde747aa50d68dec40RoboErik
537ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private CalendarController mController;
538ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private ViewSwitcher mViewSwitcher;
539ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private GestureDetector mGestureDetector;
5403f6328f579b05c02606572c777541b97aa04f9f3Michael Chan    private OverScroller mScroller;
5412c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private ScrollInterpolator mHScrollInterpolator;
542705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
543d190418220467c02327dd9b3ae1eae238889720bMichael Chan    public DayView(Context context, CalendarController controller,
54475d9b567e1fd3b9c6945cd0afe28781f5a19c6c8Michael Chan            ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) {
545ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        super(context);
5462fda24534978f55711526dbde747aa50d68dec40RoboErik        mResources = context.getResources();
54709b1b2e1e7eb9bc291d4e70c993471d3c9ffa799Michael Chan
548e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DATE_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.date_header_text_size);
549e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.day_label_text_size);
550e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        ONE_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.one_day_header_height);
551e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.day_header_bottom_margin);
552e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        EXPAND_ALL_DAY_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.all_day_bottom_margin);
553e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_TEXT_SIZE = (int) mResources.getDimension(R.dimen.hours_text_size);
554e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        AMPM_TEXT_SIZE = (int) mResources.getDimension(R.dimen.ampm_text_size);
555e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        ALLDAY_TEXT_SIZE = (int) mResources.getDimension(R.dimen.allday_text_size);
556e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        MIN_HOURS_WIDTH = (int) mResources.getDimension(R.dimen.min_hours_width);
557e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_LEFT_MARGIN = (int) mResources.getDimension(R.dimen.hours_left_margin);
558e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_RIGHT_MARGIN = (int) mResources.getDimension(R.dimen.hours_right_margin);
559e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        MULTI_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.day_header_height);
56009b1b2e1e7eb9bc291d4e70c993471d3c9ffa799Michael Chan
561705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan        if (mScale == 0) {
562e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
5632fda24534978f55711526dbde747aa50d68dec40RoboErik            mScale = mResources.getDisplayMetrics().density;
564705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan            if (mScale != 1) {
565705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                SINGLE_ALLDAY_HEIGHT *= mScale;
566705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                ALLDAY_TOP_MARGIN *= mScale;
567f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale;
568705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
569705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                NORMAL_FONT_SIZE *= mScale;
570705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                EVENT_TEXT_FONT_SIZE *= mScale;
5718f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan                GRID_LINE_LEFT_MARGIN *= mScale;
5728f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan                HOURS_TOP_MARGIN *= mScale;
573705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                MIN_CELL_WIDTH_FOR_TEXT *= mScale;
574705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                MIN_EVENT_HEIGHT *= mScale;
5752fda24534978f55711526dbde747aa50d68dec40RoboErik                MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT *= mScale;
5762fda24534978f55711526dbde747aa50d68dec40RoboErik                MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale;
5772fda24534978f55711526dbde747aa50d68dec40RoboErik                mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
578705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
579bd0e834aea317236d574f8738fe11a51fdc87968Erik                CURRENT_TIME_LINE_HEIGHT *= mScale;
580bd0e834aea317236d574f8738fe11a51fdc87968Erik                CURRENT_TIME_LINE_BORDER_WIDTH *= mScale;
581bd0e834aea317236d574f8738fe11a51fdc87968Erik
582d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                MIN_Y_SPAN *= mScale;
583d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                MAX_CELL_HEIGHT *= mScale;
584697157470a6f0d8e7b806af9b4fb7fbe0bccae87Erik                DEFAULT_CELL_HEIGHT *= mScale;
5853eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_HEIGHT *= mScale;
5863eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_LEFT_MARGIN *= mScale;
5873eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_RIGHT_MARGIN *= mScale;
5883eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale;
5893eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale;
5903eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale;
5913eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                CALENDAR_COLOR_SQUARE_SIZE *= mScale;
5923eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                EVENT_TEXT_TOP_MARGIN *= mScale;
5933eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                EVENT_TEXT_BOTTOM_MARGIN *= mScale;
5943eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                EVENT_TEXT_LEFT_MARGIN *= mScale;
5953eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                EVENT_TEXT_RIGHT_MARGIN *= mScale;
5969c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan                EVENT_ALL_DAY_TEXT_TOP_MARGIN *= mScale;
5979c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan                EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN *= mScale;
5989c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan                EVENT_ALL_DAY_TEXT_LEFT_MARGIN *= mScale;
5999c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan                EVENT_ALL_DAY_TEXT_RIGHT_MARGIN *= mScale;
60011ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_TOP_MARGIN *= mScale;
60111ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_BOTTOM_MARGIN *= mScale;
60225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale;
60311ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_LEFT_MARGIN *= mScale;
60411ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_RIGHT_MARGIN *= mScale;
60525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                EVENT_RECT_STROKE_WIDTH *= mScale;
606cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                EXPAND_ALL_DAY_BOTTOM_MARGIN *= mScale;
6072fda24534978f55711526dbde747aa50d68dec40RoboErik                EVENT_SQUARE_WIDTH *= mScale;
6082fda24534978f55711526dbde747aa50d68dec40RoboErik                EVENT_LINE_PADDING *= mScale;
609705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan            }
610705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan        }
611e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
612e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_HEIGHT = numDays == 1 ? ONE_DAY_HEADER_HEIGHT : MULTI_DAY_HEADER_HEIGHT;
613705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
6143eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_week_holo_light);
6153eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light);
616cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik        mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_allday_expand_holo_light);
617cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik        mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_allday_collapse_holo_light);
618f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        mAcceptedOrTentativeEventBoxDrawable = mResources
619f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan                .getDrawable(R.drawable.panel_month_event_holo_light);
62025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik
62125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        mDeclinedBgDrawable = new BitmapDrawable(mResources, BitmapFactory.decodeResource(
62225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                mResources, R.drawable.event_bg_declined));
62325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        mDeclinedBgDrawable.setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
62425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik
625ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mEventLoader = eventLoader;
626146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry = new EventGeometry();
627146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT);
628146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setHourGap(HOUR_GAP);
629e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mEventGeometry.setCellMargin(DAY_GAP);
630ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mContext = context;
6318f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mAllDayString = mContext.getString(R.string.edit_event_all_day_label);
632ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mDeleteEventHelper = new DeleteEventHelper(context, null, false /* don't exit when done */);
633666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
634ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mController = controller;
635ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher = viewSwitcher;
636ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mGestureDetector = new GestureDetector(context, new CalendarGestureListener());
637d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mScaleGestureDetector = new ScaleGestureDetector(getContext(), this);
63875d9b567e1fd3b9c6945cd0afe28781f5a19c6c8Michael Chan        mNumDays = numDays;
639d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mCellHeight == 0) {
640d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = Utils.getSharedPreference(mContext,
641d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT);
642d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
6433f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScroller = new OverScroller(context);
6442c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        mHScrollInterpolator = new ScrollInterpolator();
645ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        init(context);
646146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
647146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void init(Context context) {
649146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setFocusable(true);
650146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
651146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Allow focus in touch mode so that we can do keyboard shortcuts
652146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // even after we've entered touch mode.
653146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setFocusableInTouchMode(true);
654146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setClickable(true);
655146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setOnCreateContextMenuListener(this);
656146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
657487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstDayOfWeek = Utils.getFirstDayOfWeek(context);
658146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
659731f1cb808b7586b93615b408ab2636081fab0dcErik        mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater));
660bd0e834aea317236d574f8738fe11a51fdc87968Erik        long currentTime = System.currentTimeMillis();
661bd0e834aea317236d574f8738fe11a51fdc87968Erik        mCurrentTime.set(currentTime);
662bd0e834aea317236d574f8738fe11a51fdc87968Erik        //The % makes it go off at the next increment of 5 minutes.
663bd0e834aea317236d574f8738fe11a51fdc87968Erik        postDelayed(mUpdateCurrentTime,
664bd0e834aea317236d574f8738fe11a51fdc87968Erik                UPDATE_CURRENT_TIME_DELAY - (currentTime % UPDATE_CURRENT_TIME_DELAY));
665bd0e834aea317236d574f8738fe11a51fdc87968Erik        mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
666bd0e834aea317236d574f8738fe11a51fdc87968Erik
66764b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok        mWeek_saturdayColor = mResources.getColor(R.color.week_saturday);
66864b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok        mWeek_sundayColor = mResources.getColor(R.color.week_sunday);
669d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color);
670e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mTodayBgColor = mResources.getColor(R.color.calendar_today_bg_color);
671e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mBgColor = mResources.getColor(R.color.calendar_hour_background);
672d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label);
673ba902466b19b11d587468e354edc58e542707747Michael Chan        mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected);
674e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mCalendarGridLineInnerHorizontalColor = mResources
675e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                .getColor(R.color.calendar_grid_line_inner_horizontal_color);
676e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mCalendarGridLineInnerVerticalColor = mResources
677e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                .getColor(R.color.calendar_grid_line_inner_vertical_color);
6788f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label);
679146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPressedColor = mResources.getColor(R.color.pressed);
680146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextColor = mResources.getColor(R.color.calendar_event_text_color);
681e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color);
68211638ad4fb56b978770aebe75deac7a0e6ac60d2Erik
683146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE);
684146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setTextAlign(Paint.Align.LEFT);
685146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setAntiAlias(true);
686146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
687146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color);
688146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mSelectionPaint;
689146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setColor(gridLineColor);
6903937a47bc5a1afb9701e1d0565fae31f5800590dErik        p.setStyle(Style.FILL);
691146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(false);
692146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
693146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p = mPaint;
694146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
695146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
696146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Allocate space for 2 weeks worth of weekday names so that we can
697146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // easily start the week display at any week day.
698146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDayStrs = new String[14];
699146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
700146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Also create an array of 2-letter abbreviations.
701146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDayStrs2Letter = new String[14];
702146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
703146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
704146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int index = i - Calendar.SUNDAY;
705146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // e.g. Tue for Tuesday
706e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM)
707e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    .toUpperCase();
708146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mDayStrs[index + 7] = mDayStrs[index];
709146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // e.g. Tu for Tuesday
710e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT)
711e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    .toUpperCase();
71252b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer
71352b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            // If we don't have 2-letter day strings, fall back to 1-letter.
71452b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            if (mDayStrs2Letter[index].equals(mDayStrs[index])) {
71552b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer                mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST);
71652b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            }
71752b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer
718146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mDayStrs2Letter[index + 7] = mDayStrs2Letter[index];
719146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
720146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
721146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Figure out how much space we need for the 3-letter abbrev names
722146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // in the worst case.
7233eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextSize(DATE_HEADER_FONT_SIZE);
724146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(mBold);
725146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] dateStrs = {" 28", " 30"};
726146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDateStrWidth = computeMaxStringWidth(0, dateStrs, p);
7273eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextSize(DAY_HEADER_FONT_SIZE);
728146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p);
729146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
730e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
731146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(null);
73261a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan        handleOnResume();
733146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
734e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase();
735e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase();
736146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] ampm = {mAmString, mPmString};
737e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(AMPM_TEXT_SIZE);
738e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mHoursWidth = Math.max(HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p)
739e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                + HOURS_RIGHT_MARGIN);
7408f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth);
741146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
742146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        LayoutInflater inflater;
743146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
744146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView = inflater.inflate(R.layout.bubble_event, null);
745146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView.setLayoutParams(new ViewGroup.LayoutParams(
746df279e3f9d5b0f189f812c52aca78d18178e4445Romain Guy                ViewGroup.LayoutParams.MATCH_PARENT,
747146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                ViewGroup.LayoutParams.WRAP_CONTENT));
748146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup = new PopupWindow(context);
749146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setContentView(mPopupView);
750146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Resources.Theme dialogTheme = getResources().newTheme();
751146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        dialogTheme.applyStyle(android.R.style.Theme_Dialog, true);
752146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] {
753146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            android.R.attr.windowBackground });
754146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setBackgroundDrawable(ta.getDrawable(0));
755146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ta.recycle();
756146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
757146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Enable touching the popup window
758146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView.setOnClickListener(this);
759cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        // Catch long clicks for creating a new event
760cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        setOnLongClickListener(this);
761146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
762731f1cb808b7586b93615b408ab2636081fab0dcErik        mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater));
763146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = System.currentTimeMillis();
764146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mBaseDate.set(millis);
765146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
766146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEarliestStartHour = new int[mNumDays];
767146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mHasAllDayEvent = new boolean[mNumDays];
76802041687c35441e8a21cb763f1fc3a26525292efDavid Brown
76902041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // mLines is the array of points used with Canvas.drawLines() in
77002041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // drawGridBackground() and drawAllDayEvents().  Its size depends
77102041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // on the max number of lines that can ever be drawn by any single
77202041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // drawLines() call in either of those methods.
77302041687c35441e8a21cb763f1fc3a26525292efDavid Brown        final int maxGridLines = (24 + 1)  // max horizontal lines we might draw
774e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                + (mNumDays + 1); // max vertical lines we might draw
77502041687c35441e8a21cb763f1fc3a26525292efDavid Brown        mLines = new float[maxGridLines * 4];
776146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
777146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
778146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
779146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * This is called when the popup window is pressed.
780146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
781146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onClick(View v) {
782146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (v == mPopupView) {
783146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Pretend it was a trackball click because that will always
784146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // jump to the "View event" screen.
785146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchViews(true /* trackball */);
786146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
787146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
788146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
78961a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan    public void handleOnResume() {
790ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mIs24HourFormat = DateFormat.is24HourFormat(mContext);
79169ab334d6988eda70ba53618506d23ebf7d7b405Erik        mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm;
79261a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan        mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext);
79369ab334d6988eda70ba53618506d23ebf7d7b405Erik    }
79469ab334d6988eda70ba53618506d23ebf7d7b405Erik
795146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
796146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Returns the start of the selected time in milliseconds since the epoch.
797146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
798146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @return selected time in UTC milliseconds since the epoch.
799146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
800146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    long getSelectedTimeInMillis() {
801146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
802146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
803146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
804146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
805146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
806146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
807146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
808146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time.normalize(true /* ignore isDst */);
809146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
810146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
811146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Time getSelectedTime() {
812146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
813146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
814146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
815146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
816146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
817146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
818146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
819146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.normalize(true /* ignore isDst */);
820146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time;
821146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
822146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
823146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
824146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Returns the start of the selected time in minutes since midnight,
825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * local time.  The derived class must ensure that this is consistent
826146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * with the return value from getSelectedTimeInMillis().
827146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
828146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    int getSelectedMinutesSinceMidnight() {
829146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return mSelectionHour * MINUTES_PER_HOUR;
830146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
831146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
83246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    int getFirstVisibleHour() {
83346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        return mFirstHour;
83446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
83546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
83646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    void setFirstVisibleHour(int firstHour) {
83746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mFirstHour = firstHour;
83846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mFirstHourOffset = 0;
83946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
84046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
84146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    public void setSelected(Time time, boolean ignoreTime) {
842146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mBaseDate.set(time);
843146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionHour = mBaseDate.hour;
844146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = null;
845146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevSelectedEvent = null;
846146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = mBaseDate.toMillis(false /* use isDst */);
847146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionDay = Time.getJulianDay(millis, mBaseDate.gmtoff);
848146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
849146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = true;
850146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
85146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int gotoY = Integer.MIN_VALUE;
85246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
85346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (!ignoreTime && mGridAreaHeight != -1) {
85446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int lastHour = 0;
85546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
85646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (mBaseDate.hour < mFirstHour) {
85746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // Above visible region
85846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP);
85946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else {
86046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                lastHour = (mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP)
86146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + mFirstHour;
86246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
86346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mBaseDate.hour >= lastHour) {
86446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // Below visible region
86546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
86646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // target hour + 1 (to give it room to see the event) -
86746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // grid height (to get the y of the top of the visible
86846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // region)
86955a26d7062d5aa1a1b001b7271bb137276ab807eErik                    gotoY = (int) ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f)
87046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                            * (mCellHeight + HOUR_GAP) - mGridAreaHeight);
87146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
87246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
87346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
87446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (DEBUG) {
87546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                Log.e(TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH "
87646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight
87746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + " ymax " + mMaxViewStartY);
87846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
87946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
88046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (gotoY > mMaxViewStartY) {
88146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = mMaxViewStartY;
88246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) {
88346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = 0;
88446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
88546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
88646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        recalc();
888146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
889ef2185bf0c7a80154a79788915db04d4f9e7ad4dMichael Chan        // Don't draw the selection box if we are going to the "current" time
890ef2185bf0c7a80154a79788915db04d4f9e7ad4dMichael Chan        long currMillis = System.currentTimeMillis();
891ef2185bf0c7a80154a79788915db04d4f9e7ad4dMichael Chan        boolean recent = (currMillis - 10000) < millis && millis < currMillis;
89246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mSelectionMode = (recent || ignoreTime) ? SELECTION_HIDDEN : SELECTION_SELECTED;
893146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mRemeasure = true;
894146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
89546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
89646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (gotoY != Integer.MIN_VALUE) {
89746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            TypeEvaluator evaluator = new TypeEvaluator() {
89846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                @Override
89946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                public Object evaluate(float fraction, Object startValue, Object endValue) {
90046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    int start = (Integer) startValue;
90146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    int end = (Integer) endValue;
90246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    final int newValue = (int) ((end - start) * fraction + start);
90346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    setViewStartY(newValue);
90446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return new Integer(newValue);
90546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
90646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            };
90746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            ValueAnimator scrollAnim = ObjectAnimator.ofObject(evaluator, new Integer(mViewStartY),
90846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    new Integer(gotoY));
90946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//          TODO The following line is supposed to replace the two statements above.
91046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//          Need to investigate why it's not working.
91146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
91246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//          ValueAnimator scrollAnim = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY);
91346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.setDuration(200);
91446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.setInterpolator(new AccelerateDecelerateInterpolator());
91546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.addListener(mAnimatorListener);
91646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.start();
91746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
91846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
91946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
92046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    public void setViewStartY(int viewStartY) {
92146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (viewStartY > mMaxViewStartY) {
92246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            viewStartY = mMaxViewStartY;
92346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
92446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
92546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mViewStartY = viewStartY;
92646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
92746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        computeFirstHour();
92846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        invalidate();
929146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public Time getSelectedDay() {
932146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
933146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
936146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
938146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.normalize(true /* ignore isDst */);
940146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time;
941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
943eaafa2b48be7194a61754604ae37b3d62e9118d8Erik    public void updateTitle() {
944eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        Time start = new Time(mBaseDate);
945eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        start.normalize(true);
946eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        Time end = new Time(start);
947eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.monthDay += mNumDays - 1;
948eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        // Move it forward one minute so the formatter doesn't lose a day
949eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.minute += 1;
950eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.normalize(true);
951eaafa2b48be7194a61754604ae37b3d62e9118d8Erik
9529e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        long formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
9539e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        if (mNumDays != 1) {
9549e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            // Don't show day of the month if for multi-day view
9559e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            formatFlags |= DateUtils.FORMAT_NO_MONTH_DAY;
9569e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan
9579e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            // Abbreviate the month if showing multiple months
9589e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            if (start.month != end.month) {
9599e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan                formatFlags |= DateUtils.FORMAT_ABBREV_MONTH;
9609e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            }
9619e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        }
9629e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan
963f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        mController.sendEvent(this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT,
9649e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan                formatFlags, null, null);
965eaafa2b48be7194a61754604ae37b3d62e9118d8Erik    }
966eaafa2b48be7194a61754604ae37b3d62e9118d8Erik
96783b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan    /**
968487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * return a negative number if "time" is comes before the visible time
969487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * range, a positive number if "time" is after the visible time range, and 0
970487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * if it is in the visible time range.
97183b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan     */
972487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    public int compareToVisibleTimeRange(Time time) {
973487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
974487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedHour = mBaseDate.hour;
975487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedMinute = mBaseDate.minute;
976487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedSec = mBaseDate.second;
977487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
978487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.hour = 0;
979487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.minute = 0;
980487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.second = 0;
981487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
982030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        if (DEBUG) {
983030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            Log.d(TAG, "Begin " + mBaseDate.toString());
984030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            Log.d(TAG, "Diff  " + time.toString());
985030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        }
986487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
98783b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        // Compare beginning of range
98883b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        int diff = Time.compare(time, mBaseDate);
989487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        if (diff > 0) {
990487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            // Compare end of range
991487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.monthDay += mNumDays;
992487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.normalize(true);
993487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            diff = Time.compare(time, mBaseDate);
994487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
995030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            if (DEBUG) Log.d(TAG, "End   " + mBaseDate.toString());
996487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
997487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.monthDay -= mNumDays;
998487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.normalize(true);
999487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            if (diff < 0) {
1000487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                // in visible time
1001487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                diff = 0;
1002487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            } else if (diff == 0) {
1003487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                // Midnight of following day
1004487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                diff = 1;
1005487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            }
100683b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        }
100783b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1008030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        if (DEBUG) Log.d(TAG, "Diff: " + diff);
100983b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1010487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.hour = savedHour;
1011487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.minute = savedMinute;
1012487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.second = savedSec;
1013487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        return diff;
101483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan    }
101583b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1016146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void recalc() {
1017146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Set the base date to the beginning of the week if we are displaying
1018146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // 7 days at a time.
1019146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays == 7) {
10206d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            adjustToBeginningOfWeek(mBaseDate);
1021146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1022146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1023146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final long start = mBaseDate.toMillis(false /* use isDst */);
1024146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff);
1025146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastJulianDay = mFirstJulianDay + mNumDays - 1;
1026146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1027146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY);
1028487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstVisibleDate = mBaseDate.monthDay;
1029487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstVisibleDayOfWeek = mBaseDate.weekDay;
1030146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1031146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
10326d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private void adjustToBeginningOfWeek(Time time) {
10336d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        int dayOfWeek = time.weekDay;
10346d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        int diff = dayOfWeek - mFirstDayOfWeek;
10356d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (diff != 0) {
10366d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            if (diff < 0) {
10376d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan                diff += 7;
10386d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            }
10396d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            time.monthDay -= diff;
10406d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            time.normalize(true /* ignore isDst */);
10416d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
10426d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    }
10436d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
1044146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1045146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected void onSizeChanged(int width, int height, int oldw, int oldh) {
1046146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewWidth = width;
1047146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewHeight = height;
1048146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int gridAreaWidth = width - mHoursWidth;
1049146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays;
1050146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1051d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        // This would be about 1 day worth in a 7 day view
1052d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        mHorizontalSnapBackThreshold = width / 7;
1053d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan
1054146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = new Paint();
1055e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
1056146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mHoursTextHeight = (int) Math.abs(p.ascent());
1057146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        remeasure(width, height);
1058146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1059146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
106046b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa    /**
106146b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     * Measures the space needed for various parts of the view after
106246b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     * loading new events.  This can change if there are all-day events.
106346b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     */
1064146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void remeasure(int width, int height) {
10652fda24534978f55711526dbde747aa50d68dec40RoboErik        // Shrink to fit available space but make sure we can display at least two events
10662fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
10672fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6);
10682fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(MAX_UNEXPANDED_ALLDAY_HEIGHT,
10692fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 2);
10702fda24534978f55711526dbde747aa50d68dec40RoboErik        mMaxUnexpandedAlldayEventCount =
10712fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
1072146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1073146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear the array of earliest start times, and the array
1074146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // indicating presence of an all-day event.
1075146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++) {
1076146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mEarliestStartHour[day] = 25;  // some big number
1077146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mHasAllDayEvent[day] = false;
1078146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1079146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
10802fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxAllDayEvents = mMaxAlldayEvents;
1081c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa
10822fda24534978f55711526dbde747aa50d68dec40RoboErik        // The min is where 24 hours cover the entire visible area
10837939fa59d64c1e6ac95f352e24d761dbacf67547RoboErik        mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, (int) MIN_EVENT_HEIGHT);
10842fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mCellHeight < mMinCellHeight) {
10852fda24534978f55711526dbde747aa50d68dec40RoboErik            mCellHeight = mMinCellHeight;
1086146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1087146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
10889c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan        // Calculate mAllDayHeight
10893eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mFirstCell = DAY_HEADER_HEIGHT;
1090146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int allDayHeight = 0;
1091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (maxAllDayEvents > 0) {
10922fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
1093146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is at most one all-day event per day, then use less
1094146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // space (but more than the space for a single event).
1095146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (maxAllDayEvents == 1) {
1096146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                allDayHeight = SINGLE_ALLDAY_HEIGHT;
10972fda24534978f55711526dbde747aa50d68dec40RoboErik            } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount){
1098146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Allow the all-day area to grow in height depending on the
1099146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // number of all-day events we need to show, up to a limit.
1100f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
11012fda24534978f55711526dbde747aa50d68dec40RoboErik                if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
11022fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT;
11032fda24534978f55711526dbde747aa50d68dec40RoboErik                }
11042fda24534978f55711526dbde747aa50d68dec40RoboErik            } else {
11052fda24534978f55711526dbde747aa50d68dec40RoboErik                // if we have more than the magic number, check if we're animating
11062fda24534978f55711526dbde747aa50d68dec40RoboErik                // and if not adjust the sizes appropriately
11072fda24534978f55711526dbde747aa50d68dec40RoboErik                if (mAnimateDayHeight != 0) {
11082fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Don't shrink the space past the final allDay space. The animation
11092fda24534978f55711526dbde747aa50d68dec40RoboErik                    // continues to hide the last event so the more events text can
11102fda24534978f55711526dbde747aa50d68dec40RoboErik                    // fade in.
11112fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT);
11122fda24534978f55711526dbde747aa50d68dec40RoboErik                } else {
11132fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Try to fit all the events in
11142fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = (int) (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
11152fda24534978f55711526dbde747aa50d68dec40RoboErik                    // But clip the area depending on which mode we're in
11162fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
1117e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                        allDayHeight = (int) (mMaxUnexpandedAlldayEventCount *
1118e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                                MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
11192fda24534978f55711526dbde747aa50d68dec40RoboErik                    } else if (allDayHeight > maxAllAllDayHeight) {
11202fda24534978f55711526dbde747aa50d68dec40RoboErik                        allDayHeight = maxAllAllDayHeight;
11212fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
1122146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1123146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
11243eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN;
1125146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
11262fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = false;
1127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
11282fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayHeight = allDayHeight;
1129146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1130146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mGridAreaHeight = height - mFirstCell;
1131d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
11322fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the expand icon position
1133e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int allDayIconWidth = mExpandAlldayDrawable.getIntrinsicWidth();
1134e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.left = Math.max((mHoursWidth - allDayIconWidth) / 2,
1135e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                EVENT_ALL_DAY_TEXT_LEFT_MARGIN);
1136e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.right = Math.min(mExpandAllDayRect.left + allDayIconWidth, mHoursWidth
1137e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN);
1138cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik        mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN;
1139e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.top = mExpandAllDayRect.bottom
1140e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                - mExpandAlldayDrawable.getIntrinsicHeight();
1141e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
1142e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // Cause the allDay text to be relaid out on the next draw pass
1143e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mAllDayTextLayout = null;
1144e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
1145d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
114646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP);
1147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setHourHeight(mCellHeight);
1148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
114927d671cdba3e8b50219def2c9e1118abe987f908Daisuke Miyakawa        final long minimumDurationMillis = (long)
115027d671cdba3e8b50219def2c9e1118abe987f908Daisuke Miyakawa                (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f));
11512fda24534978f55711526dbde747aa50d68dec40RoboErik        Event.computePositions(mEvents, minimumDurationMillis);
115246b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa
1153c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // Compute the top of our reachable view
1154c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
1155d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (DEBUG) {
1156d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.e(TAG, "mViewStartY: " + mViewStartY);
1157d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY);
1158d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
1159d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mViewStartY > mMaxViewStartY) {
1160d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = mMaxViewStartY;
1161d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            computeFirstHour();
1162d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
1163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour == -1) {
1165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            initFirstHour();
1166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHourOffset = 0;
1167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // When we change the base date, the number of all-day events may
1170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // change and that changes the cell height.  When we switch dates,
1171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // we use the mFirstHourOffset from the previous view, but that may
1172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // be too large for the new view if the cell height is smaller.
1173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHourOffset >= mCellHeight + HOUR_GAP) {
1174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHourOffset = mCellHeight + HOUR_GAP - 1;
1175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset;
1177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
117846b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa        final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP);
1179666fc5e8ddbac15c951aad738bd818ee307eae42Erik        //When we get new events we don't want to dismiss the popup unless the event changes
1180666fc5e8ddbac15c951aad738bd818ee307eae42Erik        if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) {
1181666fc5e8ddbac15c951aad738bd818ee307eae42Erik            mPopup.dismiss();
1182666fc5e8ddbac15c951aad738bd818ee307eae42Erik        }
1183146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setWidth(eventAreaWidth - 20);
1184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
1185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1187146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1188146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Initialize the state for another view.  The given view is one that has
1189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * its own bitmap and will use an animation to replace the current view.
1190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The current view and new view are either both Week views or both Day
1191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * views.  They differ in their base date.
1192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
1193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param view the view to initialize.
1194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1195d190418220467c02327dd9b3ae1eae238889720bMichael Chan    private void initView(DayView view) {
1196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mSelectionHour = mSelectionHour;
1197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mSelectedEvents.clear();
1198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mComputeSelectedEvents = true;
1199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mFirstHour = mFirstHour;
1200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mFirstHourOffset = mFirstHourOffset;
1201146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.remeasure(getWidth(), getHeight());
12022fda24534978f55711526dbde747aa50d68dec40RoboErik        view.initAllDayHeights();
1203e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
1204146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mSelectedEvent = null;
1205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mPrevSelectedEvent = null;
1206487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        view.mFirstDayOfWeek = mFirstDayOfWeek;
1207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (view.mEvents.size() > 0) {
12082fda24534978f55711526dbde747aa50d68dec40RoboErik            view.mSelectionAllday = mSelectionAllday;
1209146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
12102fda24534978f55711526dbde747aa50d68dec40RoboErik            view.mSelectionAllday = false;
1211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1213146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Redraw the screen so that the selection box will be redrawn.  We may
1214146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // have scrolled to a different part of the day in some other view
1215146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // so the selection box in this view may no longer be visible.
1216146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.recalc();
1217146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Switch to another view based on what was selected (an event or a free
1221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * slot) and how it was selected (by touch or by trackball).
1222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
1223146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param trackBallSelection true if the selection was made using the
1224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * trackball.
1225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void switchViews(boolean trackBallSelection) {
1227146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event selectedEvent = mSelectedEvent;
1228146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1229146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.dismiss();
1230666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
1231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays > 1) {
1232146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // This is the Week view.
1233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // With touch, we always switch to Day/Agenda View
1234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // With track ball, if we selected a free slot, then create an event.
1235146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected a specific event, switch to EventInfo view.
1236146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (trackBallSelection) {
1237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (selectedEvent == null) {
1238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Switch to the EditEvent view
1239146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long startMillis = getSelectedTimeInMillis();
1240146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
12419e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1,
124287f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            startMillis, endMillis, 0, 0, -1);
1243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else {
1244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Switch to the EventInfo view
12459e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
124687f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
124787f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            getSelectedTimeInMillis());
1248146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1250146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // This was a touch selection.  If the touch selected a single
1251146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // unambiguous event, then view that event.  Otherwise go to
1252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Day/Agenda view.
1253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectedEvents.size() == 1) {
12549e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
125587f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
125687f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            getSelectedTimeInMillis());
1257146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1258146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
1260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // This is the Day view.
1261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected a free slot, then create an event.
1262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected an event, then go to the EventInfo view.
1263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (selectedEvent == null) {
1264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Switch to the EditEvent view
1265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long startMillis = getSelectedTimeInMillis();
1266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
12679e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan
12689e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1, startMillis,
126987f993ff96172f508d3f241f8a425f953c7eb739RoboErik                        endMillis, 0, 0, -1);
1270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
12719e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
127287f993ff96172f508d3f241f8a425f953c7eb739RoboErik                        selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
127387f993ff96172f508d3f241f8a425f953c7eb739RoboErik                        getSelectedTimeInMillis());
1274146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1277146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1278146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onKeyUp(int keyCode, KeyEvent event) {
1280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = false;
1281146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long duration = event.getEventTime() - event.getDownTime();
1282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1283146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (keyCode) {
1284146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            case KeyEvent.KEYCODE_DPAD_CENTER:
1285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectionMode == SELECTION_HIDDEN) {
1286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Don't do anything unless the selection is visible.
1287146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
1288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1290146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectionMode == SELECTION_PRESSED) {
1291146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This was the first press when there was nothing selected.
1292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Change the selection from the "pressed" state to the
1293146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // the "selected" state.  We treat short-press and
1294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // long-press the same here because nothing was selected.
1295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectionMode = SELECTION_SELECTED;
1296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
1297146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
1298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Check the duration to determine if this was a short press
1301146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (duration < ViewConfiguration.getLongPressTimeout()) {
1302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    switchViews(true /* trackball */);
1303146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else {
1304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectionMode = SELECTION_LONGPRESS;
1305146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
1306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    performLongClick();
1307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                break;
1309ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//            case KeyEvent.KEYCODE_BACK:
1310ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                if (event.isTracking() && !event.isCanceled()) {
1311ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    mPopup.dismiss();
1312ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    mContext.finish();
1313ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    return true;
1314ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                }
1315ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                break;
1316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return super.onKeyUp(keyCode, event);
1318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1320146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onKeyDown(int keyCode, KeyEvent event) {
1322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionMode == SELECTION_HIDDEN) {
1323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
1324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_UP
1325146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
1326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Display the selection box but don't move or select it
1327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // on this key press.
1328146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionMode = SELECTION_SELECTED;
1329146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1330146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
1331146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
1332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Display the selection box but don't select it
1333146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // on this key press.
1334146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionMode = SELECTION_PRESSED;
1335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1336146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
1337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_SELECTED;
1341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = false;
1342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean redraw;
1343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectionDay = mSelectionDay;
1344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (keyCode) {
134646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DEL:
134746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // Delete the selected event, if any
134846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                Event selectedEvent = mSelectedEvent;
134946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (selectedEvent == null) {
135046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return false;
135146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
135246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mPopup.dismiss();
135346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mLastPopupEventID = INVALID_EVENT_ID;
1354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
135546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long begin = selectedEvent.startMillis;
135646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long end = selectedEvent.endMillis;
135746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long id = selectedEvent.id;
135846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mDeleteEventHelper.delete(begin, end, id, -1);
1359b10f5762d0e50bac6ba31ec332e7513869b55bf3Michael Chan                return true;
136046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_ENTER:
136146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                switchViews(true /* trackball or keyboard */);
136246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return true;
136346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_BACK:
136446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (event.getRepeatCount() == 0) {
136546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    event.startTracking();
136646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
136746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
136846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onKeyDown(keyCode, event);
136946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_LEFT:
137046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
137146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextLeft;
137246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
137346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
137446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
137546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    selectionDay -= 1;
137646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
137746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
137846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
138046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_RIGHT:
138146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
138246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextRight;
138346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
138446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
138546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
138646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    selectionDay += 1;
138746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
138846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
138946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1390146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
139146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_UP:
139246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
139346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextUp;
1394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
139546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
139646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
13972fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (!mSelectionAllday) {
139846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectionHour -= 1;
139946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        adjustHourSelection();
140046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectedEvents.clear();
140146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mComputeSelectedEvents = true;
140246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
140346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
140446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
140546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1406146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
140746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_DOWN:
140846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
140946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mSelectedEvent = mSelectedEvent.nextDown;
1410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
141146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
141246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
14132fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (mSelectionAllday) {
14142fda24534978f55711526dbde747aa50d68dec40RoboErik                        mSelectionAllday = false;
141546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    } else {
141646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectionHour++;
141746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        adjustHourSelection();
141846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectedEvents.clear();
141946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mComputeSelectedEvents = true;
142046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
142146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
142246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
142346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
142546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            default:
142646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onKeyDown(keyCode, event);
1427146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((selectionDay < mFirstJulianDay) || (selectionDay > mLastJulianDay)) {
1430d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView view = (DayView) mViewSwitcher.getNextView();
1431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Time date = view.mBaseDate;
1432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.set(mBaseDate);
1433146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (selectionDay < mFirstJulianDay) {
1434146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                date.monthDay -= mNumDays;
1435146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1436146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                date.monthDay += mNumDays;
1437146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1438146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.normalize(true /* ignore isDst */);
1439146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            view.mSelectionDay = selectionDay;
1440146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1441146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            initView(view);
144283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
144383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            Time end = new Time(date);
144483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            end.monthDay += mNumDays - 1;
14453458a1775920dda53099b7f1913cd4ec4c8b4095Michael Chan            mController.sendEvent(this, EventType.GO_TO, date, end, -1, ViewType.CURRENT);
1446146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
1447146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
144804b288958078f8b29870dcbb2239df448c559610Erik        if (mSelectionDay != selectionDay) {
144904b288958078f8b29870dcbb2239df448c559610Erik            Time date = new Time(mBaseDate);
145004b288958078f8b29870dcbb2239df448c559610Erik            date.setJulianDay(selectionDay);
145104b288958078f8b29870dcbb2239df448c559610Erik            date.hour = mSelectionHour;
145204b288958078f8b29870dcbb2239df448c559610Erik            mController.sendEvent(this, EventType.GO_TO, date, date, -1, ViewType.CURRENT);
145304b288958078f8b29870dcbb2239df448c559610Erik        }
1454146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionDay = selectionDay;
1455146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
1456146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = true;
14578307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        mUpdateToast = true;
1458146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1459146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (redraw) {
1460146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
1461146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
1462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1463146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return super.onKeyDown(keyCode, event);
1465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1466146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1467259b8f9a21fffd3858218581e9daaf689b6b888fErik    private class AccessibilityRunnable implements Runnable {
1468259b8f9a21fffd3858218581e9daaf689b6b888fErik        int mEventType = AccessibilityEvent.TYPE_VIEW_SELECTED;
1469259b8f9a21fffd3858218581e9daaf689b6b888fErik        @Override
1470259b8f9a21fffd3858218581e9daaf689b6b888fErik        public void run() {
1471259b8f9a21fffd3858218581e9daaf689b6b888fErik            sendAccessibilityEvent(mEventType);
1472259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1473259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1474259b8f9a21fffd3858218581e9daaf689b6b888fErik
1475259b8f9a21fffd3858218581e9daaf689b6b888fErik    private AccessibilityRunnable mDispatchAccessibilityEventRunnable = new AccessibilityRunnable();
1476259b8f9a21fffd3858218581e9daaf689b6b888fErik
1477259b8f9a21fffd3858218581e9daaf689b6b888fErik    @Override
1478259b8f9a21fffd3858218581e9daaf689b6b888fErik    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1479259b8f9a21fffd3858218581e9daaf689b6b888fErik        if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SELECTED &&
1480259b8f9a21fffd3858218581e9daaf689b6b888fErik                event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
1481259b8f9a21fffd3858218581e9daaf689b6b888fErik            return false;
1482259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1483259b8f9a21fffd3858218581e9daaf689b6b888fErik        StringBuilder b = new StringBuilder(getSelectedTime()
1484259b8f9a21fffd3858218581e9daaf689b6b888fErik                .format(mIs24HourFormat ? HOUR_FORMAT_24H : HOUR_FORMAT_12H));
1485259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(PERIOD_SPACE);
1486259b8f9a21fffd3858218581e9daaf689b6b888fErik        int numEvents = mSelectedEvents.size();
1487259b8f9a21fffd3858218581e9daaf689b6b888fErik        if (mEventCountTemplate == null) {
1488259b8f9a21fffd3858218581e9daaf689b6b888fErik            mEventCountTemplate = mContext.getString(R.string.template_announce_item_index);
1489259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1490259b8f9a21fffd3858218581e9daaf689b6b888fErik        switch (event.getEventType()) {
1491259b8f9a21fffd3858218581e9daaf689b6b888fErik            // When a new hour is selected we sent this event
1492259b8f9a21fffd3858218581e9daaf689b6b888fErik            case AccessibilityEvent.TYPE_VIEW_SELECTED:
1493259b8f9a21fffd3858218581e9daaf689b6b888fErik                int i = 1;
1494259b8f9a21fffd3858218581e9daaf689b6b888fErik                for (Event calEvent : mSelectedEvents) {
1495259b8f9a21fffd3858218581e9daaf689b6b888fErik                    if (numEvents > 1) {
1496259b8f9a21fffd3858218581e9daaf689b6b888fErik                        mStringBuilder.setLength(0);
1497259b8f9a21fffd3858218581e9daaf689b6b888fErik                        b.append(mFormatter.format(mEventCountTemplate, i++, numEvents));
1498259b8f9a21fffd3858218581e9daaf689b6b888fErik                        b.append(" ");
1499259b8f9a21fffd3858218581e9daaf689b6b888fErik                    }
1500259b8f9a21fffd3858218581e9daaf689b6b888fErik                    appendEventAccessibilityString(b, calEvent);
1501259b8f9a21fffd3858218581e9daaf689b6b888fErik                }
1502259b8f9a21fffd3858218581e9daaf689b6b888fErik                break;
1503259b8f9a21fffd3858218581e9daaf689b6b888fErik            // When a different event is selected we send this event
1504259b8f9a21fffd3858218581e9daaf689b6b888fErik            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
1505259b8f9a21fffd3858218581e9daaf689b6b888fErik                if (mSelectedEvent != null) {
1506259b8f9a21fffd3858218581e9daaf689b6b888fErik                    if (numEvents > 1) {
1507259b8f9a21fffd3858218581e9daaf689b6b888fErik                        mStringBuilder.setLength(0);
1508259b8f9a21fffd3858218581e9daaf689b6b888fErik                        b.append(mFormatter.format(mEventCountTemplate,
1509259b8f9a21fffd3858218581e9daaf689b6b888fErik                                mSelectedEvents.indexOf(mSelectedEvent) + 1, numEvents));
1510259b8f9a21fffd3858218581e9daaf689b6b888fErik                        b.append(" ");
1511259b8f9a21fffd3858218581e9daaf689b6b888fErik                    }
1512259b8f9a21fffd3858218581e9daaf689b6b888fErik                    appendEventAccessibilityString(b, mSelectedEvent);
1513259b8f9a21fffd3858218581e9daaf689b6b888fErik                }
1514259b8f9a21fffd3858218581e9daaf689b6b888fErik                break;
1515259b8f9a21fffd3858218581e9daaf689b6b888fErik            default:
1516259b8f9a21fffd3858218581e9daaf689b6b888fErik                break;
1517259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1518259b8f9a21fffd3858218581e9daaf689b6b888fErik        CharSequence msg = b.toString();
1519259b8f9a21fffd3858218581e9daaf689b6b888fErik        event.getText().add(msg);
1520259b8f9a21fffd3858218581e9daaf689b6b888fErik        event.setAddedCount(msg.length());
1521259b8f9a21fffd3858218581e9daaf689b6b888fErik        return true;
1522259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1523259b8f9a21fffd3858218581e9daaf689b6b888fErik
1524259b8f9a21fffd3858218581e9daaf689b6b888fErik    /**
1525259b8f9a21fffd3858218581e9daaf689b6b888fErik     * @param b
1526259b8f9a21fffd3858218581e9daaf689b6b888fErik     * @param calEvent
1527259b8f9a21fffd3858218581e9daaf689b6b888fErik     */
1528259b8f9a21fffd3858218581e9daaf689b6b888fErik    private void appendEventAccessibilityString(StringBuilder b, Event calEvent) {
1529259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(calEvent.getTitleAndLocation());
1530259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(PERIOD_SPACE);
1531259b8f9a21fffd3858218581e9daaf689b6b888fErik        String when;
1532259b8f9a21fffd3858218581e9daaf689b6b888fErik        int flags = DateUtils.FORMAT_SHOW_DATE;
1533259b8f9a21fffd3858218581e9daaf689b6b888fErik        if (calEvent.allDay) {
1534259b8f9a21fffd3858218581e9daaf689b6b888fErik            flags |= DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY;
1535259b8f9a21fffd3858218581e9daaf689b6b888fErik        } else {
1536259b8f9a21fffd3858218581e9daaf689b6b888fErik            flags |= DateUtils.FORMAT_SHOW_TIME;
1537259b8f9a21fffd3858218581e9daaf689b6b888fErik            if (DateFormat.is24HourFormat(mContext)) {
1538259b8f9a21fffd3858218581e9daaf689b6b888fErik                flags |= DateUtils.FORMAT_24HOUR;
1539259b8f9a21fffd3858218581e9daaf689b6b888fErik            }
1540259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1541259b8f9a21fffd3858218581e9daaf689b6b888fErik        when = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags);
1542259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(when);
1543259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(PERIOD_SPACE);
1544259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1545259b8f9a21fffd3858218581e9daaf689b6b888fErik
15466d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private class GotoBroadcaster implements Animation.AnimationListener {
15476d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final int mCounter;
15486d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final Time mStart;
15496d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final Time mEnd;
15506d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
15516d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public GotoBroadcaster(Time start, Time end) {
15526d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mCounter = ++sCounter;
15536d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mStart = start;
15546d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mEnd = end;
15556d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
15566d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
15576d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
15586d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationEnd(Animation animation) {
1559f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            DayView view = (DayView) mViewSwitcher.getCurrentView();
1560f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view.mViewStartX = 0;
1561f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view = (DayView) mViewSwitcher.getNextView();
1562f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view.mViewStartX = 0;
1563f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan
15646d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            if (mCounter == sCounter) {
1565f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan                mController.sendEvent(this, EventType.GO_TO, mStart, mEnd, null, -1,
1566f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan                        ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null);
15676d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            }
15686d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
15696d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
15706d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
15716d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationRepeat(Animation animation) {
15726d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
15736d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
15746d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
15756d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationStart(Animation animation) {
15766d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
15776d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    }
15786d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
15792c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private View switchViews(boolean forward, float xOffSet, float width, float velocity) {
158095d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        mAnimationDistance = width - xOffSet;
158195d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        if (DEBUG) {
158295d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            Log.d(TAG, "switchViews(" + forward + ") O:" + xOffSet + " Dist:" + mAnimationDistance);
158395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        }
158495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
1585ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float progress = Math.abs(xOffSet) / width;
1586ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (progress > 1.0f) {
1587ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            progress = 1.0f;
1588ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
1589ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1590ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float inFromXValue, inToXValue;
1591ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float outFromXValue, outToXValue;
1592ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (forward) {
1593ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inFromXValue = 1.0f - progress;
1594ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inToXValue = 0.0f;
1595ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outFromXValue = -progress;
1596ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outToXValue = -1.0f;
1597ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        } else {
1598ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inFromXValue = progress - 1.0f;
1599ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inToXValue = 0.0f;
1600ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outFromXValue = progress;
1601ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outToXValue = 1.0f;
1602ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
1603ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
16046d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        final Time start = new Time(mBaseDate.timezone);
16056d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        start.set(mController.getTime());
16066d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (forward) {
16076d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            start.monthDay += mNumDays;
16086d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        } else {
16096d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            start.monthDay -= mNumDays;
16106d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
1611f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        mController.setTime(start.normalize(true));
1612f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan
1613f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        Time newSelected = start;
16146d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
16156d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (mNumDays == 7) {
1616f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan            newSelected = new Time(start);
16176d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            adjustToBeginningOfWeek(start);
16186d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
16196d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
16206d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        final Time end = new Time(start);
16216d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        end.monthDay += mNumDays - 1;
16226d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
1623ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        // We have to allocate these animation objects each time we switch views
1624ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        // because that is the only way to set the animation parameters.
1625ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        TranslateAnimation inAnimation = new TranslateAnimation(
1626ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, inFromXValue,
1627ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, inToXValue,
1628ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f,
1629ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f);
1630ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1631ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        TranslateAnimation outAnimation = new TranslateAnimation(
1632ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, outFromXValue,
1633ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, outToXValue,
1634ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f,
1635ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f);
1636ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
16372c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        long duration = calculateDuration(width - Math.abs(xOffSet), width, velocity);
1638ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        inAnimation.setDuration(duration);
16392c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        inAnimation.setInterpolator(mHScrollInterpolator);
16402c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        outAnimation.setInterpolator(mHScrollInterpolator);
1641ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        outAnimation.setDuration(duration);
16426d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        outAnimation.setAnimationListener(new GotoBroadcaster(start, end));
1643ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.setInAnimation(inAnimation);
1644ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.setOutAnimation(outAnimation);
1645ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1646d190418220467c02327dd9b3ae1eae238889720bMichael Chan        DayView view = (DayView) mViewSwitcher.getCurrentView();
1647ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.cleanup();
1648ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.showNext();
1649d190418220467c02327dd9b3ae1eae238889720bMichael Chan        view = (DayView) mViewSwitcher.getCurrentView();
1650f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        view.setSelected(newSelected, true);
1651ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.requestFocus();
1652ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.reloadEvents();
1653eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        view.updateTitle();
1654e98dd73c67205b01f5348271d9a7a630003ecf52David Brown
1655ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        return view;
1656ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    }
1657ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1658146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // This is called after scrolling stops to move the selected hour
1659146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // to the visible part of the screen.
1660146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void resetSelectedHour() {
1661146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < mFirstHour + 1) {
1662146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = mFirstHour + 1;
1663146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = null;
1664146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvents.clear();
1665146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mComputeSelectedEvents = true;
1666146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (mSelectionHour > mFirstHour + mNumHours - 3) {
1667146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = mFirstHour + mNumHours - 3;
1668146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = null;
1669146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvents.clear();
1670146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mComputeSelectedEvents = true;
1671146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1672146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1673146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1674146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void initFirstHour() {
1675d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mFirstHour = mSelectionHour - mNumHours / 5;
1676146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour < 0) {
1677146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHour = 0;
1678146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (mFirstHour + mNumHours > 24) {
1679146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHour = 24 - mNumHours;
1680146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1681146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1682146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1683146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1684146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Recomputes the first full hour that is visible on screen after the
1685146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * screen is scrolled.
1686146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1687146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeFirstHour() {
1688146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Compute the first full hour that is visible on screen
1689146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP);
1690146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY;
1691146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1692146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1693146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void adjustHourSelection() {
1694146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < 0) {
1695146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = 0;
16962fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > 0) {
1697146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
16982fda24534978f55711526dbde747aa50d68dec40RoboErik                mSelectionAllday = true;
1699146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1700146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1701146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1702146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour > 23) {
1703146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionHour = 23;
1704146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1705146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1706146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the selected hour is at least 2 time slots from the top and
1707146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // bottom of the screen, then don't scroll the view.
1708146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < mFirstHour + 1) {
1709146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there are all-days events for the selected day but there
1710146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // are no more normal events earlier in the day, then jump to
1711146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // the all-day event area.
1712146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Exception 1: allow the user to scroll to 8am with the trackball
1713146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // before jumping to the all-day event area.
1714146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Exception 2: if 12am is on screen, then allow the user to select
1715146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // 12am before going up to the all-day event area.
1716146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int daynum = mSelectionDay - mFirstJulianDay;
17172fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > 0 && mEarliestStartHour[daynum] > mSelectionHour
1718146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    && mFirstHour > 0 && mFirstHour < 8) {
1719146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
17202fda24534978f55711526dbde747aa50d68dec40RoboErik                mSelectionAllday = true;
1721146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionHour = mFirstHour + 1;
1722146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1723146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1724146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1725146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mFirstHour > 0) {
1726146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mFirstHour -= 1;
1727146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY -= (mCellHeight + HOUR_GAP);
1728146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mViewStartY < 0) {
1729146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mViewStartY = 0;
1730146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1731146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1732146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1733146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1734146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1735146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour > mFirstHour + mNumHours - 3) {
1736146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mFirstHour < 24 - mNumHours) {
1737146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mFirstHour += 1;
1738146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY += (mCellHeight + HOUR_GAP);
1739c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                if (mViewStartY > mMaxViewStartY) {
1740c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                    mViewStartY = mMaxViewStartY;
1741146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1742146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1743146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) {
1744c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                mViewStartY = mMaxViewStartY;
1745146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1746146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1747146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1748146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1749146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    void clearCachedEvents() {
1750146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastReloadMillis = 0;
1751146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1752146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1753146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Runnable mCancelCallback = new Runnable() {
1754146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
1755146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            clearCachedEvents();
1756146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1757146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
1758146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
175946b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa    /* package */ void reloadEvents() {
1760146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Protect against this being called before this view has been
1761146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // initialized.
1762ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        if (mContext == null) {
1763ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//            return;
1764ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        }
1765146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1766731f1cb808b7586b93615b408ab2636081fab0dcErik        // Make sure our time zones are up to date
1767731f1cb808b7586b93615b408ab2636081fab0dcErik        mTZUpdater.run();
1768731f1cb808b7586b93615b408ab2636081fab0dcErik
1769146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = null;
1770146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevSelectedEvent = null;
1771146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
1772146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1773146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // The start date is the beginning of the week at 12am
1774731f1cb808b7586b93615b408ab2636081fab0dcErik        Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater));
1775146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.set(mBaseDate);
1776146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.hour = 0;
1777146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.minute = 0;
1778146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.second = 0;
1779146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = weekStart.normalize(true /* ignore isDst */);
1780146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1781146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Avoid reloading events unnecessarily.
1782146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (millis == mLastReloadMillis) {
1783146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
1784146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1785146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastReloadMillis = millis;
1786146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1787146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // load events in the background
1788ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        mContext.startProgressSpinner();
1789146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final ArrayList<Event> events = new ArrayList<Event>();
1790b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik        mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, new Runnable() {
1791146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            public void run() {
1792146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mEvents = events;
179308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mAllDayEvents == null) {
179408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayEvents = new ArrayList<Event>();
179508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
179608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayEvents.clear();
179708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
179808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
179908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                // Create a shorter array for all day events
180046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                for (Event e : events) {
180168655b5af2b7e5f872a103858ec58a19042123ceRoboErik                    if (e.drawAsAllday()) {
180208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                        mAllDayEvents.add(e);
180308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    }
180408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
180508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
180608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                // New events, new layouts
180708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mLayouts == null || mLayouts.length < events.size()) {
180808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mLayouts = new StaticLayout[events.size()];
180908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
181008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    Arrays.fill(mLayouts, null);
18113e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh                }
181208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
181308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) {
181408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayLayouts = new StaticLayout[events.size()];
181508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
181608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    Arrays.fill(mAllDayLayouts, null);
181708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
181808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
18192fda24534978f55711526dbde747aa50d68dec40RoboErik                computeEventRelations();
18202fda24534978f55711526dbde747aa50d68dec40RoboErik
1821146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mRemeasure = true;
1822146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mComputeSelectedEvents = true;
1823146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                recalc();
1824ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                mContext.stopProgressSpinner();
1825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1826146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1827146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }, mCancelCallback);
1828146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1829146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
18302fda24534978f55711526dbde747aa50d68dec40RoboErik    private void computeEventRelations() {
18312fda24534978f55711526dbde747aa50d68dec40RoboErik        // Compute the layout relation between each event before measuring cell
18322fda24534978f55711526dbde747aa50d68dec40RoboErik        // width, as the cell width should be adjusted along with the relation.
18332fda24534978f55711526dbde747aa50d68dec40RoboErik        //
18342fda24534978f55711526dbde747aa50d68dec40RoboErik        // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm)
18352fda24534978f55711526dbde747aa50d68dec40RoboErik        // We should mark them as "overwapped". Though they are not overwapped logically, but
18362fda24534978f55711526dbde747aa50d68dec40RoboErik        // minimum cell height implicitly expands the cell height of A and it should look like
18372fda24534978f55711526dbde747aa50d68dec40RoboErik        // (1:00pm - 1:15pm) after the cell height adjustment.
18382fda24534978f55711526dbde747aa50d68dec40RoboErik
18392fda24534978f55711526dbde747aa50d68dec40RoboErik        // Compute the space needed for the all-day events, if any.
18402fda24534978f55711526dbde747aa50d68dec40RoboErik        // Make a pass over all the events, and keep track of the maximum
18412fda24534978f55711526dbde747aa50d68dec40RoboErik        // number of all-day events in any one day.  Also, keep track of
18422fda24534978f55711526dbde747aa50d68dec40RoboErik        // the earliest event in each day.
18432fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxAllDayEvents = 0;
18442fda24534978f55711526dbde747aa50d68dec40RoboErik        final ArrayList<Event> events = mEvents;
18452fda24534978f55711526dbde747aa50d68dec40RoboErik        final int len = events.size();
18462fda24534978f55711526dbde747aa50d68dec40RoboErik        // Num of all-day-events on each day.
18472fda24534978f55711526dbde747aa50d68dec40RoboErik        final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1];
18482fda24534978f55711526dbde747aa50d68dec40RoboErik        Arrays.fill(eventsCount, 0);
18492fda24534978f55711526dbde747aa50d68dec40RoboErik        for (int ii = 0; ii < len; ii++) {
18502fda24534978f55711526dbde747aa50d68dec40RoboErik            Event event = events.get(ii);
18512fda24534978f55711526dbde747aa50d68dec40RoboErik            if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) {
18522fda24534978f55711526dbde747aa50d68dec40RoboErik                continue;
18532fda24534978f55711526dbde747aa50d68dec40RoboErik            }
18542fda24534978f55711526dbde747aa50d68dec40RoboErik            if (event.drawAsAllday()) {
18552fda24534978f55711526dbde747aa50d68dec40RoboErik                // Count all the events being drawn as allDay events
18562fda24534978f55711526dbde747aa50d68dec40RoboErik                final int firstDay = Math.max(event.startDay, mFirstJulianDay);
18572fda24534978f55711526dbde747aa50d68dec40RoboErik                final int lastDay = Math.min(event.endDay, mLastJulianDay);
18582fda24534978f55711526dbde747aa50d68dec40RoboErik                for (int day = firstDay; day <= lastDay; day++) {
18592fda24534978f55711526dbde747aa50d68dec40RoboErik                    final int count = ++eventsCount[day - mFirstJulianDay];
18602fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (maxAllDayEvents < count) {
18612fda24534978f55711526dbde747aa50d68dec40RoboErik                        maxAllDayEvents = count;
18622fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
18632fda24534978f55711526dbde747aa50d68dec40RoboErik                }
18642fda24534978f55711526dbde747aa50d68dec40RoboErik
18652fda24534978f55711526dbde747aa50d68dec40RoboErik                int daynum = event.startDay - mFirstJulianDay;
18662fda24534978f55711526dbde747aa50d68dec40RoboErik                int durationDays = event.endDay - event.startDay + 1;
18672fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum < 0) {
18682fda24534978f55711526dbde747aa50d68dec40RoboErik                    durationDays += daynum;
18692fda24534978f55711526dbde747aa50d68dec40RoboErik                    daynum = 0;
18702fda24534978f55711526dbde747aa50d68dec40RoboErik                }
18712fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum + durationDays > mNumDays) {
18722fda24534978f55711526dbde747aa50d68dec40RoboErik                    durationDays = mNumDays - daynum;
18732fda24534978f55711526dbde747aa50d68dec40RoboErik                }
18742fda24534978f55711526dbde747aa50d68dec40RoboErik                for (int day = daynum; durationDays > 0; day++, durationDays--) {
18752fda24534978f55711526dbde747aa50d68dec40RoboErik                    mHasAllDayEvent[day] = true;
18762fda24534978f55711526dbde747aa50d68dec40RoboErik                }
18772fda24534978f55711526dbde747aa50d68dec40RoboErik            } else {
18782fda24534978f55711526dbde747aa50d68dec40RoboErik                int daynum = event.startDay - mFirstJulianDay;
18792fda24534978f55711526dbde747aa50d68dec40RoboErik                int hour = event.startTime / 60;
18802fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum >= 0 && hour < mEarliestStartHour[daynum]) {
18812fda24534978f55711526dbde747aa50d68dec40RoboErik                    mEarliestStartHour[daynum] = hour;
18822fda24534978f55711526dbde747aa50d68dec40RoboErik                }
18832fda24534978f55711526dbde747aa50d68dec40RoboErik
18842fda24534978f55711526dbde747aa50d68dec40RoboErik                // Also check the end hour in case the event spans more than
18852fda24534978f55711526dbde747aa50d68dec40RoboErik                // one day.
18862fda24534978f55711526dbde747aa50d68dec40RoboErik                daynum = event.endDay - mFirstJulianDay;
18872fda24534978f55711526dbde747aa50d68dec40RoboErik                hour = event.endTime / 60;
18882fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) {
18892fda24534978f55711526dbde747aa50d68dec40RoboErik                    mEarliestStartHour[daynum] = hour;
18902fda24534978f55711526dbde747aa50d68dec40RoboErik                }
18912fda24534978f55711526dbde747aa50d68dec40RoboErik            }
18922fda24534978f55711526dbde747aa50d68dec40RoboErik        }
18932fda24534978f55711526dbde747aa50d68dec40RoboErik        mMaxAlldayEvents = maxAllDayEvents;
18942fda24534978f55711526dbde747aa50d68dec40RoboErik        initAllDayHeights();
18952fda24534978f55711526dbde747aa50d68dec40RoboErik    }
18962fda24534978f55711526dbde747aa50d68dec40RoboErik
1897146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1898146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected void onDraw(Canvas canvas) {
1899146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mRemeasure) {
1900146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            remeasure(getWidth(), getHeight());
1901146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mRemeasure = false;
1902146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1903c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.save();
1904146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
19052fda24534978f55711526dbde747aa50d68dec40RoboErik        float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight;
1906c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // offset canvas by the current drag and header position
1907c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.translate(-mViewStartX, yTranslate);
1908c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // clip to everything below the allDay area
1909c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        Rect dest = mDestRect;
1910c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.top = (int) (mFirstCell - yTranslate);
1911c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.bottom = (int) (mViewHeight - yTranslate);
1912c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.left = 0;
1913c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.right = mViewWidth;
1914c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.save();
1915c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.clipRect(dest);
1916c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // Draw the movable part of the view
1917c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        doDraw(canvas);
1918c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // restore to having no clip
1919c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.restore();
1920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1921146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
1922c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            float xTranslate;
1923146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mViewStartX > 0) {
1924c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                xTranslate = mViewWidth;
1925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1926c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                xTranslate = -mViewWidth;
1927146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1928c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // Move the canvas around to prep it for the next view
1929c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // specifically, shift it by a screen and undo the
1930c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // yTranslation which will be redone in the nextView's onDraw().
1931c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(xTranslate, -yTranslate);
1932d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView nextView = (DayView) mViewSwitcher.getNextView();
1933146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Prevent infinite recursive calls to onDraw().
1935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE;
1936146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            nextView.onDraw(canvas);
1938c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // Move it back for this view
1939c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(-xTranslate, 0);
1940c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        } else {
1941c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // If we drew another view we already translated it back
1942c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // If we didn't draw another view we should be at the edge of the
1943c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // screen
1944c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(mViewStartX, -yTranslate);
1945146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1946146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1947146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the fixed areas (that don't scroll) directly to the canvas.
1948146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawAfterScroll(canvas);
19498307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        if (mComputeSelectedEvents && mUpdateToast) {
19508307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik            updateEventDetails();
19518307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik            mUpdateToast = false;
19528307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        }
1953146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = false;
1954146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        canvas.restore();
1955146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1956146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1957146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawAfterScroll(Canvas canvas) {
1958146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mPaint;
1959146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect r = mRect;
1960146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1961e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        drawAllDayHighlights(r, canvas, p);
19622fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents != 0) {
19631ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p);
19648f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            drawUpperLeftCorner(r, canvas, p);
1965146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1966146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1967c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        drawScrollLine(r, canvas, p);
19683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        drawDayHeaderLoop(r, canvas, p);
1969146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1970146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the AM and PM indicators if we're in 12 hour mode
1971146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!mIs24HourFormat) {
1972146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            drawAmPm(canvas, p);
1973146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1974146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1975146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
197646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    // This isn't really the upper-left corner. It's the square area just
1977146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // below the upper-left corner, above the hours and to the left of the
1978146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // all-day area.
19798f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) {
1980e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // StaticLayout is wonky and doesn't do multi-line center text
1981e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // correctly. In order to make it work set the paint to Align.CENTER and
1982e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // the Layout to Alignment.ALIGN_NORMAL.
1983e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextAlign(Align.CENTER);
1984e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(ALLDAY_TEXT_SIZE);
1985e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mCalendarHourLabelColor);
1986e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int translateY = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
1987e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = -mHoursWidth / 2;
1988e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mHoursWidth / 2;
1989e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = 0;
1990e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = mFirstCell - translateY;
1991e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.save();
1992e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.translate(mHoursWidth / 2, translateY);
1993e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.clipRect(r);
1994e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        StaticLayout allDayLayout = getOrCreateAllDayTextLayout(p);
1995e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        allDayLayout.draw(canvas);
1996e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.restore();
1997e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
19988f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        setupHourTextPaint(p);
19992fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
2000cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            // Draw the allDay expand/collapse icon
2001cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            if (mUseExpandIcon) {
2002cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mExpandAlldayDrawable.setBounds(mExpandAllDayRect);
2003cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mExpandAlldayDrawable.draw(canvas);
2004cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            } else {
2005cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mCollapseAlldayDrawable.setBounds(mExpandAllDayRect);
2006cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mCollapseAlldayDrawable.draw(canvas);
2007cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            }
20082fda24534978f55711526dbde747aa50d68dec40RoboErik        }
20098f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    }
2010146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2011c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik    private void drawScrollLine(Rect r, Canvas canvas, Paint p) {
2012e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        final int right = computeDayLeftPosition(mNumDays + 1);
20138f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        final int y = mFirstCell - 1;
20148f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
2015c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        p.setAntiAlias(false);
20168f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setStyle(Style.FILL);
20178f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
20188f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setColor(mCalendarGridLineInnerHorizontalColor);
20198f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
20208f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p);
2021c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        p.setAntiAlias(true);
2022c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik    }
2023c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik
2024e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    // Computes the x position for the left side of the given day
2025e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private int computeDayLeftPosition(int day) {
2026e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int effectiveWidth = mViewWidth - mHoursWidth;
2027e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        return day * effectiveWidth / mNumDays + mHoursWidth;
2028e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2029e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2030e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawAllDayHighlights(Rect r, Canvas canvas, Paint p) {
2031e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // First, color the labels area light gray
2032e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = 0;
2033e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = DAY_HEADER_HEIGHT;
2034e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = 0;
2035e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mViewWidth;
2036e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mBgColor);
2037e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStyle(Style.FILL);
2038e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.drawRect(r, p);
2039e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // and the area that says All day
2040e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = DAY_HEADER_HEIGHT;
2041e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = mFirstCell - 1;
2042e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = 0;
2043e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mHoursWidth;
2044e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.drawRect(r, p);
2045e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int todayIndex = mTodayJulianDay - mFirstJulianDay;
2046e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mNumDays > 1 && todayIndex >= 0 && todayIndex < mNumDays) {
2047e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            // Draw the today highlight
2048e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.top = 0;
2049e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.bottom = mFirstCell - 1;
2050e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.left = computeDayLeftPosition(todayIndex) + 1;
2051e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.right = computeDayLeftPosition(todayIndex + 1);
2052e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setColor(mTodayBgColor);
2053e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setStyle(Style.FILL);
2054e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(r, p);
2055e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2056e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mSelectionAllday) {
2057e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            // Draw the selection highlight on the selected all-day area
2058e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.top = DAY_HEADER_HEIGHT + 1;
2059e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.bottom = mRect.top + mAlldayHeight + ALLDAY_TOP_MARGIN - 2;
2060e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            int daynum = mSelectionDay - mFirstJulianDay;
2061e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.left = computeDayLeftPosition(daynum) + 1;
2062e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.right = computeDayLeftPosition(daynum + 1);
2063e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setColor(mCalendarGridAreaSelected);
2064e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(mRect, p);
2065e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2066e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2067e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2068146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) {
2069146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the horizontal day background banner
20703eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // p.setColor(mCalendarDateBannerBackground);
20713eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.top = 0;
20723eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.bottom = DAY_HEADER_HEIGHT;
20733eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.left = 0;
20743eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP);
20753eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // canvas.drawRect(r, p);
20763eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        //
2077146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Fill the extra space on the right side with the default background
20783eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.left = r.right;
20793eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.right = mViewWidth;
20803eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // p.setColor(mCalendarGridAreaBackground);
20813eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // canvas.drawRect(r, p);
2082e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) {
2083e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            return;
2084146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2085e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int todayNum = mTodayJulianDay - mFirstJulianDay;
2086146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
20873eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTypeface(mBold);
20883eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextAlign(Paint.Align.RIGHT);
2089146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int deltaX = mCellWidth + DAY_GAP;
2090146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cell = mFirstJulianDay;
2091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2092146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] dayNames;
2093146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mDateStrWidth < mCellWidth) {
2094146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dayNames = mDayStrs;
2095146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2096146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dayNames = mDayStrs2Letter;
2097146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2098146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2099d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        p.setAntiAlias(true);
2100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++, cell++) {
21011f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan            int dayOfWeek = day + mFirstVisibleDayOfWeek;
21021f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan            if (dayOfWeek >= 14) {
21031f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan                dayOfWeek -= 14;
2104487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            }
2105487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
21063eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            int color = mCalendarDateBannerTextColor;
2107071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa            if (mNumDays == 1) {
2108071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                if (dayOfWeek == Time.SATURDAY) {
2109071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_saturdayColor;
2110071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                } else if (dayOfWeek == Time.SUNDAY) {
2111071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_sundayColor;
2112071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                }
2113071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa            } else {
2114071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                final int column = day % 7;
2115071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                if (Utils.isSaturday(column, mFirstDayOfWeek)) {
2116071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_saturdayColor;
2117071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                } else if (Utils.isSunday(column, mFirstDayOfWeek)) {
2118071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_sundayColor;
2119071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                }
21203eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            }
21213eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
21223eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setColor(color);
2123e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p);
2124146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
21253eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTypeface(null);
2126146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawAmPm(Canvas canvas, Paint p) {
2129d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        p.setColor(mCalendarAmPmLabel);
2130e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(AMPM_TEXT_SIZE);
2131146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(mBold);
2132146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
2133e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextAlign(Paint.Align.RIGHT);
2134146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String text = mAmString;
2135146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour >= 12) {
2136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            text = mPmString;
2137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2138146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP;
21398f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
2140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour < 12 && mFirstHour + mNumHours > 12) {
2142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Also draw the "PM"
2143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            text = mPmString;
2144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP)
2145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + 2 * mHoursTextHeight + HOUR_GAP;
21468f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
2147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2150e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawCurrentTimeLine(Rect r, final int day, final int top, Canvas canvas,
2151f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            Paint p) {
2152e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1;
2153e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER;
21543eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
21553eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        r.top = top - mCurrentTimeLine.getIntrinsicHeight() / 2;
21563eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight();
2157bd0e834aea317236d574f8738fe11a51fdc87968Erik
21583eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mCurrentTimeLine.setBounds(r);
21593eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mCurrentTimeLine.draw(canvas);
2160bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
2161bd0e834aea317236d574f8738fe11a51fdc87968Erik
2162146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void doDraw(Canvas canvas) {
2163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mPaint;
2164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect r = mRect;
2165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2166e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        drawBgColors(r, canvas, p);
2167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawGridBackground(r, canvas, p);
2168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawHours(r, canvas, p);
2169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw each day
2171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cell = mFirstJulianDay;
2172e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
2173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++, cell++) {
217408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // TODO Wow, this needs cleanup. drawEvents loop through all the
217508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // events on every call.
2176e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            drawEvents(cell, day, HOUR_GAP, canvas, p);
217746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // If this is today
217846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (cell == mTodayJulianDay) {
21791ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
21801ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                        + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
21811ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
218246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // And the current time shows up somewhere on the screen
218346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) {
2184e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    drawCurrentTimeLine(r, day, lineY, canvas, p);
2185bd0e834aea317236d574f8738fe11a51fdc87968Erik                }
2186bd0e834aea317236d574f8738fe11a51fdc87968Erik            }
2187146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2188e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawHours(Rect r, Canvas canvas, Paint p) {
21923eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Comment out as the background will be a drawable
21933eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
2194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw a highlight on the selected hour (if needed)
21952fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionMode != SELECTION_HIDDEN && !mSelectionAllday) {
2196ba902466b19b11d587468e354edc58e542707747Michael Chan            // p.setColor(mCalendarHourSelected);
21973937a47bc5a1afb9701e1d0565fae31f5800590dErik            int daynum = mSelectionDay - mFirstJulianDay;
2198ba902466b19b11d587468e354edc58e542707747Michael Chan            r.top = mSelectionHour * (mCellHeight + HOUR_GAP);
21991ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top + mCellHeight + HOUR_GAP;
2200e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.left = computeDayLeftPosition(daynum);
2201e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.right = computeDayLeftPosition(daynum + 1);
2202ba902466b19b11d587468e354edc58e542707747Michael Chan
2203ba902466b19b11d587468e354edc58e542707747Michael Chan            // Draw a border around the highlighted grid hour.
22041ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            // drawEmptyRect(canvas, r, mSelectionPaint.getColor());
2205ba902466b19b11d587468e354edc58e542707747Michael Chan            saveSelectionPosition(r.left, r.top, r.right, r.bottom);
22063937a47bc5a1afb9701e1d0565fae31f5800590dErik
22073937a47bc5a1afb9701e1d0565fae31f5800590dErik            // Also draw the highlight on the grid
22083937a47bc5a1afb9701e1d0565fae31f5800590dErik            p.setColor(mCalendarGridAreaSelected);
22093937a47bc5a1afb9701e1d0565fae31f5800590dErik            r.top += HOUR_GAP;
22103937a47bc5a1afb9701e1d0565fae31f5800590dErik            r.right -= DAY_GAP;
2211e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setAntiAlias(false);
22123937a47bc5a1afb9701e1d0565fae31f5800590dErik            canvas.drawRect(r, p);
2213ba902466b19b11d587468e354edc58e542707747Michael Chan        }
2214146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
22158f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        setupHourTextPaint(p);
2216146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
22178f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        int y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN;
2218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < 24; i++) {
2220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            String time = mHourStrs[i];
22218f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            canvas.drawText(time, HOURS_LEFT_MARGIN, y, p);
2222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            y += mCellHeight + HOUR_GAP;
2223146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
22268f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private void setupHourTextPaint(Paint p) {
22278f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setColor(mCalendarHourLabelColor);
2228e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
2229f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan        p.setTypeface(Typeface.DEFAULT);
2230e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextAlign(Paint.Align.RIGHT);
22318f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setAntiAlias(true);
22328f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    }
22338f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
2234e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawDayHeader(String dayStr, int day, int cell, Canvas canvas, Paint p) {
2235487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int dateNum = mFirstVisibleDate + day;
2236e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int x;
2237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (dateNum > mMonthLength) {
2238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dateNum -= mMonthLength;
2239146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2240e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
22423eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw day of the month
22433eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        String dateNumStr = String.valueOf(dateNum);
22443eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        if (mNumDays > 1) {
22453eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN;
22463eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
22473eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the month
2248e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN;
2249e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setTextAlign(Align.RIGHT);
22503eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DATE_HEADER_FONT_SIZE);
2251f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(mBold);
22523eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dateNumStr, x, y, p);
22533eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
22543eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the week
2255e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x -= p.measureText(" " + dateNumStr);
22563eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DAY_HEADER_FONT_SIZE);
2257f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(Typeface.DEFAULT);
22583eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dayStr, x, y, p);
2259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2260e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            float y = ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN;
2261e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setTextAlign(Align.LEFT);
2262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2263f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan
22643eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the week
2265e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN;
22663eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DAY_HEADER_FONT_SIZE);
2267f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(Typeface.DEFAULT);
22683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dayStr, x, y, p);
2269d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan
22703eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the month
22713eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN;
22723eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DATE_HEADER_FONT_SIZE);
2273f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(mBold);
22743eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dateNumStr, x, y, p);
2275d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        }
2276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2277146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2278146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawGridBackground(Rect r, Canvas canvas, Paint p) {
2279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint.Style savedStyle = p.getStyle();
2280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2281e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        final float stopX = computeDayLeftPosition(mNumDays + 1);
2282000ba1109e2f4e527e9227fe341598ceeb3fbca6Michael Chan        float y = 0;
22833eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float deltaY = mCellHeight + HOUR_GAP;
228402041687c35441e8a21cb763f1fc3a26525292efDavid Brown        int linesIndex = 0;
22853eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float startY = 0;
22863eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP);
22871ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        float x = mHoursWidth;
2288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
22893eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner horizontal grid lines
22903eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setColor(mCalendarGridLineInnerHorizontalColor);
22913eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
2292e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
22933eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        y = 0;
229402041687c35441e8a21cb763f1fc3a26525292efDavid Brown        linesIndex = 0;
22953eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        for (int hour = 0; hour <= 24; hour++) {
22968f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
229702041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = y;
229802041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopX;
229902041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = y;
23003eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            y += deltaY;
23013eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
230202041687c35441e8a21cb763f1fc3a26525292efDavid Brown        if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) {
230302041687c35441e8a21cb763f1fc3a26525292efDavid Brown            canvas.drawLines(mLines, 0, linesIndex, p);
230402041687c35441e8a21cb763f1fc3a26525292efDavid Brown            linesIndex = 0;
230502041687c35441e8a21cb763f1fc3a26525292efDavid Brown            p.setColor(mCalendarGridLineInnerVerticalColor);
230602041687c35441e8a21cb763f1fc3a26525292efDavid Brown        }
23073eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
23083eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner vertical grid lines
2309e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        for (int day = 0; day <= mNumDays; day++) {
2310e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day);
231102041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
231202041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = startY;
231302041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
231402041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopY;
23153eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
231602041687c35441e8a21cb763f1fc3a26525292efDavid Brown        canvas.drawLines(mLines, 0, linesIndex, p);
23173eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
2318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Restore the saved style.
2319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setStyle(savedStyle);
2320146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
2321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2323e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
2324e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param r
2325e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param canvas
2326e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param p
2327e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
2328e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawBgColors(Rect r, Canvas canvas, Paint p) {
2329e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int todayIndex = mTodayJulianDay - mFirstJulianDay;
2330e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // Draw the hours background color
2331e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = mDestRect.top;
2332e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = mDestRect.bottom;
2333e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = 0;
2334e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mHoursWidth;
2335e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mBgColor);
2336e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStyle(Style.FILL);
2337e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
2338e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.drawRect(r, p);
2339e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mNumDays == 1 && todayIndex == 0) {
2340e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            // Draw a white background for the time later than current time
2341e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
2342e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
2343e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (lineY < mViewStartY + mViewHeight) {
2344e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                lineY = Math.max(lineY, mViewStartY);
2345e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.left = mHoursWidth;
2346e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.right = mViewWidth;
2347e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.top = lineY;
2348e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.bottom = mViewStartY + mViewHeight;
2349e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                p.setColor(mTodayBgColor);
2350e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                canvas.drawRect(r, p);
2351e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
2352e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        } else if (todayIndex >= 0 && todayIndex < mNumDays) {
2353e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.left = computeDayLeftPosition(todayIndex) + 1;
2354e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.right = computeDayLeftPosition(todayIndex + 1);
2355e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.top = mDestRect.top;
2356e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.bottom = mDestRect.bottom;
2357e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setColor(mTodayBgColor);
2358e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(r, p);
2359e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2360e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2361e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2362e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Event getSelectedEvent() {
2364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == null) {
2365146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // There is no event at the selected hour, so create a new event.
2366146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return getNewEvent(mSelectionDay, getSelectedTimeInMillis(),
2367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    getSelectedMinutesSinceMidnight());
2368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return mSelectedEvent;
2370146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2372146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    boolean isEventSelected() {
2373146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return (mSelectedEvent != null);
2374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Event getNewEvent() {
2377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return getNewEvent(mSelectionDay, getSelectedTimeInMillis(),
2378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                getSelectedMinutesSinceMidnight());
2379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    static Event getNewEvent(int julianDay, long utcMillis,
2382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int minutesSinceMidnight) {
2383146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event event = Event.newInstance();
2384146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startDay = julianDay;
2385146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endDay = julianDay;
2386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startMillis = utcMillis;
2387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endMillis = event.startMillis + MILLIS_PER_HOUR;
2388146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startTime = minutesSinceMidnight;
2389146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endTime = event.startTime + MINUTES_PER_HOUR;
2390146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return event;
2391146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2392146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2393146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) {
2394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        float maxWidthF = 0.0f;
2395146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2396146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = strings.length;
2397146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < len; i++) {
2398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            float width = p.measureText(strings[i]);
2399146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            maxWidthF = Math.max(width, maxWidthF);
2400146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2401146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int maxWidth = (int) (maxWidthF + 0.5);
2402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (maxWidth < currentMax) {
2403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            maxWidth = currentMax;
2404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2405146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return maxWidth;
2406146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2407146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2408146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void saveSelectionPosition(float left, float top, float right, float bottom) {
2409146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.left = (int) left;
2410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.right = (int) right;
2411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.top = (int) top;
2412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.bottom = (int) bottom;
2413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2415146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect getCurrentSelectionPosition() {
2416146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect box = new Rect();
2417146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        box.top = mSelectionHour * (mCellHeight + HOUR_GAP);
2418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        box.bottom = box.top + mCellHeight + HOUR_GAP;
2419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int daynum = mSelectionDay - mFirstJulianDay;
2420e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        box.left = computeDayLeftPosition(daynum) + 1;
2421e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        box.right = computeDayLeftPosition(daynum + 1);
2422146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return box;
2423146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
24251ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void setupTextRect(Rect r) {
24261ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom <= r.top || r.right <= r.left) {
24271ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top;
24281ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right = r.left;
242936a4668f603c432d08ad156cd7c731f9838c0212Michael Chan            return;
243036a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
243136a4668f603c432d08ad156cd7c731f9838c0212Michael Chan
24321ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) {
24331ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.top += EVENT_TEXT_TOP_MARGIN;
24341ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom -= EVENT_TEXT_BOTTOM_MARGIN;
243536a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
24361ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) {
24371ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.left += EVENT_TEXT_LEFT_MARGIN;
24381ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right -= EVENT_TEXT_RIGHT_MARGIN;
243936a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
24409c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    }
24419c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan
24421ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void setupAllDayTextRect(Rect r) {
24431ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom <= r.top || r.right <= r.left) {
24441ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top;
24451ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right = r.left;
244636a4668f603c432d08ad156cd7c731f9838c0212Michael Chan            return;
244736a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
244836a4668f603c432d08ad156cd7c731f9838c0212Michael Chan
24491ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) {
24501ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN;
24511ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN;
245236a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
24531ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) {
24541ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
24551ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN;
245636a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
24579c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    }
24589c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan
2459e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private StaticLayout getOrCreateAllDayTextLayout(Paint p) {
2460e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2461e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mAllDayTextLayout == null) {
2462e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            int allDayWidth = mHoursWidth - EVENT_ALL_DAY_TEXT_LEFT_MARGIN
2463e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN;
2464e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mAllDayTextLayout = new StaticLayout(mAllDayString, 0, mAllDayString.length(),
2465e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    new TextPaint(p), allDayWidth, Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null,
2466e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    allDayWidth);
2467e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2468e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        return mAllDayTextLayout;
2469e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2470e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
24713e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh    /**
24723e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh     * Return the layout for a numbered event. Create it if not already existing
24733e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh     */
247408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint,
247508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            Rect r) {
247608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        if (i < 0 || i >= layouts.length) {
24770a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan            return null;
24780a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan        }
24790a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan
248008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        StaticLayout layout = layouts[i];
2481f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        // Check if we have already initialized the StaticLayout and that
2482f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        // the width hasn't changed (due to vertical resizing which causes
2483f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        // re-layout of events at min height)
24841ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (layout == null || r.width() != layout.getWidth()) {
2485e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            SpannableStringBuilder bob = new SpannableStringBuilder();
2486e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (event.title != null) {
2487e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN));
2488e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0);
2489e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(' ');
2490e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
2491e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (event.location != null) {
2492e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(drawTextSanitizer(event.location.toString(),
2493e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                        MAX_EVENT_TEXT_LEN - bob.length()));
2494e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
24953e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
249625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            switch (event.selfAttendeeStatus) {
249725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_INVITED:
249825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    paint.setColor(event.color);
249925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    break;
250025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_DECLINED:
250125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_NONE: // Your own events
250225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_ACCEPTED:
250325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_TENTATIVE:
250425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                default:
250525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    paint.setColor(mEventTextColor);
250625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    break;
2507f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            }
2508f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan
25093e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh            // Leave a one pixel boundary on the left and right of the rectangle for the event
2510e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            layout = new StaticLayout(bob, 0, bob.length(), new TextPaint(paint), r.width(),
25111ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                    Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width());
25123e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
251308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            layouts[i] = layout;
25143e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        }
25153e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
25163e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        return layout;
25173e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh    }
25183e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
25191ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) {
25203e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
2521146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTextSize(NORMAL_FONT_SIZE);
2522146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTextAlign(Paint.Align.LEFT);
2523146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint eventTextPaint = mEventTextPaint;
2524146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
25253eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float startY = DAY_HEADER_HEIGHT;
25262fda24534978f55711526dbde747aa50d68dec40RoboErik        final float stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN;
2527e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        float x = 0;
252802041687c35441e8a21cb763f1fc3a26525292efDavid Brown        int linesIndex = 0;
25293eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
25303eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner vertical grid lines
25313eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setColor(mCalendarGridLineInnerVerticalColor);
25321ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        x = mHoursWidth;
25333eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
25341ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        // Line bounding the top of the all day area
25358f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
25361ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = startY;
2537e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mLines[linesIndex++] = computeDayLeftPosition(mNumDays + 1);
25381ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = startY;
25391ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
2540e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        for (int day = 0; day <= mNumDays; day++) {
2541e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day);
254202041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
254302041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = startY;
254402041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
254502041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopY;
25463eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
2547e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
254802041687c35441e8a21cb763f1fc3a26525292efDavid Brown        canvas.drawLines(mLines, 0, linesIndex, p);
2549146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setStyle(Style.FILL);
2550146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
25513eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
2552146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        float left = mHoursWidth;
2553146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int lastDay = firstDay + numDays - 1;
255408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        final ArrayList<Event> events = mAllDayEvents;
2555146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
25562fda24534978f55711526dbde747aa50d68dec40RoboErik        // Whether or not we should draw the more events text
25572fda24534978f55711526dbde747aa50d68dec40RoboErik        boolean hasMoreEvents = false;
25582fda24534978f55711526dbde747aa50d68dec40RoboErik        // size of the allDay area
25592fda24534978f55711526dbde747aa50d68dec40RoboErik        float drawHeight = mAlldayHeight;
25602fda24534978f55711526dbde747aa50d68dec40RoboErik        // max number of events being drawn in one day of the allday area
25612fda24534978f55711526dbde747aa50d68dec40RoboErik        float numRectangles = mMaxAlldayEvents;
25622fda24534978f55711526dbde747aa50d68dec40RoboErik        // Where to cut off drawn allday events
25632fda24534978f55711526dbde747aa50d68dec40RoboErik        int allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN;
25642fda24534978f55711526dbde747aa50d68dec40RoboErik        // The number of events that weren't drawn in each day
25652fda24534978f55711526dbde747aa50d68dec40RoboErik        mSkippedAlldayEvents = new int[numDays];
25662fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents &&
25672fda24534978f55711526dbde747aa50d68dec40RoboErik                mAnimateDayHeight == 0) {
25682fda24534978f55711526dbde747aa50d68dec40RoboErik            // We draw one fewer event than will fit so that more events text
25692fda24534978f55711526dbde747aa50d68dec40RoboErik            // can be drawn
25702fda24534978f55711526dbde747aa50d68dec40RoboErik            numRectangles = mMaxUnexpandedAlldayEventCount - 1;
25712fda24534978f55711526dbde747aa50d68dec40RoboErik            // We also clip the events above the more events text
25722fda24534978f55711526dbde747aa50d68dec40RoboErik            allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
25732fda24534978f55711526dbde747aa50d68dec40RoboErik            hasMoreEvents = true;
25742fda24534978f55711526dbde747aa50d68dec40RoboErik        } else if (mAnimateDayHeight != 0) {
25752fda24534978f55711526dbde747aa50d68dec40RoboErik            // clip at the end of the animating space
25762fda24534978f55711526dbde747aa50d68dec40RoboErik            allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN;
25772fda24534978f55711526dbde747aa50d68dec40RoboErik        }
2578146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
2579146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
2580146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startDay = event.startDay;
2581146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endDay = event.endDay;
2582c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (startDay > lastDay || endDay < firstDay) {
2583146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
2584c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2585c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (startDay < firstDay) {
2586146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startDay = firstDay;
2587c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2588c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (endDay > lastDay) {
2589146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endDay = lastDay;
2590c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2591146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startIndex = startDay - firstDay;
2592146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endIndex = endDay - firstDay;
25932fda24534978f55711526dbde747aa50d68dec40RoboErik            float height = mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount ? mAnimateDayEventHeight :
25942fda24534978f55711526dbde747aa50d68dec40RoboErik                    drawHeight / numRectangles;
2595146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2596146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Prevent a single event from getting too big
2597f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
2598f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
2599146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2600146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2601146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Leave a one-pixel space between the vertical day lines and the
2602146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // event rectangle.
2603e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            event.left = computeDayLeftPosition(startIndex);
2604e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP;
2605146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            event.top = y + height * event.getColumn();
260625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN;
26072fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
26082fda24534978f55711526dbde747aa50d68dec40RoboErik                // check if we should skip this event. We skip if it starts
26092fda24534978f55711526dbde747aa50d68dec40RoboErik                // after the clip bound or ends after the skip bound and we're
26102fda24534978f55711526dbde747aa50d68dec40RoboErik                // not animating.
26112fda24534978f55711526dbde747aa50d68dec40RoboErik                if (event.top >= allDayEventClip) {
26122fda24534978f55711526dbde747aa50d68dec40RoboErik                    incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
26132fda24534978f55711526dbde747aa50d68dec40RoboErik                    continue;
26142fda24534978f55711526dbde747aa50d68dec40RoboErik                } else if (event.bottom > allDayEventClip) {
26152fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (hasMoreEvents) {
26162fda24534978f55711526dbde747aa50d68dec40RoboErik                        incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
26172fda24534978f55711526dbde747aa50d68dec40RoboErik                        continue;
26182fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
26192fda24534978f55711526dbde747aa50d68dec40RoboErik                    event.bottom = allDayEventClip;
26202fda24534978f55711526dbde747aa50d68dec40RoboErik                }
26212fda24534978f55711526dbde747aa50d68dec40RoboErik            }
262225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            Rect r = drawEventRect(event, canvas, p, eventTextPaint, (int) event.top,
262325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    (int) event.bottom);
26241ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            setupAllDayTextRect(r);
262508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r);
262608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            drawEventText(layout, r, canvas, r.top, r.bottom);
2627146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2628146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Check if this all-day event intersects the selected day
26292fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mSelectionAllday && mComputeSelectedEvents) {
2630146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (startDay <= mSelectionDay && endDay >= mSelectionDay) {
2631146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectedEvents.add(event);
2632146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2633146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2634146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
26352fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) {
26362fda24534978f55711526dbde747aa50d68dec40RoboErik            // If the more allday text should be visible, draw it.
26372fda24534978f55711526dbde747aa50d68dec40RoboErik            p.setColor(mMoreAlldayEventsTextAlpha << 24 & mMoreEventsTextColor);
26382fda24534978f55711526dbde747aa50d68dec40RoboErik            for (int i = 0; i < mSkippedAlldayEvents.length; i++) {
26392fda24534978f55711526dbde747aa50d68dec40RoboErik                if (mSkippedAlldayEvents[i] > 0) {
26402fda24534978f55711526dbde747aa50d68dec40RoboErik                    drawMoreAlldayEvents(canvas, mSkippedAlldayEvents[i], i, p);
26412fda24534978f55711526dbde747aa50d68dec40RoboErik                }
26422fda24534978f55711526dbde747aa50d68dec40RoboErik            }
26432fda24534978f55711526dbde747aa50d68dec40RoboErik        }
2644146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
26452fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionAllday) {
2646146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Compute the neighbors for the list of all-day events that
2647146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // intersect the selected day.
2648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeAllDayNeighbors();
2649146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2650146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Set the selection position to zero so that when we move down
2651146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // to the normal event area, we will highlight the topmost event.
2652146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            saveSelectionPosition(0f, 0f, 0f, 0f);
2653146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2654146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2655146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
26562fda24534978f55711526dbde747aa50d68dec40RoboErik    // Helper method for counting the number of allday events skipped on each day
26572fda24534978f55711526dbde747aa50d68dec40RoboErik    private void incrementSkipCount(int[] counts, int startIndex, int endIndex) {
26582fda24534978f55711526dbde747aa50d68dec40RoboErik        if (counts == null || startIndex < 0 || endIndex > counts.length) {
26592fda24534978f55711526dbde747aa50d68dec40RoboErik            return;
26602fda24534978f55711526dbde747aa50d68dec40RoboErik        }
26612fda24534978f55711526dbde747aa50d68dec40RoboErik        for (int i = startIndex; i <= endIndex; i++) {
26622fda24534978f55711526dbde747aa50d68dec40RoboErik            counts[i]++;
26632fda24534978f55711526dbde747aa50d68dec40RoboErik        }
26642fda24534978f55711526dbde747aa50d68dec40RoboErik    }
26652fda24534978f55711526dbde747aa50d68dec40RoboErik
26662fda24534978f55711526dbde747aa50d68dec40RoboErik    // Draws the "box +n" text for hidden allday events
26672fda24534978f55711526dbde747aa50d68dec40RoboErik    protected void drawMoreAlldayEvents(Canvas canvas, int remainingEvents, int day, Paint p) {
2668e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
2669e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int y = (int) (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - .5f
2670e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                * EVENT_SQUARE_WIDTH + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN);
26712fda24534978f55711526dbde747aa50d68dec40RoboErik        float[] lines = new float[4 * 4];
26722fda24534978f55711526dbde747aa50d68dec40RoboErik        addChipOutline(lines, 0, x, y);
2673e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mMoreEventsTextColor);
2674e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
2675e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
26762fda24534978f55711526dbde747aa50d68dec40RoboErik        canvas.drawLines(lines, p);
2677e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
26782fda24534978f55711526dbde747aa50d68dec40RoboErik        String text = mResources.getQuantityString(R.plurals.month_more_events, remainingEvents);
26792fda24534978f55711526dbde747aa50d68dec40RoboErik        y += EVENT_SQUARE_WIDTH;
26802fda24534978f55711526dbde747aa50d68dec40RoboErik        x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING;
26812fda24534978f55711526dbde747aa50d68dec40RoboErik        canvas.drawText(String.format(text, remainingEvents), x, y, p);
26822fda24534978f55711526dbde747aa50d68dec40RoboErik    }
26832fda24534978f55711526dbde747aa50d68dec40RoboErik
26842fda24534978f55711526dbde747aa50d68dec40RoboErik    // helper method for drawing a box
26852fda24534978f55711526dbde747aa50d68dec40RoboErik    protected int addChipOutline(float[] lines, int count, int x, int y) {
26862fda24534978f55711526dbde747aa50d68dec40RoboErik        // top of box
2687e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        lines[count++] = x - 1;
26882fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y;
2689e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        lines[count++] = x + EVENT_SQUARE_WIDTH + 1;
26902fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y;
26912fda24534978f55711526dbde747aa50d68dec40RoboErik        // right side of box
26922fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = x + EVENT_SQUARE_WIDTH;
26932fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y;
26942fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = x + EVENT_SQUARE_WIDTH;
26952fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y + EVENT_SQUARE_WIDTH;
26962fda24534978f55711526dbde747aa50d68dec40RoboErik        // left side of box
26972fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = x;
26982fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y;
26992fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = x;
27002fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y + EVENT_SQUARE_WIDTH + 1;
27012fda24534978f55711526dbde747aa50d68dec40RoboErik        // bottom of box
27022fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = x;
27032fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y + EVENT_SQUARE_WIDTH;
27042fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = x + EVENT_SQUARE_WIDTH + 1;
27052fda24534978f55711526dbde747aa50d68dec40RoboErik        lines[count++] = y + EVENT_SQUARE_WIDTH;
27062fda24534978f55711526dbde747aa50d68dec40RoboErik
27072fda24534978f55711526dbde747aa50d68dec40RoboErik        return count;
27082fda24534978f55711526dbde747aa50d68dec40RoboErik    }
27092fda24534978f55711526dbde747aa50d68dec40RoboErik
2710146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeAllDayNeighbors() {
2711146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = mSelectedEvents.size();
2712146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (len == 0 || mSelectedEvent != null) {
2713146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
2714146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2715146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2716146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear all the links
2717146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
2718146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
2719146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = null;
2720146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = null;
2721146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = null;
2722146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = null;
2723146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2724146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2725146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // For each event in the selected event list "mSelectedEvents", find
272646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        // its neighbors in the up and down directions. This could be done
2727146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // more efficiently by sorting on the Event.getColumn() field, but
2728146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // the list is expected to be very small.
2729146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2730146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Find the event in the same row as the previously selected all-day
2731146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // event, if any.
2732146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int startPosition = -1;
273368655b5af2b7e5f872a103858ec58a19042123ceRoboErik        if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) {
2734146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            startPosition = mPrevSelectedEvent.getColumn();
2735146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2736146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int maxPosition = -1;
2737146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event startEvent = null;
2738146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event maxPositionEvent = null;
2739146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
2740146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
2741146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int position = ev.getColumn();
2742146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (position == startPosition) {
2743146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEvent = ev;
2744146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (position > maxPosition) {
2745146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                maxPositionEvent = ev;
2746146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                maxPosition = position;
2747146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2748146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int jj = 0; jj < len; jj++) {
2749146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (jj == ii) {
2750146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
2751146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2752146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event neighbor = mSelectedEvents.get(jj);
2753146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborPosition = neighbor.getColumn();
2754146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighborPosition == position - 1) {
2755146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    ev.nextUp = neighbor;
2756146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighborPosition == position + 1) {
2757146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    ev.nextDown = neighbor;
2758146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2759146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2760146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2761146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (startEvent != null) {
2762146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = startEvent;
2763146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2764146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = maxPositionEvent;
2765146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2766146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2767146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2768e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawEvents(int date, int dayIndex, int top, Canvas canvas, Paint p) {
2769146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint eventTextPaint = mEventTextPaint;
2770e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int left = computeDayLeftPosition(dayIndex);
2771e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int cellWidth = computeDayLeftPosition(dayIndex + 1) - left;
2772146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cellHeight = mCellHeight;
2773146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2774146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Use the selected hour as the selection region
2775259b8f9a21fffd3858218581e9daaf689b6b888fErik        Rect selectionArea = mSelectionRect;
2776146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP);
2777146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.bottom = selectionArea.top + cellHeight;
2778146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.left = left;
2779146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.right = selectionArea.left + cellWidth;
2780146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
278108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        final ArrayList<Event> events = mEvents;
2782146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
2783146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        EventGeometry geometry = mEventGeometry;
2784146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
27852fda24534978f55711526dbde747aa50d68dec40RoboErik        final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight;
2786146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
2787146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
2788146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
2789146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
2790146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2791146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
279208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // Don't draw it if it is not visible
279308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            if (event.bottom < mViewStartY || event.top > viewEndY) {
279408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                continue;
279508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            }
279608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
27972fda24534978f55711526dbde747aa50d68dec40RoboErik            if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents
2798146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    && geometry.eventIntersectsSelection(event, selectionArea)) {
2799146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectedEvents.add(event);
2800146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2801146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
280225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            Rect r = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY);
28031ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            setupTextRect(r);
280408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
280508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // Don't draw text if it is not visible
280608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            if (r.top > viewEndY || r.bottom < mViewStartY) {
280708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                continue;
280808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            }
280908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r);
281008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // TODO: not sure why we are 4 pixels off
281146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            drawEventText(layout, r, canvas, mViewStartY + 4, mViewStartY + mViewHeight
28122fda24534978f55711526dbde747aa50d68dec40RoboErik                    - DAY_HEADER_HEIGHT - mAlldayHeight);
2813146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2814146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2815259b8f9a21fffd3858218581e9daaf689b6b888fErik
2816259b8f9a21fffd3858218581e9daaf689b6b888fErik        if (mComputeSelectedEvents) {
2817259b8f9a21fffd3858218581e9daaf689b6b888fErik            mDispatchAccessibilityEventRunnable.mEventType = mSelectedEvent == null ?
2818259b8f9a21fffd3858218581e9daaf689b6b888fErik                    AccessibilityEvent.TYPE_VIEW_SELECTED :
2819259b8f9a21fffd3858218581e9daaf689b6b888fErik                    AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
2820259b8f9a21fffd3858218581e9daaf689b6b888fErik            post(mDispatchAccessibilityEventRunnable);
2821259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
2822259b8f9a21fffd3858218581e9daaf689b6b888fErik
28232fda24534978f55711526dbde747aa50d68dec40RoboErik        if (date == mSelectionDay && !mSelectionAllday && isFocused()
2824146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                && mSelectionMode != SELECTION_HIDDEN) {
2825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeNeighbors();
2826146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2827146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2828146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2829146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Computes the "nearest" neighbor event in four directions (left, right,
2830146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // up, down) for each of the events in the mSelectedEvents array.
2831146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeNeighbors() {
2832146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = mSelectedEvents.size();
2833146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (len == 0 || mSelectedEvent != null) {
2834146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
2835146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2836146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2837146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear all the links
2838146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
2839146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
2840146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = null;
2841146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = null;
2842146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = null;
2843146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = null;
2844146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2845146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2846146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event startEvent = mSelectedEvents.get(0);
284746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int startEventDistance1 = 100000; // any large number
284846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int startEventDistance2 = 100000; // any large number
2849146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevLocation = FROM_NONE;
2850146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevTop;
2851146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevBottom;
2852146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevLeft;
2853146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevRight;
2854146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevCenter = 0;
2855146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect box = getCurrentSelectionPosition();
2856146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mPrevSelectedEvent != null) {
2857146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevTop = (int) mPrevSelectedEvent.top;
2858146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevBottom = (int) mPrevSelectedEvent.bottom;
2859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLeft = (int) mPrevSelectedEvent.left;
2860146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevRight = (int) mPrevSelectedEvent.right;
2861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Check if the previously selected event intersects the previous
286246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // selection box. (The previously selected event may be from a
2863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // much older selection box.)
2864146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (prevTop >= mPrevBox.bottom || prevBottom <= mPrevBox.top
2865146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || prevRight <= mPrevBox.left || prevLeft >= mPrevBox.right) {
2866146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
2867146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevTop = mPrevBox.top;
2868146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevBottom = mPrevBox.bottom;
2869146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevLeft = mPrevBox.left;
2870146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevRight = mPrevBox.right;
2871146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
2872146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Clip the top and bottom to the previous selection box.
2873146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (prevTop < mPrevBox.top) {
2874146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    prevTop = mPrevBox.top;
2875146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2876146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (prevBottom > mPrevBox.bottom) {
2877146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    prevBottom = mPrevBox.bottom;
2878146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2879146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2880146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2881146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Just use the previously drawn selection box
2882146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevTop = mPrevBox.top;
2883146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevBottom = mPrevBox.bottom;
2884146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLeft = mPrevBox.left;
2885146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevRight = mPrevBox.right;
2886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2888146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Figure out where we came from and compute the center of that area.
2889146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (prevLeft >= box.right) {
2890146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was to the right of us.
2891146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_RIGHT;
2892146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevTop + prevBottom) / 2;
2893146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevRight <= box.left) {
2894146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was to the left of us.
2895146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_LEFT;
2896146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevTop + prevBottom) / 2;
2897146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevBottom <= box.top) {
2898146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was above us.
2899146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_ABOVE;
2900146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevLeft + prevRight) / 2;
2901146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevTop >= box.bottom) {
2902146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was below us.
2903146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_BELOW;
2904146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevLeft + prevRight) / 2;
2905146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2906146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2907146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // For each event in the selected event list "mSelectedEvents", search
2908146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // all the other events in that list for the nearest neighbor in 4
2909146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // directions.
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
2913146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startTime = ev.startTime;
2914146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endTime = ev.endTime;
2915146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int left = (int) ev.left;
2916146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int right = (int) ev.right;
2917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int top = (int) ev.top;
2918146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (top < box.top) {
2919146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                top = box.top;
2920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2921146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int bottom = (int) ev.bottom;
2922146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (bottom > box.bottom) {
2923146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                bottom = box.bottom;
2924146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (false) {
2926146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL
2927146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
2928ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                if (DateFormat.is24HourFormat(mContext)) {
2929146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    flags |= DateUtils.FORMAT_24HOUR;
2930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
293146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                String timeRange = DateUtils.formatDateRange(mContext, ev.startMillis,
293246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        ev.endMillis, flags);
293346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                Log.i("Cal", "left: " + left + " right: " + right + " top: " + top + " bottom: "
293446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + bottom + " ev: " + timeRange + " " + ev.title);
293546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
293646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int upDistanceMin = 10000; // any large number
293746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int downDistanceMin = 10000; // any large number
293846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int leftDistanceMin = 10000; // any large number
293946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int rightDistanceMin = 10000; // any large number
2940146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event upEvent = null;
2941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event downEvent = null;
2942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event leftEvent = null;
2943146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event rightEvent = null;
2944146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2945146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Pick the starting event closest to the previously selected event,
294646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // if any. distance1 takes precedence over distance2.
2947146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int distance1 = 0;
2948146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int distance2 = 0;
2949146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (prevLocation == FROM_ABOVE) {
2950146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (left >= prevCenter) {
2951146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = left - prevCenter;
2952146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (right <= prevCenter) {
2953146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - right;
2954146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2955146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = top - prevBottom;
2956146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_BELOW) {
2957146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (left >= prevCenter) {
2958146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = left - prevCenter;
2959146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (right <= prevCenter) {
2960146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - right;
2961146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2962146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = prevTop - bottom;
2963146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_LEFT) {
2964146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (bottom <= prevCenter) {
2965146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - bottom;
2966146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (top >= prevCenter) {
2967146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = top - prevCenter;
2968146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2969146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = left - prevRight;
2970146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_RIGHT) {
2971146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (bottom <= prevCenter) {
2972146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - bottom;
2973146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (top >= prevCenter) {
2974146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = top - prevCenter;
2975146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2976146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = prevLeft - right;
2977146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2978146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (distance1 < startEventDistance1
2979146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || (distance1 == startEventDistance1 && distance2 < startEventDistance2)) {
2980146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEvent = ev;
2981146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEventDistance1 = distance1;
2982146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEventDistance2 = distance2;
2983146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2984146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2985146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // For each neighbor, figure out if it is above or below or left
2986146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // or right of me and compute the distance.
2987146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int jj = 0; jj < len; jj++) {
2988146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (jj == ii) {
2989146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
2990146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2991146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event neighbor = mSelectedEvents.get(jj);
2992146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborLeft = (int) neighbor.left;
2993146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborRight = (int) neighbor.right;
2994146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighbor.endTime <= startTime) {
2995146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely above me.
2996146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // If we overlap the same column, then compute the distance.
2997146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborLeft < right && neighborRight > left) {
2998146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int distance = startTime - neighbor.endTime;
2999146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (distance < upDistanceMin) {
3000146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            upDistanceMin = distance;
3001146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            upEvent = neighbor;
3002146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else if (distance == upDistanceMin) {
3003146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int center = (left + right) / 2;
3004146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentDistance = 0;
3005146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentLeft = (int) upEvent.left;
3006146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentRight = (int) upEvent.right;
3007146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (currentRight <= center) {
3008146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = center - currentRight;
3009146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (currentLeft >= center) {
3010146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = currentLeft - center;
3011146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3012146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3013146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int neighborDistance = 0;
3014146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborRight <= center) {
3015146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = center - neighborRight;
3016146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (neighborLeft >= center) {
3017146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = neighborLeft - center;
3018146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3019146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborDistance < currentDistance) {
3020146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                upDistanceMin = distance;
3021146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                upEvent = neighbor;
3022146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3023146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3024146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3025146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighbor.startTime >= endTime) {
3026146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely below me.
3027146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // If we overlap the same column, then compute the distance.
3028146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborLeft < right && neighborRight > left) {
3029146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int distance = neighbor.startTime - endTime;
3030146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (distance < downDistanceMin) {
3031146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            downDistanceMin = distance;
3032146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            downEvent = neighbor;
3033146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else if (distance == downDistanceMin) {
3034146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int center = (left + right) / 2;
3035146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentDistance = 0;
3036146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentLeft = (int) downEvent.left;
3037146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentRight = (int) downEvent.right;
3038146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (currentRight <= center) {
3039146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = center - currentRight;
3040146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (currentLeft >= center) {
3041146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = currentLeft - center;
3042146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3043146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3044146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int neighborDistance = 0;
3045146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborRight <= center) {
3046146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = center - neighborRight;
3047146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (neighborLeft >= center) {
3048146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = neighborLeft - center;
3049146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3050146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborDistance < currentDistance) {
3051146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                downDistanceMin = distance;
3052146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                downEvent = neighbor;
3053146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3054146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3055146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3056146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3057146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3058146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighborLeft >= right) {
3059146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely to the right of me.
3060146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Take the closest neighbor in the y direction.
3061146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int center = (top + bottom) / 2;
3062146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int distance = 0;
3063146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborBottom = (int) neighbor.bottom;
3064146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborTop = (int) neighbor.top;
3065146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborBottom <= center) {
3066146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = center - neighborBottom;
3067146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (neighborTop >= center) {
3068146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = neighborTop - center;
3069146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3070146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (distance < rightDistanceMin) {
3071146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        rightDistanceMin = distance;
3072146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        rightEvent = neighbor;
3073146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (distance == rightDistanceMin) {
3074146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Pick the closest in the x direction
3075146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int neighborDistance = neighborLeft - right;
3076146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int currentDistance = (int) rightEvent.left - right;
3077146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (neighborDistance < currentDistance) {
3078146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            rightDistanceMin = distance;
3079146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            rightEvent = neighbor;
3080146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3081146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3082146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighborRight <= left) {
3083146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely to the left of me.
3084146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Take the closest neighbor in the y direction.
3085146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int center = (top + bottom) / 2;
3086146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int distance = 0;
3087146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborBottom = (int) neighbor.bottom;
3088146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborTop = (int) neighbor.top;
3089146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborBottom <= center) {
3090146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = center - neighborBottom;
3091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (neighborTop >= center) {
3092146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = neighborTop - center;
3093146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3094146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (distance < leftDistanceMin) {
3095146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        leftDistanceMin = distance;
3096146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        leftEvent = neighbor;
3097146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (distance == leftDistanceMin) {
3098146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Pick the closest in the x direction
3099146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int neighborDistance = left - neighborRight;
3100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int currentDistance = left - (int) leftEvent.right;
3101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (neighborDistance < currentDistance) {
3102146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            leftDistanceMin = distance;
3103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            leftEvent = neighbor;
3104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = upEvent;
3109146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = downEvent;
3110146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = leftEvent;
3111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = rightEvent;
3112146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = startEvent;
3114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
311625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint,
311725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            int visibleTop, int visibleBot) {
311811ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw the Event Rect
31191ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        Rect r = mRect;
312025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN, visibleTop);
312125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN, visibleBot);
31221ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
312325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.right = (int) event.right;
31241ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
3125f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        Drawable eventBoxDrawable;
3126f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        switch (event.selfAttendeeStatus) {
3127f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_INVITED:
312825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                p.setStyle(Style.STROKE);
312925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                break;
3130f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_DECLINED:
313125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                mDeclinedBgDrawable.setColorFilter((event.color & 0x00FFFFFF) | DECLINED_ALPHA,
313225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                        PorterDuff.Mode.OVERLAY);
3133f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan                break;
3134f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_NONE: // Your own events
3135f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_ACCEPTED:
3136f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_TENTATIVE:
3137f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            default:
313825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                p.setStyle(Style.FILL_AND_STROKE);
313925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                // eventBoxDrawable = mAcceptedOrTentativeEventBoxDrawable;
3140f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan                break;
3141f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        }
3142e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
3143e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
3144e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
314525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        if (event.selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) {
314625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            // Magic
314725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            int oldTop = r.top;
314825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            r.top = r.bottom;
314925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            r.bottom = oldTop;
315025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            mDeclinedBgDrawable.setBounds(r);
315125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            mDeclinedBgDrawable.draw(canvas);
315225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        } else {
315325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            int halfStroke = EVENT_RECT_STROKE_WIDTH / 2;
315425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN + halfStroke, visibleTop);
315525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN - halfStroke,
315625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    visibleBot);
315725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            r.left += EVENT_RECT_STROKE_WIDTH / 2;
315825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            r.right -= EVENT_RECT_STROKE_WIDTH / 2;
315925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
316025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            p.setColor(event.color);
316125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            canvas.drawRect(r, p);
316225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        }
3163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
31641ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        p.setStyle(Style.FILL);
3165e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If this event is selected, then use the selection color
3167146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == event) {
31681ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            boolean paintIt = false;
31691ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            int color = 0;
3170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectionMode == SELECTION_PRESSED) {
3171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Also, remember the last selected event that we drew
3172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = event;
3173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // box = mBoxPressed;
317478f58c119af15ed85a727e16cddd793ced4c809dMichael Chan                color = mPressedColor;
31751ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                paintIt = true;
3176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mSelectionMode == SELECTION_SELECTED) {
3177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Also, remember the last selected event that we drew
3178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = event;
3179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // box = mBoxSelected;
318078f58c119af15ed85a727e16cddd793ced4c809dMichael Chan                color = mPressedColor;
31811ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                paintIt = true;
3182146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mSelectionMode == SELECTION_LONGPRESS) {
3183146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // box = mBoxLongPressed;
318478f58c119af15ed85a727e16cddd793ced4c809dMichael Chan                color = mPressedColor;
31851ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                paintIt = true;
31861ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            }
31871ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
31881ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            if (paintIt) {
31891ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                p.setColor(color);
31901ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                canvas.drawRect(r, p);
3191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3192e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
3193e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setAntiAlias(true);
3194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3195146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
319611ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw cal color square border
319725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.top = (int) event.top + CALENDAR_COLOR_SQUARE_V_OFFSET;
319825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.left = (int) event.left + CALENDAR_COLOR_SQUARE_H_OFFSET;
319925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.bottom = r.top + CALENDAR_COLOR_SQUARE_SIZE + 1;
320025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.right = r.left + CALENDAR_COLOR_SQUARE_SIZE + 1;
320125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // p.setColor(0xFFFFFFFF);
320225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // canvas.drawRect(r, p);
32038af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project
320411ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw cal color
320525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.top++;
320625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.left++;
320725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.bottom--;
320825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.right--;
320925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // p.setColor(event.color);
321025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // canvas.drawRect(r, p);
3211e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
32123eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Setup rect for drawEventText which follows
32131ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.top = (int) event.top + EVENT_RECT_TOP_MARGIN;
32141ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN;
32151ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
32161ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN;
32171ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        return r;
3218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
32200d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    private Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],");
32210d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan
32220d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // Sanitize a string before passing it to drawText or else we get little
32230d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // squares. For newlines and tabs before a comma, delete the character.
32240d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // Otherwise, just replace them with a space.
3225f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    private String drawTextSanitizer(String string, int maxEventTextLen) {
32260d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan        Matcher m = drawTextSanitizerFilter.matcher(string);
3227f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        string = m.replaceAll(",");
3228f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan
3229f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        int len = string.length();
3230f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        if (len > maxEventTextLen) {
3231f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            string = string.substring(0, maxEventTextLen);
3232f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            len = maxEventTextLen;
3233f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        }
3234f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan
3235f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        return string.replace('\n', ' ');
32360d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    }
32370d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan
323808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top,
323908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            int bottom) {
32401ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging
32411ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
32421ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        int width = rect.right - rect.left;
32431ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        int height = rect.bottom - rect.top;
3244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3245146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the rectangle is too small for text, then return
32460a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan        if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) {
3247f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            return;
3248f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        }
3249f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan
325008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        int totalLineHeight = 0;
3251f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        int lineCount = eventLayout.getLineCount();
3252f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        for (int i = 0; i < lineCount; i++) {
3253f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            int lineBottom = eventLayout.getLineBottom(i);
3254f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            if (lineBottom <= height) {
325508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                totalLineHeight = lineBottom;
325608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            } else {
325708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                break;
3258f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            }
3259f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        }
3260f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan
326108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight < top) {
3262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
32653e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        // Use a StaticLayout to format the string.
32663e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        canvas.save();
32671ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        canvas.translate(rect.left, rect.top);
32681ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.left = 0;
32691ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.right = width;
32701ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.top = 0;
327108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        rect.bottom = totalLineHeight;
327208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
327308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // There's a bug somewhere. If this rect is outside of a previous
327408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // cliprect, this becomes a no-op. What happens is that the text draw
327508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // past the event rect. The current fix is to not draw the staticLayout
327608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // at all if it is completely out of bound.
32771ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        canvas.clipRect(rect);
32783e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        eventLayout.draw(canvas);
32793e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        canvas.restore();
3280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3281146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
32821ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    // This is to replace p.setStyle(Style.STROKE); canvas.drawRect() since it
32831ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    // doesn't work well with hardware acceleration
32841ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void drawEmptyRect(Canvas canvas, Rect r, int color) {
32851ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        int linesIndex = 0;
32861ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.left;
32871ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.top;
32881ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.right;
32891ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.top;
32901ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
32911ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.left;
32921ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.bottom;
32931ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.right;
32941ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.bottom;
32951ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
32961ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.left;
32971ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.top;
32981ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.left;
32991ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.bottom;
33001ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
33011ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.right;
33021ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.top;
33031ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.right;
33041ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = r.bottom;
33051ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mPaint.setColor(color);
33061ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        canvas.drawLines(mLines, 0, linesIndex, mPaint);
33071ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    }
33081ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
3309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void updateEventDetails() {
3310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == null || mSelectionMode == SELECTION_HIDDEN
3311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                || mSelectionMode == SELECTION_LONGPRESS) {
3312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPopup.dismiss();
3313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3315666fc5e8ddbac15c951aad738bd818ee307eae42Erik        if (mLastPopupEventID == mSelectedEvent.id) {
3316666fc5e8ddbac15c951aad738bd818ee307eae42Erik            return;
3317666fc5e8ddbac15c951aad738bd818ee307eae42Erik        }
3318666fc5e8ddbac15c951aad738bd818ee307eae42Erik
3319666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = mSelectedEvent.id;
3320146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Remove any outstanding callbacks to dismiss the popup.
3322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        getHandler().removeCallbacks(mDismissPopup);
3323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event event = mSelectedEvent;
3325146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView titleView = (TextView) mPopupView.findViewById(R.id.event_title);
3326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        titleView.setText(event.title);
3327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3328146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ImageView imageView = (ImageView) mPopupView.findViewById(R.id.reminder_icon);
3329146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView.setVisibility(event.hasAlarm ? View.VISIBLE : View.GONE);
3330146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3331146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView = (ImageView) mPopupView.findViewById(R.id.repeat_icon);
3332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView.setVisibility(event.isRepeating ? View.VISIBLE : View.GONE);
3333146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3334146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int flags;
3335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (event.allDay) {
333646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            flags = DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_DATE
333746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL;
3338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE
3340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL
3341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
3342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3343ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (DateFormat.is24HourFormat(mContext)) {
3344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            flags |= DateUtils.FORMAT_24HOUR;
3345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
334646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        String timeRange = Utils.formatDateRange(mContext, event.startMillis, event.endMillis,
334746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                flags);
3348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView timeView = (TextView) mPopupView.findViewById(R.id.time);
3349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        timeView.setText(timeRange);
3350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3351146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView whereView = (TextView) mPopupView.findViewById(R.id.where);
3352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final boolean empty = TextUtils.isEmpty(event.location);
3353146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        whereView.setVisibility(empty ? View.GONE : View.VISIBLE);
3354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!empty) whereView.setText(event.location);
3355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.LEFT, mHoursWidth, 5);
3357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        postDelayed(mDismissPopup, POPUP_DISMISS_DELAY);
3358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3360146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // The following routines are called from the parent activity when certain
3361146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // touch events occur.
33624484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doDown(MotionEvent ev) {
3363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mTouchMode = TOUCH_MODE_DOWN;
3364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewStartX = 0;
3365146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mOnFlingCalled = false;
3366146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        getHandler().removeCallbacks(mContinueScroll);
3367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
33692fda24534978f55711526dbde747aa50d68dec40RoboErik    // Kicks off all the animations when the expand allday area is tapped
33702fda24534978f55711526dbde747aa50d68dec40RoboErik    private void doExpandAllDayClick() {
33712fda24534978f55711526dbde747aa50d68dec40RoboErik        mShowAllAllDayEvents = !mShowAllAllDayEvents;
33722fda24534978f55711526dbde747aa50d68dec40RoboErik
33732fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator.setFrameDelay(0);
33742fda24534978f55711526dbde747aa50d68dec40RoboErik
33752fda24534978f55711526dbde747aa50d68dec40RoboErik        // Determine the starting height
33762fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAnimateDayHeight == 0) {
33772fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayHeight = mShowAllAllDayEvents ?
33782fda24534978f55711526dbde747aa50d68dec40RoboErik                    mAlldayHeight - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT : mAlldayHeight;
33792fda24534978f55711526dbde747aa50d68dec40RoboErik        }
33802fda24534978f55711526dbde747aa50d68dec40RoboErik        // Cancel current animations
33812fda24534978f55711526dbde747aa50d68dec40RoboErik        mCancellingAnimations = true;
33822fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayAnimator != null) {
33832fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayAnimator.cancel();
33842fda24534978f55711526dbde747aa50d68dec40RoboErik        }
33852fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayEventAnimator != null) {
33862fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator.cancel();
33872fda24534978f55711526dbde747aa50d68dec40RoboErik        }
33882fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMoreAlldayEventsAnimator != null) {
33892fda24534978f55711526dbde747aa50d68dec40RoboErik            mMoreAlldayEventsAnimator.cancel();
33902fda24534978f55711526dbde747aa50d68dec40RoboErik        }
33912fda24534978f55711526dbde747aa50d68dec40RoboErik        mCancellingAnimations = false;
33922fda24534978f55711526dbde747aa50d68dec40RoboErik        // get new animators
33932fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator = getAllDayAnimator();
33942fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayEventAnimator = getAllDayEventAnimator();
33952fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(this,
33962fda24534978f55711526dbde747aa50d68dec40RoboErik                    "moreAllDayEventsTextAlpha",
33972fda24534978f55711526dbde747aa50d68dec40RoboErik                    mShowAllAllDayEvents ? MORE_EVENTS_MAX_ALPHA : 0,
33982fda24534978f55711526dbde747aa50d68dec40RoboErik                    mShowAllAllDayEvents ? 0 : MORE_EVENTS_MAX_ALPHA);
33992fda24534978f55711526dbde747aa50d68dec40RoboErik
34002fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up delays and start the animators
34012fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator.setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
34022fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator.start();
34032fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.setStartDelay(mShowAllAllDayEvents ? 0 : ANIMATION_DURATION);
34042fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION);
34052fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.start();
34062fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayEventAnimator != null) {
34072fda24534978f55711526dbde747aa50d68dec40RoboErik            // This is the only animator that can return null, so check it
34082fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator
34092fda24534978f55711526dbde747aa50d68dec40RoboErik                    .setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
34102fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator.start();
34112fda24534978f55711526dbde747aa50d68dec40RoboErik        }
34122fda24534978f55711526dbde747aa50d68dec40RoboErik    }
34132fda24534978f55711526dbde747aa50d68dec40RoboErik
34142fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
34152fda24534978f55711526dbde747aa50d68dec40RoboErik     * Figures out the initial heights for allDay events and space when
34162fda24534978f55711526dbde747aa50d68dec40RoboErik     * a view is being set up.
34172fda24534978f55711526dbde747aa50d68dec40RoboErik     */
34182fda24534978f55711526dbde747aa50d68dec40RoboErik    public void initAllDayHeights() {
34192fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) {
34202fda24534978f55711526dbde747aa50d68dec40RoboErik            return;
34212fda24534978f55711526dbde747aa50d68dec40RoboErik        }
34222fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mShowAllAllDayEvents) {
34232fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
34242fda24534978f55711526dbde747aa50d68dec40RoboErik            maxADHeight = Math.min(maxADHeight,
34252fda24534978f55711526dbde747aa50d68dec40RoboErik                    (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
34262fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents;
34272fda24534978f55711526dbde747aa50d68dec40RoboErik        } else {
34282fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayEventHeight = (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
34292fda24534978f55711526dbde747aa50d68dec40RoboErik        }
34302fda24534978f55711526dbde747aa50d68dec40RoboErik    }
34312fda24534978f55711526dbde747aa50d68dec40RoboErik
34322fda24534978f55711526dbde747aa50d68dec40RoboErik    // Sets up an animator for changing the height of allday events
34332fda24534978f55711526dbde747aa50d68dec40RoboErik    private ObjectAnimator getAllDayEventAnimator() {
34342fda24534978f55711526dbde747aa50d68dec40RoboErik        // First calculate the absolute max height
34352fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
34362fda24534978f55711526dbde747aa50d68dec40RoboErik        // Now expand to fit but not beyond the absolute max
34372fda24534978f55711526dbde747aa50d68dec40RoboErik        maxADHeight =
34382fda24534978f55711526dbde747aa50d68dec40RoboErik                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
34392fda24534978f55711526dbde747aa50d68dec40RoboErik        // calculate the height of individual events in order to fit
34402fda24534978f55711526dbde747aa50d68dec40RoboErik        int fitHeight = maxADHeight / mMaxAlldayEvents;
34412fda24534978f55711526dbde747aa50d68dec40RoboErik        int currentHeight = mAnimateDayEventHeight;
34422fda24534978f55711526dbde747aa50d68dec40RoboErik        int desiredHeight =
34432fda24534978f55711526dbde747aa50d68dec40RoboErik                mShowAllAllDayEvents ? fitHeight : (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
34442fda24534978f55711526dbde747aa50d68dec40RoboErik        // if there's nothing to animate just return
34452fda24534978f55711526dbde747aa50d68dec40RoboErik        if (currentHeight == desiredHeight) {
34462fda24534978f55711526dbde747aa50d68dec40RoboErik            return null;
34472fda24534978f55711526dbde747aa50d68dec40RoboErik        }
34482fda24534978f55711526dbde747aa50d68dec40RoboErik
34492fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the animator with the calculated values
34502fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayEventHeight",
34512fda24534978f55711526dbde747aa50d68dec40RoboErik                currentHeight, desiredHeight);
34522fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.setDuration(ANIMATION_DURATION);
34532fda24534978f55711526dbde747aa50d68dec40RoboErik        return animator;
34542fda24534978f55711526dbde747aa50d68dec40RoboErik    }
34552fda24534978f55711526dbde747aa50d68dec40RoboErik
34562fda24534978f55711526dbde747aa50d68dec40RoboErik    // Sets up an animator for changing the height of the allday area
34572fda24534978f55711526dbde747aa50d68dec40RoboErik    private ObjectAnimator getAllDayAnimator() {
34582fda24534978f55711526dbde747aa50d68dec40RoboErik        // Calculate the absolute max height
34592fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
34602fda24534978f55711526dbde747aa50d68dec40RoboErik        // Find the desired height but don't exceed abs max
34612fda24534978f55711526dbde747aa50d68dec40RoboErik        maxADHeight =
34622fda24534978f55711526dbde747aa50d68dec40RoboErik                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
34632fda24534978f55711526dbde747aa50d68dec40RoboErik        // calculate the current and desired heights
34642fda24534978f55711526dbde747aa50d68dec40RoboErik        int currentHeight = mAnimateDayHeight != 0 ? mAnimateDayHeight : mAlldayHeight;
34652fda24534978f55711526dbde747aa50d68dec40RoboErik        int desiredHeight = mShowAllAllDayEvents ? maxADHeight :
34662fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1);
34672fda24534978f55711526dbde747aa50d68dec40RoboErik
34682fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the animator with the calculated values
34692fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayHeight",
34702fda24534978f55711526dbde747aa50d68dec40RoboErik                currentHeight, desiredHeight);
34712fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.setDuration(ANIMATION_DURATION);
34722fda24534978f55711526dbde747aa50d68dec40RoboErik
34732fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.addListener(new AnimatorListenerAdapter() {
34742fda24534978f55711526dbde747aa50d68dec40RoboErik            @Override
34752fda24534978f55711526dbde747aa50d68dec40RoboErik            public void onAnimationEnd(Animator animation) {
34762fda24534978f55711526dbde747aa50d68dec40RoboErik                if (!mCancellingAnimations) {
34772fda24534978f55711526dbde747aa50d68dec40RoboErik                    // when finished, set this to 0 to signify not animating
34782fda24534978f55711526dbde747aa50d68dec40RoboErik                    mAnimateDayHeight = 0;
3479cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                    mUseExpandIcon = !mShowAllAllDayEvents;
34802fda24534978f55711526dbde747aa50d68dec40RoboErik                }
34812fda24534978f55711526dbde747aa50d68dec40RoboErik                mRemeasure = true;
34822fda24534978f55711526dbde747aa50d68dec40RoboErik                invalidate();
34832fda24534978f55711526dbde747aa50d68dec40RoboErik            }
34842fda24534978f55711526dbde747aa50d68dec40RoboErik        });
34852fda24534978f55711526dbde747aa50d68dec40RoboErik        return animator;
34862fda24534978f55711526dbde747aa50d68dec40RoboErik    }
34872fda24534978f55711526dbde747aa50d68dec40RoboErik
34882fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the 'box +n' alpha text used by the animator
34892fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setMoreAllDayEventsTextAlpha(int alpha) {
34902fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsTextAlpha = alpha;
34912fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
34922fda24534978f55711526dbde747aa50d68dec40RoboErik    }
34932fda24534978f55711526dbde747aa50d68dec40RoboErik
34942fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the height of the allday area used by the animator
34952fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setAnimateDayHeight(int height) {
34962fda24534978f55711526dbde747aa50d68dec40RoboErik        mAnimateDayHeight = height;
34972fda24534978f55711526dbde747aa50d68dec40RoboErik        mRemeasure = true;
34982fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
34992fda24534978f55711526dbde747aa50d68dec40RoboErik    }
35002fda24534978f55711526dbde747aa50d68dec40RoboErik
35012fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the height of allday events used by the animator
35022fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setAnimateDayEventHeight(int height) {
35032fda24534978f55711526dbde747aa50d68dec40RoboErik        mAnimateDayEventHeight = height;
35042fda24534978f55711526dbde747aa50d68dec40RoboErik        mRemeasure = true;
35052fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
35062fda24534978f55711526dbde747aa50d68dec40RoboErik    }
35072fda24534978f55711526dbde747aa50d68dec40RoboErik
35084484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doSingleTapUp(MotionEvent ev) {
3509bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        if (!mHandleActionUp || mScrolling) {
3510d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            return;
3511d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3512d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3513146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int x = (int) ev.getX();
3514146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int y = (int) ev.getY();
3515146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectedDay = mSelectionDay;
3516146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectedHour = mSelectionHour;
3517146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
35182fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
35192fda24534978f55711526dbde747aa50d68dec40RoboErik            // check if the tap was in the allday expansion area
35202fda24534978f55711526dbde747aa50d68dec40RoboErik            int bottom = MAX_UNEXPANDED_ALLDAY_HEIGHT + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
35212fda24534978f55711526dbde747aa50d68dec40RoboErik            if((x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight)
35222fda24534978f55711526dbde747aa50d68dec40RoboErik                    || (!mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom &&
35232fda24534978f55711526dbde747aa50d68dec40RoboErik                            y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)) {
35242fda24534978f55711526dbde747aa50d68dec40RoboErik                doExpandAllDayClick();
35252fda24534978f55711526dbde747aa50d68dec40RoboErik                return;
35262fda24534978f55711526dbde747aa50d68dec40RoboErik            }
35272fda24534978f55711526dbde747aa50d68dec40RoboErik        }
35282fda24534978f55711526dbde747aa50d68dec40RoboErik
3529146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean validPosition = setSelectionFromPosition(x, y);
3530146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!validPosition) {
3531360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            if (y < DAY_HEADER_HEIGHT) {
3532360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                Time selectedTime = new Time(mBaseDate);
3533360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.setJulianDay(mSelectionDay);
3534360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.hour = mSelectionHour;
3535360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.normalize(true /* ignore isDst */);
3536360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                mController.sendEvent(this, EventType.GO_TO, null, null, selectedTime, -1,
3537360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                        ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null);
3538360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            }
3539146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3540146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3541146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
354280bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        mSelectionMode = SELECTION_SELECTED;
3543146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
3544146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
354580bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        if (mSelectedEvent != null) {
354680bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan            // If the tap is on an event, launch the "View event" view
3547344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mSelectedEvent.id,
3548344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan                    mSelectedEvent.startMillis, mSelectedEvent.endMillis, (int) ev.getRawX(),
354987f993ff96172f508d3f241f8a425f953c7eb739RoboErik                    (int) ev.getRawY(), getSelectedTimeInMillis());
3550344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan        } else if (selectedDay == mSelectionDay && selectedHour == mSelectionHour) {
3551344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            // If the tap is on an already selected hour slot, then create a new
3552344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            // event
3553344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1,
355487f993ff96172f508d3f241f8a425f953c7eb739RoboErik                    getSelectedTimeInMillis(), 0, (int) ev.getRawX(), (int) ev.getRawY(), -1);
3555344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan        } else {
3556344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            Time startTime = new Time(mBaseDate);
3557344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.setJulianDay(mSelectionDay);
3558344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.hour = mSelectionHour;
3559344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.normalize(true /* ignore isDst */);
356083b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
356183b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            Time endTime = new Time(startTime);
356283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            endTime.hour++;
356383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
356446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mController.sendEvent(this, EventType.GO_TO, startTime, endTime, -1, ViewType.CURRENT,
356546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    CalendarController.EXTRA_GOTO_TIME, null, null);
3566146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3567146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3568146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
35694484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doLongPress(MotionEvent ev) {
3570bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        if (mScrolling) {
3571bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan            return;
3572bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        }
3573bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan
3574d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        // Scale gesture in progress
3575d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mStartingSpanY != 0) {
3576d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            return;
3577d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3578d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
357980bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        int x = (int) ev.getX();
358080bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        int y = (int) ev.getY();
358180bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan
358280bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        boolean validPosition = setSelectionFromPosition(x, y);
358380bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        if (!validPosition) {
358480bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan            // return if the touch wasn't on an area of concern
358580bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan            return;
358680bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        }
358780bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan
3588146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_LONGPRESS;
3589146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
3590146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        performLongClick();
3591146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3592146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
35934484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) {
359495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        cancelAnimation();
35954f58287a1e02325337e39073a50bbb880094099fRoboErik        if (mStartingScroll) {
35964f58287a1e02325337e39073a50bbb880094099fRoboErik            mInitialScrollX = 0;
35974f58287a1e02325337e39073a50bbb880094099fRoboErik            mInitialScrollY = 0;
35984f58287a1e02325337e39073a50bbb880094099fRoboErik            mStartingScroll = false;
35994f58287a1e02325337e39073a50bbb880094099fRoboErik        }
36006d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
36014f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollX += deltaX;
36024f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollY += deltaY;
36034f58287a1e02325337e39073a50bbb880094099fRoboErik        int distanceX = (int) mInitialScrollX;
36044f58287a1e02325337e39073a50bbb880094099fRoboErik        int distanceY = (int) mInitialScrollY;
3605146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3606146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If we haven't figured out the predominant scroll direction yet,
3607146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // then do it now.
3608146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mTouchMode == TOUCH_MODE_DOWN) {
3609146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int absDistanceX = Math.abs(distanceX);
3610146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int absDistanceY = Math.abs(distanceY);
3611146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mScrollStartY = mViewStartY;
3612146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPreviousDirection = 0;
3613146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3614f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            if (absDistanceX > absDistanceY) {
3615146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mTouchMode = TOUCH_MODE_HSCROLL;
3616146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartX = distanceX;
3617146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                initNextView(-mViewStartX);
3618146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
3619146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mTouchMode = TOUCH_MODE_VSCROLL;
3620146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3621146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
3622146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // We are already scrolling horizontally, so check if we
3623146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // changed the direction of scrolling so that the other week
3624146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // is now visible.
3625146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartX = distanceX;
3626146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (distanceX != 0) {
3627146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int direction = (distanceX > 0) ? 1 : -1;
3628146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (direction != mPreviousDirection) {
3629146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // The user has switched the direction of scrolling
3630146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // so re-init the next view
3631146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    initNextView(-mViewStartX);
3632146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mPreviousDirection = direction;
3633146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3634146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3635146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3636146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3637146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) {
3638146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartY = mScrollStartY + distanceY;
3639146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mViewStartY < 0) {
3640146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = 0;
3641146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mViewStartY > mMaxViewStartY) {
3642146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = mMaxViewStartY;
3643146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3644146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeFirstHour();
3645146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3646146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3647146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = true;
3648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3649d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        mSelectionMode = SELECTION_HIDDEN;
3650146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
3651146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3652146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3653f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    private void cancelAnimation() {
3654f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        Animation in = mViewSwitcher.getInAnimation();
3655f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        if (in != null) {
365695d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            // cancel() doesn't terminate cleanly.
365795d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            in.scaleCurrentDuration(0);
36586d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
3659f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        Animation out = mViewSwitcher.getOutAnimation();
3660f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        if (out != null) {
366195d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            // cancel() doesn't terminate cleanly.
366295d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            out.scaleCurrentDuration(0);
3663f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        }
3664f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    }
3665f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan
3666f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
3667f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        cancelAnimation();
36686d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
3669146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_HIDDEN;
3670146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mOnFlingCalled = true;
367190a68379c01137781249156742cfc9ba21684b83Michael Chan
367290a68379c01137781249156742cfc9ba21684b83Michael Chan        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
3673030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            // Horizontal fling.
367411638ad4fb56b978770aebe75deac7a0e6ac60d2Erik            // initNextView(deltaX);
367590a68379c01137781249156742cfc9ba21684b83Michael Chan            mTouchMode = TOUCH_MODE_INITIAL_STATE;
367690a68379c01137781249156742cfc9ba21684b83Michael Chan            if (DEBUG) Log.d(TAG, "doFling: velocityX " + velocityX);
367790a68379c01137781249156742cfc9ba21684b83Michael Chan            int deltaX = (int) e2.getX() - (int) e1.getX();
36782c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            switchViews(deltaX < 0, mViewStartX, mViewWidth, velocityX);
3679146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartX = 0;
3680146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3681146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3682146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
368390a68379c01137781249156742cfc9ba21684b83Michael Chan        if ((mTouchMode & TOUCH_MODE_VSCROLL) == 0) {
368490a68379c01137781249156742cfc9ba21684b83Michael Chan            if (DEBUG) Log.d(TAG, "doFling: no fling");
368590a68379c01137781249156742cfc9ba21684b83Michael Chan            return;
368690a68379c01137781249156742cfc9ba21684b83Michael Chan        }
368790a68379c01137781249156742cfc9ba21684b83Michael Chan
3688030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        // Vertical fling.
368990a68379c01137781249156742cfc9ba21684b83Michael Chan        mTouchMode = TOUCH_MODE_INITIAL_STATE;
3690030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        mViewStartX = 0;
3691030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown
36923f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        if (DEBUG) {
36933f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            Log.d(TAG, "doFling: mViewStartY" + mViewStartY + " velocityY " + velocityY);
36943f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        }
36953f6328f579b05c02606572c777541b97aa04f9f3Michael Chan
3696146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Continue scrolling vertically
36973f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScrolling = true;
36983f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScroller.fling(0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */,
36993f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                (int) -velocityY, 0 /* minX */, 0 /* maxX */, 0 /* minY */, mMaxViewStartY);
3700146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        post(mContinueScroll);
3701146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3702146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3703146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean initNextView(int deltaX) {
3704146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Change the view to the previous day or week
3705d190418220467c02327dd9b3ae1eae238889720bMichael Chan        DayView view = (DayView) mViewSwitcher.getNextView();
3706146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time date = view.mBaseDate;
3707146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        date.set(mBaseDate);
3708146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean switchForward;
3709146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (deltaX > 0) {
3710146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.monthDay -= mNumDays;
3711146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            view.mSelectionDay = mSelectionDay - mNumDays;
3712146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchForward = false;
3713146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3714146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.monthDay += mNumDays;
3715146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            view.mSelectionDay = mSelectionDay + mNumDays;
3716146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchForward = true;
3717146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3718146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        date.normalize(true /* ignore isDst */);
3719146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        initView(view);
3720ad36a3c3cde7a2ec6d3a35d2529d46f03bd8d59dMichael Chan        view.layout(getLeft(), getTop(), getRight(), getBottom());
3721146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.reloadEvents();
3722146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return switchForward;
3723146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3724146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3725d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
3726d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public boolean onScaleBegin(ScaleGestureDetector detector) {
3727d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mHandleActionUp = false;
37282fda24534978f55711526dbde747aa50d68dec40RoboErik        float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
3729d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP);
3730d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3731d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY()));
3732d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mCellHeightBeforeScaleGesture = mCellHeight;
3733d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3734d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (DEBUG) {
3735d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
3736d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.d(TAG, "mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: "
3737d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:"
3738d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + mCellHeight);
3739d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3740d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3741d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        return true;
3742d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
3743d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3744d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
3745d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public boolean onScale(ScaleGestureDetector detector) {
3746d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        float spanY = Math.abs(detector.getCurrentSpanY());
3747d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3748d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY);
3749d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3750d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mCellHeight < mMinCellHeight) {
3751d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            // If mStartingSpanY is too small, even a small increase in the
3752d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT
3753d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mStartingSpanY = Math.max(MIN_Y_SPAN, spanY);
3754d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = mMinCellHeight;
3755d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeightBeforeScaleGesture = mMinCellHeight;
3756d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        } else if (mCellHeight > MAX_CELL_HEIGHT) {
3757d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mStartingSpanY = spanY;
3758d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = MAX_CELL_HEIGHT;
3759d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT;
3760d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3761d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
37622fda24534978f55711526dbde747aa50d68dec40RoboErik        int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
3763d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels;
3764d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
3765d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3766d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (DEBUG) {
3767d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
3768d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.d(TAG, " mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: "
3769d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:"
3770d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + mCellHeight + " SpanY:" + detector.getCurrentSpanY());
3771d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3772d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3773d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mViewStartY < 0) {
3774d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = 0;
3775d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
3776d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    / (float) (mCellHeight + DAY_GAP);
3777d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        } else if (mViewStartY > mMaxViewStartY) {
3778d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = mMaxViewStartY;
3779d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
3780d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    / (float) (mCellHeight + DAY_GAP);
3781d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3782d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        computeFirstHour();
3783d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3784d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mRemeasure = true;
3785d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        invalidate();
3786d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        return true;
3787d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
3788d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3789d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
3790d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public void onScaleEnd(ScaleGestureDetector detector) {
37914f58287a1e02325337e39073a50bbb880094099fRoboErik        mScrollStartY = mViewStartY;
37924f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollY = 0;
37934f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollX = 0;
3794d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mStartingSpanY = 0;
3795d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
3796d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3797146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
3798146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onTouchEvent(MotionEvent ev) {
3799146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int action = ev.getAction();
3800146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
38011ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan        if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) {
38021ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan            mScaleGestureDetector.onTouchEvent(ev);
38031ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan            if (mScaleGestureDetector.isInProgress()) {
38041ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan                return true;
38051ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan            }
3806d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3807d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3808146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (action) {
380946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_DOWN:
38104f58287a1e02325337e39073a50bbb880094099fRoboErik                mStartingScroll = true;
381146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (DEBUG) Log.e(TAG, "ACTION_DOWN");
38122fda24534978f55711526dbde747aa50d68dec40RoboErik                int bottom = mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
38132fda24534978f55711526dbde747aa50d68dec40RoboErik                if (ev.getY() < bottom) {
38142fda24534978f55711526dbde747aa50d68dec40RoboErik                    mTouchStartedInAlldayArea = true;
38152fda24534978f55711526dbde747aa50d68dec40RoboErik                } else {
38162fda24534978f55711526dbde747aa50d68dec40RoboErik                    mTouchStartedInAlldayArea = false;
38172fda24534978f55711526dbde747aa50d68dec40RoboErik                }
3818d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                mHandleActionUp = true;
381946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
3820d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                return true;
382146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
382246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_MOVE:
382346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (DEBUG) Log.e(TAG, "ACTION_MOVE");
382446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
3825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
382646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
382746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_UP:
382846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (DEBUG) Log.e(TAG, "ACTION_UP " + mHandleActionUp);
38294f58287a1e02325337e39073a50bbb880094099fRoboErik                mStartingScroll = false;
383046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
383146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (!mHandleActionUp) {
383246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mHandleActionUp = true;
38337abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project                    return true;
383446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
383546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mOnFlingCalled) {
383646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
383746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
383846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
383946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mTouchMode = TOUCH_MODE_INITIAL_STATE;
384046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) {
384146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // The user has gone beyond the threshold so switch views
384246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views");
38432c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                        switchViews(mViewStartX > 0, mViewStartX, mViewWidth, 0);
384446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mViewStartX = 0;
384546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        return true;
384646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    } else {
384746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // Not beyond the threshold so invalidate which will cause
384846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // the view to snap back. Also call recalc() to ensure
384946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // that we have the correct starting date and title.
385046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back");
385146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        recalc();
385246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        invalidate();
385346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mViewStartX = 0;
385446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
385546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
385646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
385746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // If we were scrolling, then reset the selected hour so that it
385846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // is visible.
385946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mScrolling) {
386046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mScrolling = false;
386146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    resetSelectedHour();
3862146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
3863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
386446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return true;
3865146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
386646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // This case isn't expected to happen.
386746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_CANCEL:
386846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (DEBUG) Log.e(TAG, "ACTION_CANCEL");
386946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
3870146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mScrolling = false;
3871146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                resetSelectedHour();
3872146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
387346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
387446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            default:
38754f58287a1e02325337e39073a50bbb880094099fRoboErik                if (DEBUG) Log.e(TAG, "Not MotionEvent " + action);
387646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mGestureDetector.onTouchEvent(ev)) {
387746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
387846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
387946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onTouchEvent(ev);
3880146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3881146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3882146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3883146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
3884146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        MenuItem item;
3885146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the trackball is held down, then the context menu pops up and
388746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        // we never get onKeyUp() for the long-press. So check for it here
3888146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // and change the selection to the long-press state.
3889146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionMode != SELECTION_LONGPRESS) {
3890146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionMode = SELECTION_LONGPRESS;
3891146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
3892146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3893146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3894e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        final long startMillis = getSelectedTimeInMillis();
3895146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int flags = DateUtils.FORMAT_SHOW_TIME
3896146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                | DateUtils.FORMAT_CAP_NOON_MIDNIGHT
3897146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                | DateUtils.FORMAT_SHOW_WEEKDAY;
3898c5f92d7bdeb1950ace783cc022aa41a4b4f00cd6Erik        final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags);
3899146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        menu.setHeaderTitle(title);
3900e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3901146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numSelectedEvents = mSelectedEvents.size();
3902146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays == 1) {
3903146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Day view.
3904146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3905146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is a selected event, then allow it to be viewed and
3906146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // edited.
3907146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (numSelectedEvents >= 1) {
39084484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view);
3909146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
3910146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_info_details);
3911146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3912ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                int accessLevel = getEventAccessLevel(mContext, mSelectedEvent);
3913a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel == ACCESS_LEVEL_EDIT) {
39144484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit);
3915146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
3916146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_edit);
3917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setAlphabeticShortcut('e');
3918a505627659f6bfba8869eac8089f7030f2d0d92cErik                }
3919146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3920a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel >= ACCESS_LEVEL_DELETE) {
39214484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete);
3922146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
3923146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_delete);
3924146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
39264484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
3927146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
3928146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_add);
3929146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setAlphabeticShortcut('n');
3930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
3931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Otherwise, if the user long-pressed on a blank hour, allow
393246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // them to create an event. They can also do this by tapping.
39334484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
3934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
3935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_add);
3936146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setAlphabeticShortcut('n');
3937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3938146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Week view.
3940e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is a selected event, then allow it to be viewed and
3942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // edited.
3943146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (numSelectedEvents >= 1) {
39444484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view);
3945146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
3946146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_info_details);
3947146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3948ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                int accessLevel = getEventAccessLevel(mContext, mSelectedEvent);
3949a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel == ACCESS_LEVEL_EDIT) {
39504484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit);
3951146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
3952146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_edit);
3953146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setAlphabeticShortcut('e');
3954a505627659f6bfba8869eac8089f7030f2d0d92cErik                }
3955146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3956a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel >= ACCESS_LEVEL_DELETE) {
39574484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete);
3958146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
3959146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_delete);
3960146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
39614484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            }
3962146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
39634484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
39644484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setOnMenuItemClickListener(mContextMenuHandler);
39654484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setIcon(android.R.drawable.ic_menu_add);
39664484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setAlphabeticShortcut('n');
3967146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
39684484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item = menu.add(0, MENU_DAY, 0, R.string.show_day_view);
39694484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setOnMenuItemClickListener(mContextMenuHandler);
39704484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setIcon(android.R.drawable.ic_menu_day);
39714484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setAlphabeticShortcut('d');
3972146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3973146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3974146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.dismiss();
3975146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3976146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3977146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private class ContextMenuHandler implements MenuItem.OnMenuItemClickListener {
3978146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public boolean onMenuItemClick(MenuItem item) {
3979146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switch (item.getItemId()) {
39804484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_VIEW: {
3981146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
3982a7694eead7cbb293990bc89c3906e17bad36194cErik                        mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT_DETAILS,
39839e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                                mSelectedEvent.id, mSelectedEvent.startMillis,
398487f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                mSelectedEvent.endMillis, 0, 0, -1);
3985146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3986146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
3987146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
39884484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_EDIT: {
3989146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
39909e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                        mController.sendEventRelatedEvent(this, EventType.EDIT_EVENT,
39919e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                                mSelectedEvent.id, mSelectedEvent.startMillis,
399287f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                mSelectedEvent.endMillis, 0, 0, -1);
3993146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3994146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
3995146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
39964484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_DAY: {
3997487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                    mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1,
39989e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                            ViewType.DAY);
3999146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4000146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
40014484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_AGENDA: {
4002487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                    mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1,
40039e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                            ViewType.AGENDA);
4004146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4005146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
40064484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_CREATE: {
4007146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long startMillis = getSelectedTimeInMillis();
4008146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
40099e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1,
401087f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            startMillis, endMillis, 0, 0, -1);
4011146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4012146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
40134484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_DELETE: {
4014146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
4015146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        Event selectedEvent = mSelectedEvent;
4016146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long begin = selectedEvent.startMillis;
4017146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long end = selectedEvent.endMillis;
4018146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long id = selectedEvent.id;
40199e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                        mController.sendEventRelatedEvent(this, EventType.DELETE_EVENT, id, begin,
402087f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                end, 0, 0, -1);
4021146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4022146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4023146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4024146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                default: {
4025146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    return false;
4026146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4027146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4028146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
4029146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4030146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4031146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4032a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static int getEventAccessLevel(Context context, Event e) {
4033146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ContentResolver cr = context.getContentResolver();
4034146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4035ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        int accessLevel = Calendars.CAL_ACCESS_NONE;
4036146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4037146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Get the calendar id for this event
4038146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Cursor cursor = cr.query(ContentUris.withAppendedId(Events.CONTENT_URI, e.id),
4039146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                new String[] { Events.CALENDAR_ID },
4040146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* selection */,
4041146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* selectionArgs */,
4042146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* sort */);
4043e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4044e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        if (cursor == null) {
4045a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
4046146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4047e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4048e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        if (cursor.getCount() == 0) {
4049e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan            cursor.close();
4050a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
4051e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        }
4052e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4053146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cursor.moveToFirst();
4054146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long calId = cursor.getLong(0);
4055697bd6a8cec36cc75c72603674e57e4c6fe42dbfKen Shirriff        cursor.close();
4056146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4057146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Uri uri = Calendars.CONTENT_URI;
4058146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String where = String.format(CALENDARS_WHERE, calId);
4059146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cursor = cr.query(uri, CALENDARS_PROJECTION, where, null, null);
4060146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
40616d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        String calendarOwnerAccount = null;
4062146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (cursor != null) {
4063146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            cursor.moveToFirst();
4064ced0eb67ef49c3203f72520c296e73d9cb4f654bMichael Chan            accessLevel = cursor.getInt(CALENDARS_INDEX_ACCESS_LEVEL);
40656d34cec523930368976bc8d358d0a6de1da3462cMichael Chan            calendarOwnerAccount = cursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
4066146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            cursor.close();
4067146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4068e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4069ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        if (accessLevel < Calendars.CAL_ACCESS_CONTRIBUTOR) {
4070a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
40716d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        }
4072146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
40736d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        if (e.guestsCanModify) {
4074a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_EDIT;
4075a505627659f6bfba8869eac8089f7030f2d0d92cErik        }
4076a505627659f6bfba8869eac8089f7030f2d0d92cErik
407746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (!TextUtils.isEmpty(calendarOwnerAccount)
407846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                && calendarOwnerAccount.equalsIgnoreCase(e.organizer)) {
4079a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_EDIT;
4080146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4081146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4082a505627659f6bfba8869eac8089f7030f2d0d92cErik        return ACCESS_LEVEL_DELETE;
4083146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4084146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4085146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
4086146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position.
4087146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * If the touch position is not within the displayed grid, then this
4088146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * method returns false.
4089146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
4090146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param x the x position of the touch
4091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param y the y position of the touch
4092146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @return true if the touch position is valid
4093146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
4094360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan    private boolean setSelectionFromPosition(int x, final int y) {
4095146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (x < mHoursWidth) {
4096360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            x = mHoursWidth;
4097146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4098146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4099146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP);
4100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (day >= mNumDays) {
4101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            day = mNumDays - 1;
4102146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        day += mFirstJulianDay;
4104360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        mSelectionDay = day;
4105360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan
4106360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        if (y < DAY_HEADER_HEIGHT) {
4107360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            return false;
4108360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        }
410946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
411046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mSelectionHour = mFirstHour; /* First fully visible hour */
411146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
411246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (y < mFirstCell) {
41132fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = true;
4114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
411546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // y is now offset from top of the scrollable region
411655a26d7062d5aa1a1b001b7271bb137276ab807eErik            int adjustedY = y - mFirstCell;
411746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
411855a26d7062d5aa1a1b001b7271bb137276ab807eErik            if (adjustedY < mFirstHourOffset) {
411946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                --mSelectionHour; /* In the partially visible hour */
412046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else {
412155a26d7062d5aa1a1b001b7271bb137276ab807eErik                mSelectionHour += (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP);
412246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
412346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
41242fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = false;
4125146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4126360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan
4127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        findSelectedEvent(x, y);
412846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
412946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//        Log.i("Cal", "setSelectionFromPosition( " + x + ", " + y + " ) day: " + day + " hour: "
413046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                + mSelectionHour + " mFirstCell: " + mFirstCell + " mFirstHourOffset: "
413146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                + mFirstHourOffset);
4132146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//        if (mSelectedEvent != null) {
413346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//            Log.i("Cal", "  num events: " + mSelectedEvents.size() + " event: "
413446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                    + mSelectedEvent.title);
4135146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//            for (Event ev : mSelectedEvents) {
4136146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL
4137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                        | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
413846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                String timeRange = formatDateRange(mContext, ev.startMillis, ev.endMillis, flags);
4139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//
4140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                Log.i("Cal", "  " + timeRange + " " + ev.title);
4141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//            }
4142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//        }
4143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return true;
4144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void findSelectedEvent(int x, int y) {
4147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int date = mSelectionDay;
4148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cellWidth = mCellWidth;
4149b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik        ArrayList<Event> events = mEvents;
4150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
4151e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay);
4152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int top = 0;
4153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvent = null;
4154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
41562fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionAllday) {
4157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            float yDistance;
415846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            float minYdistance = 10000.0f; // any large number
4159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event closestEvent = null;
41602fda24534978f55711526dbde747aa50d68dec40RoboErik            float drawHeight = mAlldayHeight;
41613eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
41622fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount;
41632fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
41642fda24534978f55711526dbde747aa50d68dec40RoboErik                // Leave a gap for the 'box +n' text
41652fda24534978f55711526dbde747aa50d68dec40RoboErik                maxUnexpandedColumn--;
41662fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4167b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik            events = mAllDayEvents;
4168b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik            numEvents = events.size();
4169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int i = 0; i < numEvents; i++) {
4170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event event = events.get(i);
41712fda24534978f55711526dbde747aa50d68dec40RoboErik                if (!event.drawAsAllday() ||
41722fda24534978f55711526dbde747aa50d68dec40RoboErik                        (!mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn)) {
41732fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Don't check non-allday events or events that aren't shown
4174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
4175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) {
4178b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik                    float numRectangles = mShowAllAllDayEvents ? mMaxAlldayEvents
4179b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik                            : mMaxUnexpandedAlldayEventCount;
4180146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float height = drawHeight / numRectangles;
4181f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                    if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
4182f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                        height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
4183146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float eventTop = yOffset + height * event.getColumn();
4185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float eventBottom = eventTop + height;
4186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (eventTop < y && eventBottom > y) {
4187146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // If the touch is inside the event rectangle, then
4188146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // add the event.
4189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        mSelectedEvents.add(event);
4190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        closestEvent = event;
4191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        break;
4192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else {
4193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Find the closest event
4194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (eventTop >= y) {
4195146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            yDistance = eventTop - y;
4196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else {
4197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            yDistance = y - eventBottom;
4198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
4199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (yDistance < minYdistance) {
4200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            minYdistance = yDistance;
4201146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            closestEvent = event;
4202146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
4203146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4204146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4206146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = closestEvent;
4207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
4208146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4209146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4210146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Adjust y for the scrollable bitmap
4211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        y += mViewStartY - mFirstCell;
4212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4213146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Use a region around (x,y) for the selection region
4214146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect region = mRect;
4215146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.left = x - 10;
4216146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.right = x + 10;
4217146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.top = y - 10;
4218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.bottom = y + 10;
4219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        EventGeometry geometry = mEventGeometry;
4221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
4223146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
4224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Compute the event rectangle.
4225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
4226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
4227146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4228146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4229146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If the event intersects the selection region, then add it to
4230146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // mSelectedEvents.
4231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (geometry.eventIntersectsSelection(event, region)) {
4232146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectedEvents.add(event);
4233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4235146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4236146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If there are any events in the selected region, then assign the
4237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // closest one to mSelectedEvent.
4238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvents.size() > 0) {
4239146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int len = mSelectedEvents.size();
4240146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event closestEvent = null;
424146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            float minDist = mViewWidth + mViewHeight; // some large distance
4242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int index = 0; index < len; index++) {
4243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event ev = mSelectedEvents.get(index);
4244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                float dist = geometry.pointToEvent(x, y, ev);
4245146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (dist < minDist) {
4246146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    minDist = dist;
4247146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    closestEvent = ev;
4248146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4250146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvent = closestEvent;
4251146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Keep the selected hour and day consistent with the selected
425346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // event. They could be different if we touched on an empty hour
425446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // slot very close to an event in the previous hour slot. In
4255146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // that case we will select the nearby event.
4256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startDay = mSelectedEvent.startDay;
4257146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endDay = mSelectedEvent.endDay;
4258146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectionDay < startDay) {
4259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionDay = startDay;
4260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mSelectionDay > endDay) {
4261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionDay = endDay;
4262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startHour = mSelectedEvent.startTime / 60;
4265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endHour;
4266146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectedEvent.startTime < mSelectedEvent.endTime) {
4267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endHour = (mSelectedEvent.endTime - 1) / 60;
4268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
4269146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endHour = mSelectedEvent.endTime / 60;
4270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4271146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4272349dd91e8475a72eb68a5eae4482ce7e82c01d16RoboErik            if (mSelectionHour < startHour && mSelectionDay == startDay) {
4273146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionHour = startHour;
4274349dd91e8475a72eb68a5eae4482ce7e82c01d16RoboErik            } else if (mSelectionHour > endHour && mSelectionDay == endDay) {
4275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionHour = endHour;
4276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4277146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4278146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Encapsulates the code to continue the scrolling after the
428146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    // finger is lifted. Instead of stopping the scroll immediately,
4282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // the scroll continues to "free spin" and gradually slows down.
4283146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private class ContinueScroll implements Runnable {
4284146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
42853f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            mScrolling = mScrolling && mScroller.computeScrollOffset();
42863f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            if (!mScrolling) {
42873f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                resetSelectedHour();
42883f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                invalidate();
42893f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                return;
4290146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4291146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
42923f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            mViewStartY = mScroller.getCurrY();
4293146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mViewStartY < 0) {
4295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = 0;
4296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mViewStartY > mMaxViewStartY) {
4297146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = mMaxViewStartY;
4298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeFirstHour();
43013f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            post(this);
4302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
4303146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4305146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
4307bd0e834aea317236d574f8738fe11a51fdc87968Erik     * Cleanup the pop-up and timers.
4308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
4309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void cleanup() {
4310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Protect against null-pointer exceptions
4311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mPopup != null) {
4312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPopup.dismiss();
4313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4314666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
4315146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Handler handler = getHandler();
4316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (handler != null) {
4317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            handler.removeCallbacks(mDismissPopup);
4318bd0e834aea317236d574f8738fe11a51fdc87968Erik            handler.removeCallbacks(mUpdateCurrentTime);
4319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4320e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4321d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT,
4322d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight);
4323d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
43247abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project        // Turn off redraw
43257abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project        mRemeasure = false;
4326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4328bd0e834aea317236d574f8738fe11a51fdc87968Erik    /**
4329bd0e834aea317236d574f8738fe11a51fdc87968Erik     * Restart the update timer
4330bd0e834aea317236d574f8738fe11a51fdc87968Erik     */
4331bd0e834aea317236d574f8738fe11a51fdc87968Erik    public void restartCurrentTimeUpdates() {
4332bd0e834aea317236d574f8738fe11a51fdc87968Erik        post(mUpdateCurrentTime);
4333bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
4334bd0e834aea317236d574f8738fe11a51fdc87968Erik
433546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    @Override
433646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    protected void onDetachedFromWindow() {
4337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cleanup();
4338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        super.onDetachedFromWindow();
4339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    class DismissPopup implements Runnable {
4342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
4343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Protect against null-pointer exceptions
4344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mPopup != null) {
4345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPopup.dismiss();
4346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4347146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4349bd0e834aea317236d574f8738fe11a51fdc87968Erik
4350bd0e834aea317236d574f8738fe11a51fdc87968Erik    class UpdateCurrentTime implements Runnable {
4351bd0e834aea317236d574f8738fe11a51fdc87968Erik        public void run() {
4352bd0e834aea317236d574f8738fe11a51fdc87968Erik            long currentTime = System.currentTimeMillis();
4353bd0e834aea317236d574f8738fe11a51fdc87968Erik            mCurrentTime.set(currentTime);
4354bd0e834aea317236d574f8738fe11a51fdc87968Erik            //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.)
4355bd0e834aea317236d574f8738fe11a51fdc87968Erik            postDelayed(mUpdateCurrentTime,
4356bd0e834aea317236d574f8738fe11a51fdc87968Erik                    UPDATE_CURRENT_TIME_DELAY - (currentTime % UPDATE_CURRENT_TIME_DELAY));
4357bd0e834aea317236d574f8738fe11a51fdc87968Erik            mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
4358bd0e834aea317236d574f8738fe11a51fdc87968Erik            invalidate();
4359bd0e834aea317236d574f8738fe11a51fdc87968Erik        }
4360bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
4361ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4362ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
4363ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4364ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onSingleTapUp(MotionEvent ev) {
4365d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doSingleTapUp(ev);
4366ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4367ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4368ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4369ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4370ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public void onLongPress(MotionEvent ev) {
4371d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doLongPress(ev);
4372ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4373ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4374ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4375ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
43762fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mTouchStartedInAlldayArea) {
43770e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                if (Math.abs(distanceX) < Math.abs(distanceY)) {
43780e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                    return false;
43790e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                }
43800e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                // don't scroll vertically if this started in the allday area
43810e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                distanceY = 0;
43822fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4383d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doScroll(e1, e2, distanceX, distanceY);
4384ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4385ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4386ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4387ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4388ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
43892fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mTouchStartedInAlldayArea) {
43900e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                if (Math.abs(velocityX) < Math.abs(velocityY)) {
43910e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                    return false;
43920e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                }
43930e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                // don't fling vertically if this started in the allday area
43940e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                velocityY = 0;
43952fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4396d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doFling(e1, e2, velocityX, velocityY);
4397ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4398ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4399ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4400ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4401ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onDown(MotionEvent ev) {
4402d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doDown(ev);
4403ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4404ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4405ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    }
4406cd405a28e6821b116dcf3c531805d0c90b6cc08cErik
4407cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    @Override
4408cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    public boolean onLongClick(View v) {
4409cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1,
441087f993ff96172f508d3f241f8a425f953c7eb739RoboErik                getSelectedTimeInMillis(), 0, -1, -1, -1);
4411cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        return true;
4412cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    }
44132c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
44142c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    // The rest of this file was borrowed from Launcher2 - PagedView.java
44152c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private static final int MINIMUM_SNAP_VELOCITY = 2200;
44162c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
441795d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan    private class ScrollInterpolator implements Interpolator {
44182c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        public ScrollInterpolator() {
44192c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
44202c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
44212c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        public float getInterpolation(float t) {
44222c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            t -= 1.0f;
442395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            t = t * t * t * t * t + 1;
442495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
442595d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            if ((1 - t) * mAnimationDistance < 1) {
442695d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan                cancelAnimation();
442795d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            }
442895d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
442995d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            return t;
44302c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
44312c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
44322c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
44332c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private long calculateDuration(float delta, float width, float velocity) {
44342c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        /*
44352c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * Here we compute a "distance" that will be used in the computation of
44362c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * the overall snap duration. This is a function of the actual distance
44372c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * that needs to be traveled; we keep this value close to half screen
44382c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * size in order to reduce the variance in snap duration as a function
44392c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * of the distance the page needs to travel.
44402c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         */
44412c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        final float halfScreenSize = width / 2;
44422c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distanceRatio = delta / width;
44432c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio);
44442c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration;
44452c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
44462c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        velocity = Math.abs(velocity);
44472c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
44482c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
44492c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        /*
44502c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * we want the page's snap velocity to approximately match the velocity
44512c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * at which the user flings, so we scale the duration by a value near to
44522c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to
44532c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * make it a little slower.
44542c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         */
44552c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        long duration = 6 * Math.round(1000 * Math.abs(distance / velocity));
44562c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        if (DEBUG) {
44572c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            Log.e(TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:"
44582c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + distanceRatio + " distance:" + distance + " velocity:" + velocity
44592c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + " duration:" + duration + " distanceInfluenceForSnapDuration:"
44602c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + distanceInfluenceForSnapDuration);
44612c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
44622c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        return duration;
44632c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
44642c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
44652c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    /*
44662c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * We want the duration of the page snap animation to be influenced by the
44672c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * distance that the screen has to travel, however, we don't want this
44682c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * duration to be effected in a purely linear fashion. Instead, we use this
44692c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * method to moderate the effect that the distance of travel has on the
44702c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * overall snap duration.
44712c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     */
44722c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private float distanceInfluenceForSnapDuration(float f) {
44732c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        f -= 0.5f; // center the values about 0.
44742c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        f *= 0.3f * Math.PI / 2.0f;
44752c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        return (float) Math.sin(f);
44762c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
4477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project}
4478