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
1946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.Animator;
2046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.AnimatorListenerAdapter;
2146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.ObjectAnimator;
2246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.animation.ValueAnimator;
2320b6fb580bf09dbd610a256ffd3165c03053cb70RoboErikimport android.app.AlertDialog;
249ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chanimport android.app.Service;
25146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentResolver;
26146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.ContentUris;
27146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.Context;
2820b6fb580bf09dbd610a256ffd3165c03053cb70RoboErikimport android.content.DialogInterface;
29146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.res.Resources;
30146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.content.res.TypedArray;
31146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.database.Cursor;
32146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Canvas;
33146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Paint;
34e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.graphics.Paint.Align;
35dd95df57c8c5a58a85c4c0effad5652dec14f621Erikimport android.graphics.Paint.Style;
36146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Rect;
37146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.graphics.Typeface;
38c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawaimport android.graphics.drawable.Drawable;
39146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.net.Uri;
40146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.os.Handler;
41a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Attendees;
42a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Calendars;
43a7c0390d9c5dd4ff730de505682687fae5f5ced0RoboErikimport android.provider.CalendarContract.Events;
443e7015267340b09d142dfcd3ff8acda257711023Shachar Shemeshimport android.text.Layout.Alignment;
45e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.SpannableStringBuilder;
46f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chanimport android.text.StaticLayout;
473e7015267340b09d142dfcd3ff8acda257711023Shachar Shemeshimport android.text.TextPaint;
48f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chanimport android.text.TextUtils;
49146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.DateFormat;
50146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.DateUtils;
51146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.text.format.Time;
52e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErikimport android.text.style.StyleSpan;
53146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.util.Log;
54146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ContextMenu;
55dd95df57c8c5a58a85c4c0effad5652dec14f621Erikimport android.view.ContextMenu.ContextMenuInfo;
56ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.GestureDetector;
57146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.Gravity;
58146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.KeyEvent;
59146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.LayoutInflater;
60146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.MenuItem;
61146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.MotionEvent;
62d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chanimport android.view.ScaleGestureDetector;
63146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.View;
64146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ViewConfiguration;
65146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.ViewGroup;
66146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.view.WindowManager;
67259b8f9a21fffd3858218581e9daaf689b6b888fErikimport android.view.accessibility.AccessibilityEvent;
689ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chanimport android.view.accessibility.AccessibilityManager;
6946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chanimport android.view.animation.AccelerateDecelerateInterpolator;
70ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.animation.Animation;
712c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chanimport android.view.animation.Interpolator;
72ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.view.animation.TranslateAnimation;
73433ba924de1ac26b923e2beb061812012749e4f3Michael Chanimport android.widget.EdgeEffect;
74146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.ImageView;
753f6328f579b05c02606572c777541b97aa04f9f3Michael Chanimport android.widget.OverScroller;
76146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.PopupWindow;
77146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport android.widget.TextView;
78ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chanimport android.widget.ViewSwitcher;
79146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
809e196e46b238e6bca1ef86c7d673807b78ab7016Michael Chanimport com.android.calendar.CalendarController.EventType;
819e196e46b238e6bca1ef86c7d673807b78ab7016Michael Chanimport com.android.calendar.CalendarController.ViewType;
829e196e46b238e6bca1ef86c7d673807b78ab7016Michael Chan
83146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport java.util.ArrayList;
84c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawaimport java.util.Arrays;
85146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Projectimport java.util.Calendar;
86259b8f9a21fffd3858218581e9daaf689b6b888fErikimport java.util.Formatter;
87259b8f9a21fffd3858218581e9daaf689b6b888fErikimport java.util.Locale;
880d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chanimport java.util.regex.Matcher;
890d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chanimport java.util.regex.Pattern;
90146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
91146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project/**
92d190418220467c02327dd9b3ae1eae238889720bMichael Chan * View for multi-day view. So far only 1 and 7 day have been tested.
93d190418220467c02327dd9b3ae1eae238889720bMichael Chan */
94d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chanpublic class DayView extends View implements View.OnCreateContextMenuListener,
95cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener
96cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        {
97d190418220467c02327dd9b3ae1eae238889720bMichael Chan    private static String TAG = "DayView";
98d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static boolean DEBUG = false;
999ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private static boolean DEBUG_SCALING = false;
100259b8f9a21fffd3858218581e9daaf689b6b888fErik    private static final String PERIOD_SPACE = ". ";
101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
102705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan    private static float mScale = 0; // Used for supporting different screen densities
103666fc5e8ddbac15c951aad738bd818ee307eae42Erik    private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event
1042fda24534978f55711526dbde747aa50d68dec40RoboErik    // Duration of the allday expansion
105ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    private static final long ANIMATION_DURATION = 400;
1062fda24534978f55711526dbde747aa50d68dec40RoboErik    // duration of the more allday event text fade
1072fda24534978f55711526dbde747aa50d68dec40RoboErik    private static final long ANIMATION_SECONDARY_DURATION = 200;
1085b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    // duration of the scroll to go to a specified time
1095b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private static final int GOTO_SCROLL_DURATION = 200;
1100a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    // duration for events' cross-fade animation
1110a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    private static final int EVENTS_CROSS_FADE_DURATION = 400;
112ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    // duration to show the event clicked
11331c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson    private static final int CLICK_DISPLAY_DURATION = 50;
114705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
1154484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_AGENDA = 2;
1164484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_DAY = 3;
1174484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_VIEW = 5;
1184484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_CREATE = 6;
1194484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_EDIT = 7;
1204484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private static final int MENU_EVENT_DELETE = 8;
1214484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan
12211ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan    private static int DEFAULT_CELL_HEIGHT = 64;
123d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int MAX_CELL_HEIGHT = 150;
124d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int MIN_Y_SPAN = 100;
125d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
126146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mOnFlingCalled;
1274f58287a1e02325337e39073a50bbb880094099fRoboErik    private boolean mStartingScroll = false;
128ea3f117db073ae796453479d00e9c7664d4471c5RoboErik    protected boolean mPaused = true;
129ea3f117db073ae796453479d00e9c7664d4471c5RoboErik    private Handler mHandler;
130666fc5e8ddbac15c951aad738bd818ee307eae42Erik    /**
131666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * ID of the last event which was displayed with the toast popup.
132666fc5e8ddbac15c951aad738bd818ee307eae42Erik     *
133666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * This is used to prevent popping up multiple quick views for the same event, especially
134666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * during calendar syncs. This becomes valid when an event is selected, either by default
135666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * on starting calendar or by scrolling to an event. It becomes invalid when the user
136666fc5e8ddbac15c951aad738bd818ee307eae42Erik     * explicitly scrolls to an empty time slot, changes views, or deletes the event.
137666fc5e8ddbac15c951aad738bd818ee307eae42Erik     */
138666fc5e8ddbac15c951aad738bd818ee307eae42Erik    private long mLastPopupEventID;
139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
140ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    protected Context mContext;
141146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String[] CALENDARS_PROJECTION = new String[] {
143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Calendars._ID,          // 0
144ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        Calendars.CALENDAR_ACCESS_LEVEL, // 1
1456d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        Calendars.OWNER_ACCOUNT, // 2
146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int CALENDARS_INDEX_ACCESS_LEVEL = 1;
1486d34cec523930368976bc8d358d0a6de1da3462cMichael Chan    private static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final String CALENDARS_WHERE = Calendars._ID + "=%d";
150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_NONE = 0;
152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_ABOVE = 1;
153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_BELOW = 2;
154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_LEFT = 4;
155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int FROM_RIGHT = 8;
156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
157a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_NONE = 0;
158a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_DELETE = 1;
159a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static final int ACCESS_LEVEL_EDIT = 2;
160a505627659f6bfba8869eac8089f7030f2d0d92cErik
161d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan    private static int mHorizontalSnapBackThreshold = 128;
162146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
163056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final ContinueScroll mContinueScroll = new ContinueScroll();
164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Make this visible within the package for more informative debugging
166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Time mBaseDate;
167bd0e834aea317236d574f8738fe11a51fdc87968Erik    private Time mCurrentTime;
168bd0e834aea317236d574f8738fe11a51fdc87968Erik    //Update the current time line every five minutes if the window is left open that long
169bd0e834aea317236d574f8738fe11a51fdc87968Erik    private static final int UPDATE_CURRENT_TIME_DELAY = 300000;
170056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime();
171bd0e834aea317236d574f8738fe11a51fdc87968Erik    private int mTodayJulianDay;
172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
173056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Typeface mBold = Typeface.DEFAULT_BOLD;
174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstJulianDay;
1751662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson    private int mLoadedFirstJulianDay = -1;
176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mLastJulianDay;
177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mMonthLength;
179487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstVisibleDate;
180487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstVisibleDayOfWeek;
181146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int[] mEarliestStartHour;    // indexed by the week day offset
182146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean[] mHasAllDayEvent;   // indexed by the week day offset
183259b8f9a21fffd3858218581e9daaf689b6b888fErik    private String mEventCountTemplate;
184056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final CharSequence[] mLongPressItems;
18520b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik    private String mLongPressTitle;
186056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private Event mClickedEvent;           // The event the user clicked on
187ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    private Event mSavedClickedEvent;
188ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    private static int mOnDownDelay;
189056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private int mClickedYLocation;
19031c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson    private long mDownTouchTime;
191056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson
1920a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    private int mEventsAlpha = 255;
1930a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    private ObjectAnimator mEventsCrossFadeAnimation;
194259b8f9a21fffd3858218581e9daaf689b6b888fErik
195259b8f9a21fffd3858218581e9daaf689b6b888fErik    protected static StringBuilder mStringBuilder = new StringBuilder(50);
196259b8f9a21fffd3858218581e9daaf689b6b888fErik    // TODO recreate formatter when locale changes
197259b8f9a21fffd3858218581e9daaf689b6b888fErik    protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
199056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Runnable mTZUpdater = new Runnable() {
200731f1cb808b7586b93615b408ab2636081fab0dcErik        @Override
201731f1cb808b7586b93615b408ab2636081fab0dcErik        public void run() {
202731f1cb808b7586b93615b408ab2636081fab0dcErik            String tz = Utils.getTimeZone(mContext, this);
203731f1cb808b7586b93615b408ab2636081fab0dcErik            mBaseDate.timezone = tz;
204731f1cb808b7586b93615b408ab2636081fab0dcErik            mBaseDate.normalize(true);
205731f1cb808b7586b93615b408ab2636081fab0dcErik            mCurrentTime.switchTimezone(tz);
206731f1cb808b7586b93615b408ab2636081fab0dcErik            invalidate();
207731f1cb808b7586b93615b408ab2636081fab0dcErik        }
208731f1cb808b7586b93615b408ab2636081fab0dcErik    };
209731f1cb808b7586b93615b408ab2636081fab0dcErik
210ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    // Sets the "clicked" color from the clicked event
211ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    private final Runnable mSetClick = new Runnable() {
212ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        @Override
213ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        public void run() {
214ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson                mClickedEvent = mSavedClickedEvent;
215ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson                mSavedClickedEvent = null;
216ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson                DayView.this.invalidate();
217ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        }
218ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    };
219056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson
22031c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson    // Clears the "clicked" color from the clicked event and launch the event
221056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Runnable mClearClick = new Runnable() {
222056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        @Override
223056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        public void run() {
22431c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson            if (mClickedEvent != null) {
22531c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson                mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mClickedEvent.id,
22631c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson                        mClickedEvent.startMillis, mClickedEvent.endMillis,
227b85452b2589be5f057a2d4bd8991c7c7c740752aIsaac Katzenelson                        DayView.this.getWidth() / 2, mClickedYLocation,
22831c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson                        getSelectedTimeInMillis());
229056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            }
23031c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson            mClickedEvent = null;
23131c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson            DayView.this.invalidate();
232056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        }
233056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    };
234056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson
235056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final TodayAnimatorListener mTodayAnimatorListener = new TodayAnimatorListener();
2365b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2375b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    class TodayAnimatorListener extends AnimatorListenerAdapter {
2385b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        private volatile Animator mAnimator = null;
2395b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        private volatile boolean mFadingIn = false;
2405b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2415b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        @Override
2425b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void onAnimationEnd(Animator animation) {
2435b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            synchronized (this) {
2445b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mAnimator != animation) {
2455b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    animation.removeAllListeners();
2465b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    animation.cancel();
2475b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    return;
2485b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
2495b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mFadingIn) {
2505b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    if (mTodayAnimator != null) {
2515b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mTodayAnimator.removeAllListeners();
2525b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mTodayAnimator.cancel();
2535b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    }
2545b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator = ObjectAnimator
2555b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                            .ofInt(DayView.this, "animateTodayAlpha", 255, 0);
2565b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator = mTodayAnimator;
2575b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mFadingIn = false;
2585b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.addListener(this);
2595b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.setDuration(600);
2605b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.start();
2615b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                } else {
2625b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimateToday = false;
2635b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimateTodayAlpha = 0;
2645b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator.removeAllListeners();
2655b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mAnimator = null;
2665b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator = null;
2675b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    invalidate();
2685b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
2695b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            }
2705b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2715b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2725b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void setAnimator(Animator animation) {
2735b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mAnimator = animation;
2745b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2755b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2765b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        public void setFadingIn(boolean fadingIn) {
2775b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mFadingIn = fadingIn;
2785b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2795b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
2805b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
2815b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
28246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() {
28346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
28446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationStart(Animator animation) {
28546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = true;
28646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
28746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
28846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
28946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationCancel(Animator animation) {
29046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = false;
29146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
29246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
29346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        @Override
29446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        public void onAnimationEnd(Animator animation) {
29546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mScrolling = false;
29646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            resetSelectedHour();
29746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            invalidate();
29846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
29946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    };
30046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
301146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * This variable helps to avoid unnecessarily reloading events by keeping
303146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * track of the start millis parameter used for the most recent loading
304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * of events.  If the next reload matches this, then the events are not
305146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * reloaded.  To force a reload, set this to zero (this is set to zero
306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * in the method clearCachedEvents()).
307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private long mLastReloadMillis;
309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private ArrayList<Event> mEvents = new ArrayList<Event>();
31108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private ArrayList<Event> mAllDayEvents = new ArrayList<Event>();
31208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout[] mLayouts = null;
31308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout[] mAllDayLayouts = null;
314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionDay;        // Julian day
315146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionHour;
316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3172fda24534978f55711526dbde747aa50d68dec40RoboErik    boolean mSelectionAllday;
318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
319989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    // Current selection info for accessibility
320989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private int mSelectionDayForAccessibility;        // Julian day
321989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private int mSelectionHourForAccessibility;
322989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private Event mSelectedEventForAccessibility;
323989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    // Last selection info for accessibility
324989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private int mLastSelectionDayForAccessibility;
325989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private int mLastSelectionHourForAccessibility;
326989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private Event mLastSelectedEventForAccessibility;
327989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson
3289ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
3293eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    /** Width of a day or non-conflicting event */
330146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mCellWidth;
331146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Pre-allocate these objects and re-use them
333056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Rect mRect = new Rect();
334056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Rect mDestRect = new Rect();
335056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Rect mSelectionRect = new Rect();
3362fda24534978f55711526dbde747aa50d68dec40RoboErik    // This encloses the more allDay events icon
337056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Rect mExpandAllDayRect = new Rect();
338e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    // TODO Clean up paint usage
339056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Paint mPaint = new Paint();
340056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Paint mEventTextPaint = new Paint();
341056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Paint mSelectionPaint = new Paint();
34202041687c35441e8a21cb763f1fc3a26525292efDavid Brown    private float[] mLines;
343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
344487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    private int mFirstDayOfWeek; // First day of the week
345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private PopupWindow mPopup;
347146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private View mPopupView;
348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // The number of milliseconds to show the popup window
350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int POPUP_DISMISS_DELAY = 3000;
351056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final DismissPopup mDismissPopup = new DismissPopup();
352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
353146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mRemeasure = true;
354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private final EventLoader mEventLoader;
356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected final EventGeometry mEventGeometry;
357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
358e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float GRID_LINE_LEFT_MARGIN = 0;
359000ba1109e2f4e527e9227fe341598ceeb3fbca6Michael Chan    private static final float GRID_LINE_INNER_WIDTH = 1;
3603eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
361146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int DAY_GAP = 1;
362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int HOUR_GAP = 1;
3632fda24534978f55711526dbde747aa50d68dec40RoboErik    // This is the standard height of an allday event with no restrictions
3649c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    private static int SINGLE_ALLDAY_HEIGHT = 34;
3652fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
3662fda24534978f55711526dbde747aa50d68dec40RoboErik    * This is the minimum desired height of a allday event.
3672fda24534978f55711526dbde747aa50d68dec40RoboErik    * When unexpanded, allday events will use this height.
3682fda24534978f55711526dbde747aa50d68dec40RoboErik    * When expanded allDay events will attempt to grow to fit all
3692fda24534978f55711526dbde747aa50d68dec40RoboErik    * events at this height.
3702fda24534978f55711526dbde747aa50d68dec40RoboErik    */
3712fda24534978f55711526dbde747aa50d68dec40RoboErik    private static float MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0F; // in pixels
3722fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
3732fda24534978f55711526dbde747aa50d68dec40RoboErik     * This is how big the unexpanded allday height is allowed to be.
3742fda24534978f55711526dbde747aa50d68dec40RoboErik     * It will get adjusted based on screen size
3752fda24534978f55711526dbde747aa50d68dec40RoboErik     */
3762fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int MAX_UNEXPANDED_ALLDAY_HEIGHT =
3772fda24534978f55711526dbde747aa50d68dec40RoboErik            (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
3782fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
3792fda24534978f55711526dbde747aa50d68dec40RoboErik     * This is the minimum size reserved for displaying regular events.
3802fda24534978f55711526dbde747aa50d68dec40RoboErik     * The expanded allDay region can't expand into this.
3812fda24534978f55711526dbde747aa50d68dec40RoboErik     */
3822fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int MIN_HOURS_HEIGHT = 180;
38325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int ALLDAY_TOP_MARGIN = 1;
3842fda24534978f55711526dbde747aa50d68dec40RoboErik    // The largest a single allDay event will become.
385f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34;
386e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3878f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_TOP_MARGIN = 2;
388e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int HOURS_LEFT_MARGIN = 2;
3898f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_RIGHT_MARGIN = 4;
3908f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
39195f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private static int NEW_EVENT_MARGIN = 4;
392462196d197de326006aaf15f8bb7a8d83b8e2a60Peter Ng    private static int NEW_EVENT_WIDTH = 2;
39395f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private static int NEW_EVENT_MAX_LENGTH = 16;
394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
395763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int CURRENT_TIME_LINE_SIDE_BUFFER = 4;
396763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int CURRENT_TIME_LINE_TOP_OFFSET = 2;
397bd0e834aea317236d574f8738fe11a51fdc87968Erik
398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MINUTES_PER_HOUR = 60;
399146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24;
400146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000;
401146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000);
402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24;
403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4042fda24534978f55711526dbde747aa50d68dec40RoboErik    // More events text will transition between invisible and this alpha
4052fda24534978f55711526dbde747aa50d68dec40RoboErik    private static final int MORE_EVENTS_MAX_ALPHA = 0x4C;
406e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0;
407e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5;
408e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6;
409e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_RIGHT_MARGIN = 4;
410e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int DAY_HEADER_BOTTOM_MARGIN = 3;
4113eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float DAY_HEADER_FONT_SIZE = 14;
4128f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static float DATE_HEADER_FONT_SIZE = 32;
4133eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float NORMAL_FONT_SIZE = 12;
4143eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static float EVENT_TEXT_FONT_SIZE = 12;
415e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float HOURS_TEXT_SIZE = 12;
416e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static float AMPM_TEXT_SIZE = 9;
4178f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int MIN_HOURS_WIDTH = 96;
418763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int MIN_CELL_WIDTH_FOR_TEXT = 20;
419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int MAX_EVENT_TEXT_LEN = 500;
4202fda24534978f55711526dbde747aa50d68dec40RoboErik    // smallest height to draw an event with
42125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static float MIN_EVENT_HEIGHT = 24.0F; // in pixels
4221ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int CALENDAR_COLOR_SQUARE_SIZE = 10;
42325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_TOP_MARGIN = 1;
424763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int EVENT_RECT_BOTTOM_MARGIN = 0;
425763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int EVENT_RECT_LEFT_MARGIN = 1;
426763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik    private static int EVENT_RECT_RIGHT_MARGIN = 0;
42725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_RECT_STROKE_WIDTH = 2;
4281ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_TEXT_TOP_MARGIN = 2;
42925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private static int EVENT_TEXT_BOTTOM_MARGIN = 2;
43095d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng    private static int EVENT_TEXT_LEFT_MARGIN = 6;
43195d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng    private static int EVENT_TEXT_RIGHT_MARGIN = 6;
432e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1;
4331ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN;
4341ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN;
4351ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
4361ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN;
4372fda24534978f55711526dbde747aa50d68dec40RoboErik    // margins and sizing for the expand allday icon
438cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik    private static int EXPAND_ALL_DAY_BOTTOM_MARGIN = 10;
4392fda24534978f55711526dbde747aa50d68dec40RoboErik    // sizing for "box +n" in allDay events
4402fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int EVENT_SQUARE_WIDTH = 10;
4412fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int EVENT_LINE_PADDING = 4;
442ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson    private static int NEW_EVENT_HINT_FONT_SIZE = 12;
443146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
444146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static int mPressedColor;
445056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private static int mClickedColor;
446146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static int mEventTextColor;
4472fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int mMoreEventsTextColor;
448f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan
44964b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok    private static int mWeek_saturdayColor;
45064b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok    private static int mWeek_sundayColor;
451d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan    private static int mCalendarDateBannerTextColor;
452d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan    private static int mCalendarAmPmLabel;
453ba902466b19b11d587468e354edc58e542707747Michael Chan    private static int mCalendarGridAreaSelected;
4543eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int mCalendarGridLineInnerHorizontalColor;
4553eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int mCalendarGridLineInnerVerticalColor;
45621bd42039c92232647f54461cd20708224cc7eb6Michael Chan    private static int mFutureBgColor;
457cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan    private static int mFutureBgColorRes;
458e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int mBgColor;
459ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson    private static int mNewEventHintColor;
4608f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private static int mCalendarHourLabelColor;
4612fda24534978f55711526dbde747aa50d68dec40RoboErik    private static int mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA;
462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
46395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan    private float mAnimationDistance = 0;
464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewStartX;
465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewStartY;
466146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mMaxViewStartY;
467146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewHeight;
468146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mViewWidth;
46946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    private int mGridAreaHeight = -1;
470d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int mCellHeight = 0; // shared among all DayViews
471d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private static int mMinCellHeight = 32;
472146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mScrollStartY;
473146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mPreviousDirection;
4744dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan    private static int mScaledPagingTouchSlop = 0;
475146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
476d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /**
477d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * Vertical distance or span between the two touch points at the start of a
478d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * scaling gesture
479d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     */
480d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private float mStartingSpanY = 0;
481d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /** Height of 1 hour in pixels at the start of a scaling gesture */
482d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private int mCellHeightBeforeScaleGesture;
483d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /** The hour at the center two touch points */
484d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private float mGestureCenterHour = 0;
485a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan
486a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan    private boolean mRecalCenterHour = false;
487a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan
488d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    /**
489d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * Flag to decide whether to handle the up event. Cases where up events
490d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * should be ignored are 1) right after a scale gesture and 2) finger was
491d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     * down before app launch
492d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan     */
493d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    private boolean mHandleActionUp = true;
494d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mHoursTextHeight;
4962fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
4972fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the area used for allday events
4982fda24534978f55711526dbde747aa50d68dec40RoboErik     */
4992fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAlldayHeight;
5002fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
5012fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the allday event area used during animation
5022fda24534978f55711526dbde747aa50d68dec40RoboErik     */
5032fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAnimateDayHeight = 0;
5042fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
5052fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of an individual allday event during animation
5062fda24534978f55711526dbde747aa50d68dec40RoboErik     */
5072fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
5082fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
509cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik     * Whether to use the expand or collapse icon.
5102fda24534978f55711526dbde747aa50d68dec40RoboErik     */
51197326ee1ea9b1bb17e0e2bcfe344352d4bb03001RoboErik    private static boolean mUseExpandIcon = true;
5122fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
5132fda24534978f55711526dbde747aa50d68dec40RoboErik     * The height of the day names/numbers
5142fda24534978f55711526dbde747aa50d68dec40RoboErik     */
5153eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    private static int DAY_HEADER_HEIGHT = 45;
516f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    /**
517e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * The height of the day names/numbers for multi-day views
518e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
519e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
520e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
521e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * The height of the day names/numbers when viewing a single day
522e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
523e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private static int ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
524e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
525f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan     * Max of all day events in a given day in this view.
526f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan     */
5272fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mMaxAlldayEvents;
5282fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
5292fda24534978f55711526dbde747aa50d68dec40RoboErik     * A count of the number of allday events that were not drawn for each day
5302fda24534978f55711526dbde747aa50d68dec40RoboErik     */
5312fda24534978f55711526dbde747aa50d68dec40RoboErik    private int[] mSkippedAlldayEvents;
5322fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
5332fda24534978f55711526dbde747aa50d68dec40RoboErik     * The number of allDay events at which point we start hiding allDay events.
5342fda24534978f55711526dbde747aa50d68dec40RoboErik     */
5352fda24534978f55711526dbde747aa50d68dec40RoboErik    private int mMaxUnexpandedAlldayEventCount = 4;
5362fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
5372fda24534978f55711526dbde747aa50d68dec40RoboErik     * Whether or not to expand the allDay area to fill the screen
5382fda24534978f55711526dbde747aa50d68dec40RoboErik     */
5392fda24534978f55711526dbde747aa50d68dec40RoboErik    private static boolean mShowAllAllDayEvents = false;
540146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
541146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected int mNumDays = 7;
542146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mNumHours = 10;
5433eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
5443eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    /** Width of the time line (list of hours) to the left. */
545146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mHoursWidth;
546146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mDateStrWidth;
54746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** Top of the scrollable region i.e. below date labels and all day events */
548146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstCell;
54946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** First fully visibile hour */
550146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstHour = -1;
55146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    /** Distance between the mFirstCell and the top of first fully visible hour. */
552146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mFirstHourOffset;
553146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mHourStrs;
554146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mDayStrs;
555146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String[] mDayStrs2Letter;
556146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mIs24HourFormat;
557146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
558056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final ArrayList<Event> mSelectedEvents = new ArrayList<Event>();
559146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mComputeSelectedEvents;
5608307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik    private boolean mUpdateToast;
561146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Event mSelectedEvent;
562146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Event mPrevSelectedEvent;
563056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Rect mPrevBox = new Rect();
564146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected final Resources mResources;
5653eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    protected final Drawable mCurrentTimeLine;
5665b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    protected final Drawable mCurrentTimeAnimateLine;
5673eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan    protected final Drawable mTodayHeaderDrawable;
5682fda24534978f55711526dbde747aa50d68dec40RoboErik    protected final Drawable mExpandAlldayDrawable;
569cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik    protected final Drawable mCollapseAlldayDrawable;
570f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan    protected Drawable mAcceptedOrTentativeEventBoxDrawable;
571146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String mAmString;
572146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private String mPmString;
573056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final DeleteEventHelper mDeleteEventHelper;
5746d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private static int sCounter = 0;
575146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
576056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final ContextMenuHandler mContextMenuHandler = new ContextMenuHandler();
577146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
578d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    ScaleGestureDetector mScaleGestureDetector;
579d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
580146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
581146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The initial state of the touch mode when we enter this view.
582146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
583146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_INITIAL_STATE = 0;
584146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
585146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
586146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates we just received the touch event and we are waiting to see if
587146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * it is a tap or a scroll gesture.
588146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
589146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_DOWN = 1;
590146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
591146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
592146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates the touch gesture is a vertical scroll
593146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
594146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_VSCROLL = 0x20;
595146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
596146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
597146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Indicates the touch gesture is a horizontal scroll
598146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
599146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int TOUCH_MODE_HSCROLL = 0x40;
600146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
601146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mTouchMode = TOUCH_MODE_INITIAL_STATE;
602146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
603146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
604146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS.
605146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
606146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_HIDDEN = 0;
607ef2185bf0c7a80154a79788915db04d4f9e7ad4dMichael Chan    private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet
608146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_SELECTED = 2;
609146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private static final int SELECTION_LONGPRESS = 3;
610146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
611146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int mSelectionMode = SELECTION_HIDDEN;
612146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
613146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean mScrolling = false;
614146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
615a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan    // Pixels scrolled
6164f58287a1e02325337e39073a50bbb880094099fRoboErik    private float mInitialScrollX;
6174f58287a1e02325337e39073a50bbb880094099fRoboErik    private float mInitialScrollY;
6182fda24534978f55711526dbde747aa50d68dec40RoboErik
6195b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private boolean mAnimateToday = false;
6205b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    private int mAnimateTodayAlpha = 0;
6215b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
6222fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the height of the allday region
6232fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mAlldayAnimator;
6242fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the height of events in the allday region
6252fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mAlldayEventAnimator;
6262fda24534978f55711526dbde747aa50d68dec40RoboErik    // Animates the transparency of the more events text
6272fda24534978f55711526dbde747aa50d68dec40RoboErik    ObjectAnimator mMoreAlldayEventsAnimator;
6285b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    // Animates the current time marker when Today is pressed
6295b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    ObjectAnimator mTodayAnimator;
6302fda24534978f55711526dbde747aa50d68dec40RoboErik    // whether or not an event is stopping because it was cancelled
6312fda24534978f55711526dbde747aa50d68dec40RoboErik    private boolean mCancellingAnimations = false;
6322fda24534978f55711526dbde747aa50d68dec40RoboErik    // tracks whether a touch originated in the allday area
6332fda24534978f55711526dbde747aa50d68dec40RoboErik    private boolean mTouchStartedInAlldayArea = false;
6342fda24534978f55711526dbde747aa50d68dec40RoboErik
635056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final CalendarController mController;
636056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final ViewSwitcher mViewSwitcher;
637056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final GestureDetector mGestureDetector;
638056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final OverScroller mScroller;
639056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final EdgeEffect mEdgeEffectTop;
640056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final EdgeEffect mEdgeEffectBottom;
641433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private boolean mCallEdgeEffectOnAbsorb;
642433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private final int OVERFLING_DISTANCE;
643433ba924de1ac26b923e2beb061812012749e4f3Michael Chan    private float mLastVelocity;
644433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
645056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final ScrollInterpolator mHScrollInterpolator;
646aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private AccessibilityManager mAccessibilityMgr = null;
647aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private boolean mIsAccessibilityEnabled = false;
6489ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private boolean mTouchExplorationEnabled = false;
649056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final String mCreateNewEventString;
650056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final String mNewEventHintString;
651705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
652d190418220467c02327dd9b3ae1eae238889720bMichael Chan    public DayView(Context context, CalendarController controller,
65375d9b567e1fd3b9c6945cd0afe28781f5a19c6c8Michael Chan            ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) {
654ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        super(context);
655aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mContext = context;
656aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        initAccessibilityVariables();
657aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan
6582fda24534978f55711526dbde747aa50d68dec40RoboErik        mResources = context.getResources();
6599ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        mCreateNewEventString = mResources.getString(R.string.event_create);
660ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson        mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint);
66100671d5b9254772d223587b3b831d5816476f681Michael Chan        mNumDays = numDays;
66209b1b2e1e7eb9bc291d4e70c993471d3c9ffa799Michael Chan
663e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DATE_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.date_header_text_size);
664e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.day_label_text_size);
665e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        ONE_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.one_day_header_height);
666e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        DAY_HEADER_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.day_header_bottom_margin);
667e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        EXPAND_ALL_DAY_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.all_day_bottom_margin);
668e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_TEXT_SIZE = (int) mResources.getDimension(R.dimen.hours_text_size);
669e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        AMPM_TEXT_SIZE = (int) mResources.getDimension(R.dimen.ampm_text_size);
670e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        MIN_HOURS_WIDTH = (int) mResources.getDimension(R.dimen.min_hours_width);
671e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_LEFT_MARGIN = (int) mResources.getDimension(R.dimen.hours_left_margin);
672e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_RIGHT_MARGIN = (int) mResources.getDimension(R.dimen.hours_right_margin);
673e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        MULTI_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.day_header_height);
67400671d5b9254772d223587b3b831d5816476f681Michael Chan        int eventTextSizeId;
67500671d5b9254772d223587b3b831d5816476f681Michael Chan        if (mNumDays == 1) {
67600671d5b9254772d223587b3b831d5816476f681Michael Chan            eventTextSizeId = R.dimen.day_view_event_text_size;
67700671d5b9254772d223587b3b831d5816476f681Michael Chan        } else {
67800671d5b9254772d223587b3b831d5816476f681Michael Chan            eventTextSizeId = R.dimen.week_view_event_text_size;
67900671d5b9254772d223587b3b831d5816476f681Michael Chan        }
68000671d5b9254772d223587b3b831d5816476f681Michael Chan        EVENT_TEXT_FONT_SIZE = (int) mResources.getDimension(eventTextSizeId);
681ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson        NEW_EVENT_HINT_FONT_SIZE = (int) mResources.getDimension(R.dimen.new_event_hint_text_size);
6825ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height);
683714c299ea5c05858e273d98efbacf20fedd328f1RoboErik        MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT;
6845ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_TEXT_TOP_MARGIN = (int) mResources.getDimension(R.dimen.event_text_vertical_margin);
6855ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN;
6865ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN;
6875ca51369b44901fbab6da0ac67b3001c19f2ae29RoboErik        EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN;
68809b1b2e1e7eb9bc291d4e70c993471d3c9ffa799Michael Chan
68995d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_TEXT_LEFT_MARGIN = (int) mResources
69095d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng                .getDimension(R.dimen.event_text_horizontal_margin);
69195d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
69295d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
69395d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng        EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
69495d6fe0b672284a19699edc4727c366e3b0ccec7Peter Ng
695705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan        if (mScale == 0) {
696e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
6972fda24534978f55711526dbde747aa50d68dec40RoboErik            mScale = mResources.getDisplayMetrics().density;
698705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan            if (mScale != 1) {
699705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                SINGLE_ALLDAY_HEIGHT *= mScale;
700705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                ALLDAY_TOP_MARGIN *= mScale;
701f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale;
702705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
703705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                NORMAL_FONT_SIZE *= mScale;
7048f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan                GRID_LINE_LEFT_MARGIN *= mScale;
7058f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan                HOURS_TOP_MARGIN *= mScale;
706705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan                MIN_CELL_WIDTH_FOR_TEXT *= mScale;
7072fda24534978f55711526dbde747aa50d68dec40RoboErik                MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale;
7082fda24534978f55711526dbde747aa50d68dec40RoboErik                mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
709705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
710763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik                CURRENT_TIME_LINE_SIDE_BUFFER *= mScale;
711763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik                CURRENT_TIME_LINE_TOP_OFFSET *= mScale;
712bd0e834aea317236d574f8738fe11a51fdc87968Erik
713d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                MIN_Y_SPAN *= mScale;
714d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                MAX_CELL_HEIGHT *= mScale;
715697157470a6f0d8e7b806af9b4fb7fbe0bccae87Erik                DEFAULT_CELL_HEIGHT *= mScale;
7163eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_HEIGHT *= mScale;
7173eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_RIGHT_MARGIN *= mScale;
7183eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale;
7193eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale;
7203eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale;
7213eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan                CALENDAR_COLOR_SQUARE_SIZE *= mScale;
72211ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_TOP_MARGIN *= mScale;
72311ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_BOTTOM_MARGIN *= mScale;
72425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale;
72511ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_LEFT_MARGIN *= mScale;
72611ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan                EVENT_RECT_RIGHT_MARGIN *= mScale;
72725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                EVENT_RECT_STROKE_WIDTH *= mScale;
7282fda24534978f55711526dbde747aa50d68dec40RoboErik                EVENT_SQUARE_WIDTH *= mScale;
7292fda24534978f55711526dbde747aa50d68dec40RoboErik                EVENT_LINE_PADDING *= mScale;
73095f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                NEW_EVENT_MARGIN *= mScale;
73195f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                NEW_EVENT_WIDTH *= mScale;
73295f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                NEW_EVENT_MAX_LENGTH *= mScale;
733705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan            }
734705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan        }
735e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
73600671d5b9254772d223587b3b831d5816476f681Michael Chan        DAY_HEADER_HEIGHT = mNumDays == 1 ? ONE_DAY_HEADER_HEIGHT : MULTI_DAY_HEADER_HEIGHT;
737705043ffec0bcd8fd0348d4038ac34fd45b09818Michael Chan
7389175d29276c78a2a294ce2fce112432171ef7c7cRoboErik        mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_indicator_holo_light);
7395b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        mCurrentTimeAnimateLine = mResources
7405b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                .getDrawable(R.drawable.timeline_indicator_activated_holo_light);
7413eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light);
74299704a2787158bf670fa4a7b4e4f89ace10afa00Michael Chan        mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_expand_holo_light);
74399704a2787158bf670fa4a7b4e4f89ace10afa00Michael Chan        mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_collapse_holo_light);
744ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson        mNewEventHintColor =  mResources.getColor(R.color.new_event_hint_text_color);
745f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        mAcceptedOrTentativeEventBoxDrawable = mResources
746f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan                .getDrawable(R.drawable.panel_month_event_holo_light);
74725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik
748ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mEventLoader = eventLoader;
749146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry = new EventGeometry();
750146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT);
751146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setHourGap(HOUR_GAP);
752e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mEventGeometry.setCellMargin(DAY_GAP);
75320b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        mLongPressItems = new CharSequence[] {
75420b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik            mResources.getString(R.string.new_event_dialog_option)
75520b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        };
75620b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        mLongPressTitle = mResources.getString(R.string.new_event_dialog_label);
757ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mDeleteEventHelper = new DeleteEventHelper(context, null, false /* don't exit when done */);
758666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
759ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mController = controller;
760ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher = viewSwitcher;
761ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mGestureDetector = new GestureDetector(context, new CalendarGestureListener());
762d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mScaleGestureDetector = new ScaleGestureDetector(getContext(), this);
763d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mCellHeight == 0) {
764d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = Utils.getSharedPreference(mContext,
765d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT);
766d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
7673f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScroller = new OverScroller(context);
7682c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        mHScrollInterpolator = new ScrollInterpolator();
769433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectTop = new EdgeEffect(context);
770433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectBottom = new EdgeEffect(context);
771ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        ViewConfiguration vc = ViewConfiguration.get(context);
7724dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan        mScaledPagingTouchSlop = vc.getScaledPagingTouchSlop();
7739e196e46b238e6bca1ef86c7d673807b78ab7016Michael Chan        mOnDownDelay = ViewConfiguration.getTapTimeout();
774ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        OVERFLING_DISTANCE = vc.getScaledOverflingDistance();
775433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
776ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        init(context);
777146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
778146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
779ea3f117db073ae796453479d00e9c7664d4471c5RoboErik    @Override
780ea3f117db073ae796453479d00e9c7664d4471c5RoboErik    protected void onAttachedToWindow() {
781ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        if (mHandler == null) {
782ea3f117db073ae796453479d00e9c7664d4471c5RoboErik            mHandler = getHandler();
783ea3f117db073ae796453479d00e9c7664d4471c5RoboErik            mHandler.post(mUpdateCurrentTime);
784ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        }
785ea3f117db073ae796453479d00e9c7664d4471c5RoboErik    }
786ea3f117db073ae796453479d00e9c7664d4471c5RoboErik
787146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void init(Context context) {
788146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setFocusable(true);
789146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
790146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Allow focus in touch mode so that we can do keyboard shortcuts
791146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // even after we've entered touch mode.
792146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setFocusableInTouchMode(true);
793146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setClickable(true);
794146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        setOnCreateContextMenuListener(this);
795146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
796487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstDayOfWeek = Utils.getFirstDayOfWeek(context);
797146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
798731f1cb808b7586b93615b408ab2636081fab0dcErik        mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater));
799bd0e834aea317236d574f8738fe11a51fdc87968Erik        long currentTime = System.currentTimeMillis();
800bd0e834aea317236d574f8738fe11a51fdc87968Erik        mCurrentTime.set(currentTime);
801bd0e834aea317236d574f8738fe11a51fdc87968Erik        mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
802bd0e834aea317236d574f8738fe11a51fdc87968Erik
80364b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok        mWeek_saturdayColor = mResources.getColor(R.color.week_saturday);
80464b3e63d59967eeb11e1d8328c74ee335abc0ffbsatok        mWeek_sundayColor = mResources.getColor(R.color.week_sunday);
805d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color);
806cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan        mFutureBgColorRes = mResources.getColor(R.color.calendar_future_bg_color);
807e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mBgColor = mResources.getColor(R.color.calendar_hour_background);
808d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label);
809ba902466b19b11d587468e354edc58e542707747Michael Chan        mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected);
810e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mCalendarGridLineInnerHorizontalColor = mResources
811e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                .getColor(R.color.calendar_grid_line_inner_horizontal_color);
812e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mCalendarGridLineInnerVerticalColor = mResources
813e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                .getColor(R.color.calendar_grid_line_inner_vertical_color);
8148f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label);
815146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPressedColor = mResources.getColor(R.color.pressed);
816056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        mClickedColor = mResources.getColor(R.color.day_event_clicked_background_color);
817146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextColor = mResources.getColor(R.color.calendar_event_text_color);
818e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color);
81911638ad4fb56b978770aebe75deac7a0e6ac60d2Erik
820146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE);
821146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setTextAlign(Paint.Align.LEFT);
822146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventTextPaint.setAntiAlias(true);
823146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
824146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color);
825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mSelectionPaint;
826146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setColor(gridLineColor);
8273937a47bc5a1afb9701e1d0565fae31f5800590dErik        p.setStyle(Style.FILL);
828146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(false);
829146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
830146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p = mPaint;
831146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
832146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
833146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Allocate space for 2 weeks worth of weekday names so that we can
834146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // easily start the week display at any week day.
835146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDayStrs = new String[14];
836146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
837146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Also create an array of 2-letter abbreviations.
838146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDayStrs2Letter = new String[14];
839146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
840146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
841146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int index = i - Calendar.SUNDAY;
842146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // e.g. Tue for Tuesday
843e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM)
844e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    .toUpperCase();
845146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mDayStrs[index + 7] = mDayStrs[index];
846146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // e.g. Tu for Tuesday
847e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT)
848e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    .toUpperCase();
84952b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer
85052b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            // If we don't have 2-letter day strings, fall back to 1-letter.
85152b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            if (mDayStrs2Letter[index].equals(mDayStrs[index])) {
85252b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer                mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST);
85352b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer            }
85452b2d70115e88b408c994db0d2b1889c9fb48527Eric Fischer
855146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mDayStrs2Letter[index + 7] = mDayStrs2Letter[index];
856146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
857146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
858146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Figure out how much space we need for the 3-letter abbrev names
859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // in the worst case.
8603eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextSize(DATE_HEADER_FONT_SIZE);
861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(mBold);
862146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] dateStrs = {" 28", " 30"};
863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDateStrWidth = computeMaxStringWidth(0, dateStrs, p);
8643eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextSize(DAY_HEADER_FONT_SIZE);
865146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p);
866146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
867e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
868146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(null);
86961a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan        handleOnResume();
870146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
871e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase();
872e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase();
873146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] ampm = {mAmString, mPmString};
874e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(AMPM_TEXT_SIZE);
875e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mHoursWidth = Math.max(HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p)
876e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                + HOURS_RIGHT_MARGIN);
8778f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth);
878146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
879146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        LayoutInflater inflater;
880146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
881146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView = inflater.inflate(R.layout.bubble_event, null);
882146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView.setLayoutParams(new ViewGroup.LayoutParams(
883df279e3f9d5b0f189f812c52aca78d18178e4445Romain Guy                ViewGroup.LayoutParams.MATCH_PARENT,
884146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                ViewGroup.LayoutParams.WRAP_CONTENT));
885146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup = new PopupWindow(context);
886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setContentView(mPopupView);
887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Resources.Theme dialogTheme = getResources().newTheme();
888146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        dialogTheme.applyStyle(android.R.style.Theme_Dialog, true);
889146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] {
890146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            android.R.attr.windowBackground });
891146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setBackgroundDrawable(ta.getDrawable(0));
892146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ta.recycle();
893146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
894146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Enable touching the popup window
895146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopupView.setOnClickListener(this);
896cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        // Catch long clicks for creating a new event
897cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        setOnLongClickListener(this);
898146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
899731f1cb808b7586b93615b408ab2636081fab0dcErik        mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater));
900146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = System.currentTimeMillis();
901146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mBaseDate.set(millis);
902146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
903146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEarliestStartHour = new int[mNumDays];
904146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mHasAllDayEvent = new boolean[mNumDays];
90502041687c35441e8a21cb763f1fc3a26525292efDavid Brown
90602041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // mLines is the array of points used with Canvas.drawLines() in
90702041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // drawGridBackground() and drawAllDayEvents().  Its size depends
90802041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // on the max number of lines that can ever be drawn by any single
90902041687c35441e8a21cb763f1fc3a26525292efDavid Brown        // drawLines() call in either of those methods.
91002041687c35441e8a21cb763f1fc3a26525292efDavid Brown        final int maxGridLines = (24 + 1)  // max horizontal lines we might draw
911e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                + (mNumDays + 1); // max vertical lines we might draw
91202041687c35441e8a21cb763f1fc3a26525292efDavid Brown        mLines = new float[maxGridLines * 4];
913146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
914146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
915146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
916146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * This is called when the popup window is pressed.
917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
918146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onClick(View v) {
919146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (v == mPopupView) {
920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Pretend it was a trackball click because that will always
921146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // jump to the "View event" screen.
922146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchViews(true /* trackball */);
923146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
924146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
92661a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan    public void handleOnResume() {
927aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        initAccessibilityVariables();
928cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan        if(Utils.getSharedPreference(mContext, OtherPreferences.KEY_OTHER_1, false)) {
929cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            mFutureBgColor = 0;
930cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan        } else {
931cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            mFutureBgColor = mFutureBgColorRes;
932cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan        }
933ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mIs24HourFormat = DateFormat.is24HourFormat(mContext);
93469ab334d6988eda70ba53618506d23ebf7d7b405Erik        mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm;
93561a2e87e7412281d17e86ccff4352cbf32fe77e7Michael Chan        mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext);
936989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mLastSelectionDayForAccessibility = 0;
937989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mLastSelectionHourForAccessibility = 0;
938989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mLastSelectedEventForAccessibility = null;
939f7cf45455ce8cc5588479dcbb5d876f7638f79dcRoboErik        mSelectionMode = SELECTION_HIDDEN;
94069ab334d6988eda70ba53618506d23ebf7d7b405Erik    }
94169ab334d6988eda70ba53618506d23ebf7d7b405Erik
942aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private void initAccessibilityVariables() {
943aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mAccessibilityMgr = (AccessibilityManager) mContext
944aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                .getSystemService(Service.ACCESSIBILITY_SERVICE);
945aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled();
946aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        mTouchExplorationEnabled = isTouchExplorationEnabled();
947aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    }
948aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan
949146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
950146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Returns the start of the selected time in milliseconds since the epoch.
951146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
952146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @return selected time in UTC milliseconds since the epoch.
953146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
954146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    long getSelectedTimeInMillis() {
955146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
956146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
957146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
958146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
959146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
960146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
961146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
962146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time.normalize(true /* ignore isDst */);
963146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
964146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
965146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Time getSelectedTime() {
966146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
967146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
968146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
969146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
970146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
971146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
972146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
973146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.normalize(true /* ignore isDst */);
974146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time;
975146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
976146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
977989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    Time getSelectedTimeForAccessibility() {
978989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        Time time = new Time(mBaseDate);
979989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        time.setJulianDay(mSelectionDayForAccessibility);
980989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        time.hour = mSelectionHourForAccessibility;
981989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson
982989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        // We ignore the "isDst" field because we want normalize() to figure
983989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        // out the correct DST value and not adjust the selected time based
984989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        // on the current setting of DST.
985989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        time.normalize(true /* ignore isDst */);
986989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        return time;
987989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    }
988989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson
989146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
990146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Returns the start of the selected time in minutes since midnight,
991146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * local time.  The derived class must ensure that this is consistent
992146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * with the return value from getSelectedTimeInMillis().
993146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
994146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    int getSelectedMinutesSinceMidnight() {
995146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return mSelectionHour * MINUTES_PER_HOUR;
996146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
997146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
99846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    int getFirstVisibleHour() {
99946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        return mFirstHour;
100046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
100146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
100246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    void setFirstVisibleHour(int firstHour) {
100346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mFirstHour = firstHour;
100446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mFirstHourOffset = 0;
100546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
100646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
10075b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public void setSelected(Time time, boolean ignoreTime, boolean animateToday) {
1008146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mBaseDate.set(time);
1009989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedHour(mBaseDate.hour);
1010989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedEvent(null);
1011146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevSelectedEvent = null;
1012146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = mBaseDate.toMillis(false /* use isDst */);
1013989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedDay(Time.getJulianDay(millis, mBaseDate.gmtoff));
1014146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
1015146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = true;
1016146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
101746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int gotoY = Integer.MIN_VALUE;
101846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
101946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (!ignoreTime && mGridAreaHeight != -1) {
102046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int lastHour = 0;
102146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
102246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (mBaseDate.hour < mFirstHour) {
102346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // Above visible region
102446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP);
102546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else {
102646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                lastHour = (mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP)
102746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + mFirstHour;
102846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
102946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mBaseDate.hour >= lastHour) {
103046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // Below visible region
103146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
103246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // target hour + 1 (to give it room to see the event) -
103346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // grid height (to get the y of the top of the visible
103446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    // region)
103555a26d7062d5aa1a1b001b7271bb137276ab807eErik                    gotoY = (int) ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f)
103646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                            * (mCellHeight + HOUR_GAP) - mGridAreaHeight);
103746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
103846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
103946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
104046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (DEBUG) {
104146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                Log.e(TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH "
104246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight
104346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        + " ymax " + mMaxViewStartY);
104446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
104546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
104646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (gotoY > mMaxViewStartY) {
104746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = mMaxViewStartY;
104846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) {
104946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                gotoY = 0;
105046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
105146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
105246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
1053146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        recalc();
1054146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1055146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mRemeasure = true;
1056146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
105746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
10585b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        boolean delayAnimateToday = false;
105946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (gotoY != Integer.MIN_VALUE) {
10605b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            ValueAnimator scrollAnim = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY);
10615b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            scrollAnim.setDuration(GOTO_SCROLL_DURATION);
106246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.setInterpolator(new AccelerateDecelerateInterpolator());
106346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.addListener(mAnimatorListener);
106446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            scrollAnim.start();
10655b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            delayAnimateToday = true;
10665b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
10675b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        if (animateToday) {
10685b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            synchronized (mTodayAnimatorListener) {
10695b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (mTodayAnimator != null) {
10705b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.removeAllListeners();
10715b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.cancel();
10725b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
10735b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha",
10745b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                        mAnimateTodayAlpha, 255);
10755b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mAnimateToday = true;
10765b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimatorListener.setFadingIn(true);
10775b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimatorListener.setAnimator(mTodayAnimator);
10785b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.addListener(mTodayAnimatorListener);
10795b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.setDuration(150);
10805b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                if (delayAnimateToday) {
10815b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                    mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION);
10825b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                }
10835b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik                mTodayAnimator.start();
10845b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            }
108546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
1086aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        sendAccessibilityEventAsNeeded(false);
108746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    }
108846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
1089a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan    // Called from animation framework via reflection. Do not remove
109046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    public void setViewStartY(int viewStartY) {
109146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (viewStartY > mMaxViewStartY) {
109246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            viewStartY = mMaxViewStartY;
109346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        }
109446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
109546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mViewStartY = viewStartY;
109646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
109746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        computeFirstHour();
109846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        invalidate();
1099146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
11015b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    public void setAnimateTodayAlpha(int todayAlpha) {
11025b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        mAnimateTodayAlpha = todayAlpha;
11035b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        invalidate();
11045b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik    }
11055b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik
1106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public Time getSelectedDay() {
1107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time time = new Time(mBaseDate);
1108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.setJulianDay(mSelectionDay);
1109146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.hour = mSelectionHour;
1110146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1111146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // We ignore the "isDst" field because we want normalize() to figure
1112146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // out the correct DST value and not adjust the selected time based
1113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // on the current setting of DST.
1114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        time.normalize(true /* ignore isDst */);
1115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return time;
1116146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1117146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1118eaafa2b48be7194a61754604ae37b3d62e9118d8Erik    public void updateTitle() {
1119eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        Time start = new Time(mBaseDate);
1120eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        start.normalize(true);
1121eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        Time end = new Time(start);
1122eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.monthDay += mNumDays - 1;
1123eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        // Move it forward one minute so the formatter doesn't lose a day
1124eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.minute += 1;
1125eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        end.normalize(true);
1126eaafa2b48be7194a61754604ae37b3d62e9118d8Erik
11279e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        long formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
11289e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        if (mNumDays != 1) {
11299e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            // Don't show day of the month if for multi-day view
11309e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            formatFlags |= DateUtils.FORMAT_NO_MONTH_DAY;
11319e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan
11329e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            // Abbreviate the month if showing multiple months
11339e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            if (start.month != end.month) {
11349e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan                formatFlags |= DateUtils.FORMAT_ABBREV_MONTH;
11359e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan            }
11369e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan        }
11379e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan
1138f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        mController.sendEvent(this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT,
11399e9e0678fe1969659dede3909c2cab6d2c3e5c8bMichael Chan                formatFlags, null, null);
1140eaafa2b48be7194a61754604ae37b3d62e9118d8Erik    }
1141eaafa2b48be7194a61754604ae37b3d62e9118d8Erik
114283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan    /**
1143487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * return a negative number if "time" is comes before the visible time
1144487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * range, a positive number if "time" is after the visible time range, and 0
1145487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan     * if it is in the visible time range.
114683b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan     */
1147487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan    public int compareToVisibleTimeRange(Time time) {
1148487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1149487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedHour = mBaseDate.hour;
1150487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedMinute = mBaseDate.minute;
1151487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int savedSec = mBaseDate.second;
1152487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1153487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.hour = 0;
1154487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.minute = 0;
1155487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.second = 0;
1156487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1157030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        if (DEBUG) {
1158030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            Log.d(TAG, "Begin " + mBaseDate.toString());
1159030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            Log.d(TAG, "Diff  " + time.toString());
1160030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        }
1161487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
116283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        // Compare beginning of range
116383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        int diff = Time.compare(time, mBaseDate);
1164487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        if (diff > 0) {
1165487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            // Compare end of range
1166487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.monthDay += mNumDays;
1167487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.normalize(true);
1168487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            diff = Time.compare(time, mBaseDate);
1169487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1170030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            if (DEBUG) Log.d(TAG, "End   " + mBaseDate.toString());
1171487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
1172487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.monthDay -= mNumDays;
1173487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            mBaseDate.normalize(true);
1174487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            if (diff < 0) {
1175487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                // in visible time
1176487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                diff = 0;
1177487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            } else if (diff == 0) {
1178487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                // Midnight of following day
1179487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                diff = 1;
1180487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            }
118183b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan        }
118283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1183030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        if (DEBUG) Log.d(TAG, "Diff: " + diff);
118483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1185487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.hour = savedHour;
1186487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.minute = savedMinute;
1187487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mBaseDate.second = savedSec;
1188487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        return diff;
118983b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan    }
119083b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
1191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void recalc() {
1192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Set the base date to the beginning of the week if we are displaying
1193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // 7 days at a time.
1194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays == 7) {
11956d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            adjustToBeginningOfWeek(mBaseDate);
1196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final long start = mBaseDate.toMillis(false /* use isDst */);
1199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff);
1200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastJulianDay = mFirstJulianDay + mNumDays - 1;
1201146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1202146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY);
1203487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstVisibleDate = mBaseDate.monthDay;
1204487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        mFirstVisibleDayOfWeek = mBaseDate.weekDay;
1205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1206146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
12076d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private void adjustToBeginningOfWeek(Time time) {
12086d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        int dayOfWeek = time.weekDay;
12096d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        int diff = dayOfWeek - mFirstDayOfWeek;
12106d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (diff != 0) {
12116d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            if (diff < 0) {
12126d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan                diff += 7;
12136d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            }
12146d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            time.monthDay -= diff;
12156d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            time.normalize(true /* ignore isDst */);
12166d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
12176d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    }
12186d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
1219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected void onSizeChanged(int width, int height, int oldw, int oldh) {
1221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewWidth = width;
1222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewHeight = height;
1223433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectTop.setSize(mViewWidth, mViewHeight);
1224433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        mEdgeEffectBottom.setSize(mViewWidth, mViewHeight);
1225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int gridAreaWidth = width - mHoursWidth;
1226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays;
1227146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1228d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        // This would be about 1 day worth in a 7 day view
1229d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        mHorizontalSnapBackThreshold = width / 7;
1230d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan
1231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = new Paint();
1232e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
1233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mHoursTextHeight = (int) Math.abs(p.ascent());
1234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        remeasure(width, height);
1235146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1236146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
123746b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa    /**
123846b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     * Measures the space needed for various parts of the view after
123946b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     * loading new events.  This can change if there are all-day events.
124046b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa     */
1241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void remeasure(int width, int height) {
12422fda24534978f55711526dbde747aa50d68dec40RoboErik        // Shrink to fit available space but make sure we can display at least two events
12432fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
12442fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6);
12452fda24534978f55711526dbde747aa50d68dec40RoboErik        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(MAX_UNEXPANDED_ALLDAY_HEIGHT,
12462fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 2);
12472fda24534978f55711526dbde747aa50d68dec40RoboErik        mMaxUnexpandedAlldayEventCount =
12482fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
1249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1250146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear the array of earliest start times, and the array
1251146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // indicating presence of an all-day event.
1252146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++) {
1253146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mEarliestStartHour[day] = 25;  // some big number
1254146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mHasAllDayEvent[day] = false;
1255146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
12572fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxAllDayEvents = mMaxAlldayEvents;
1258c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa
12592fda24534978f55711526dbde747aa50d68dec40RoboErik        // The min is where 24 hours cover the entire visible area
12607939fa59d64c1e6ac95f352e24d761dbacf67547RoboErik        mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, (int) MIN_EVENT_HEIGHT);
12612fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mCellHeight < mMinCellHeight) {
12622fda24534978f55711526dbde747aa50d68dec40RoboErik            mCellHeight = mMinCellHeight;
1263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
12659c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan        // Calculate mAllDayHeight
12663eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mFirstCell = DAY_HEADER_HEIGHT;
1267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int allDayHeight = 0;
1268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (maxAllDayEvents > 0) {
12692fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
1270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is at most one all-day event per day, then use less
1271146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // space (but more than the space for a single event).
1272146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (maxAllDayEvents == 1) {
1273146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                allDayHeight = SINGLE_ALLDAY_HEIGHT;
12742fda24534978f55711526dbde747aa50d68dec40RoboErik            } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount){
1275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Allow the all-day area to grow in height depending on the
1276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // number of all-day events we need to show, up to a limit.
1277f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
12782fda24534978f55711526dbde747aa50d68dec40RoboErik                if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
12792fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT;
12802fda24534978f55711526dbde747aa50d68dec40RoboErik                }
12812fda24534978f55711526dbde747aa50d68dec40RoboErik            } else {
12822fda24534978f55711526dbde747aa50d68dec40RoboErik                // if we have more than the magic number, check if we're animating
12832fda24534978f55711526dbde747aa50d68dec40RoboErik                // and if not adjust the sizes appropriately
12842fda24534978f55711526dbde747aa50d68dec40RoboErik                if (mAnimateDayHeight != 0) {
12852fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Don't shrink the space past the final allDay space. The animation
12862fda24534978f55711526dbde747aa50d68dec40RoboErik                    // continues to hide the last event so the more events text can
12872fda24534978f55711526dbde747aa50d68dec40RoboErik                    // fade in.
12882fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT);
12892fda24534978f55711526dbde747aa50d68dec40RoboErik                } else {
12902fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Try to fit all the events in
12912fda24534978f55711526dbde747aa50d68dec40RoboErik                    allDayHeight = (int) (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
12922fda24534978f55711526dbde747aa50d68dec40RoboErik                    // But clip the area depending on which mode we're in
12932fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
1294e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                        allDayHeight = (int) (mMaxUnexpandedAlldayEventCount *
1295e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                                MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
12962fda24534978f55711526dbde747aa50d68dec40RoboErik                    } else if (allDayHeight > maxAllAllDayHeight) {
12972fda24534978f55711526dbde747aa50d68dec40RoboErik                        allDayHeight = maxAllAllDayHeight;
12982fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
1299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
13013eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN;
1302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
13032fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = false;
1304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
13052fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayHeight = allDayHeight;
1306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mGridAreaHeight = height - mFirstCell;
1308d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
13092fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the expand icon position
1310e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int allDayIconWidth = mExpandAlldayDrawable.getIntrinsicWidth();
1311e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.left = Math.max((mHoursWidth - allDayIconWidth) / 2,
1312e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                EVENT_ALL_DAY_TEXT_LEFT_MARGIN);
1313e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.right = Math.min(mExpandAllDayRect.left + allDayIconWidth, mHoursWidth
1314e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN);
1315cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik        mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN;
1316e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        mExpandAllDayRect.top = mExpandAllDayRect.bottom
1317e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                - mExpandAlldayDrawable.getIntrinsicHeight();
1318e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
131946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP);
1320146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mEventGeometry.setHourHeight(mCellHeight);
1321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
132227d671cdba3e8b50219def2c9e1118abe987f908Daisuke Miyakawa        final long minimumDurationMillis = (long)
132327d671cdba3e8b50219def2c9e1118abe987f908Daisuke Miyakawa                (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f));
13242fda24534978f55711526dbde747aa50d68dec40RoboErik        Event.computePositions(mEvents, minimumDurationMillis);
132546b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa
1326c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // Compute the top of our reachable view
1327c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
1328d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (DEBUG) {
1329d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.e(TAG, "mViewStartY: " + mViewStartY);
1330d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY);
1331d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
1332d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mViewStartY > mMaxViewStartY) {
1333d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = mMaxViewStartY;
1334d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            computeFirstHour();
1335d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
1336146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour == -1) {
1338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            initFirstHour();
1339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHourOffset = 0;
1340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // When we change the base date, the number of all-day events may
1343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // change and that changes the cell height.  When we switch dates,
1344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // we use the mFirstHourOffset from the previous view, but that may
1345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // be too large for the new view if the cell height is smaller.
1346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHourOffset >= mCellHeight + HOUR_GAP) {
1347146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHourOffset = mCellHeight + HOUR_GAP - 1;
1348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset;
1350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
135146b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa        final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP);
1352666fc5e8ddbac15c951aad738bd818ee307eae42Erik        //When we get new events we don't want to dismiss the popup unless the event changes
1353666fc5e8ddbac15c951aad738bd818ee307eae42Erik        if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) {
1354666fc5e8ddbac15c951aad738bd818ee307eae42Erik            mPopup.dismiss();
1355666fc5e8ddbac15c951aad738bd818ee307eae42Erik        }
1356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setWidth(eventAreaWidth - 20);
1357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
1358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1360146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1361146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Initialize the state for another view.  The given view is one that has
1362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * its own bitmap and will use an animation to replace the current view.
1363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * The current view and new view are either both Week views or both Day
1364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * views.  They differ in their base date.
1365146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
1366146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param view the view to initialize.
1367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1368d190418220467c02327dd9b3ae1eae238889720bMichael Chan    private void initView(DayView view) {
1369989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        view.setSelectedHour(mSelectionHour);
1370146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mSelectedEvents.clear();
1371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mComputeSelectedEvents = true;
1372146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mFirstHour = mFirstHour;
1373146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mFirstHourOffset = mFirstHourOffset;
1374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.remeasure(getWidth(), getHeight());
13752fda24534978f55711526dbde747aa50d68dec40RoboErik        view.initAllDayHeights();
1376e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
1377989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        view.setSelectedEvent(null);
1378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.mPrevSelectedEvent = null;
1379487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        view.mFirstDayOfWeek = mFirstDayOfWeek;
1380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (view.mEvents.size() > 0) {
13812fda24534978f55711526dbde747aa50d68dec40RoboErik            view.mSelectionAllday = mSelectionAllday;
1382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
13832fda24534978f55711526dbde747aa50d68dec40RoboErik            view.mSelectionAllday = false;
1384146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1385146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Redraw the screen so that the selection box will be redrawn.  We may
1387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // have scrolled to a different part of the day in some other view
1388146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // so the selection box in this view may no longer be visible.
1389146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.recalc();
1390146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1391146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1392146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1393146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Switch to another view based on what was selected (an event or a free
1394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * slot) and how it was selected (by touch or by trackball).
1395146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
1396146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param trackBallSelection true if the selection was made using the
1397146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * trackball.
1398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1399146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void switchViews(boolean trackBallSelection) {
1400146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event selectedEvent = mSelectedEvent;
1401146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.dismiss();
1403666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
1404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays > 1) {
1405146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // This is the Week view.
1406146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // With touch, we always switch to Day/Agenda View
1407146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // With track ball, if we selected a free slot, then create an event.
1408146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected a specific event, switch to EventInfo view.
1409146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (trackBallSelection) {
1410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (selectedEvent == null) {
1411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Switch to the EditEvent view
1412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long startMillis = getSelectedTimeInMillis();
1413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
1414edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    long extraLong = 0;
1415edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    if (mSelectionAllday) {
1416edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                        extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
1417edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    }
1418edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    mController.sendEventRelatedEventWithExtra(this, EventType.CREATE_EVENT, -1,
1419edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            startMillis, endMillis, -1, -1, extraLong, -1);
1420146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else {
1421aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    if (mIsAccessibilityEnabled) {
1422aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        mAccessibilityMgr.interrupt();
1423aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    }
1424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Switch to the EventInfo view
14259e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
142687f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
142787f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            getSelectedTimeInMillis());
1428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1430146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // This was a touch selection.  If the touch selected a single
1431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // unambiguous event, then view that event.  Otherwise go to
1432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Day/Agenda view.
1433146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectedEvents.size() == 1) {
1434aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    if (mIsAccessibilityEnabled) {
1435aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        mAccessibilityMgr.interrupt();
1436aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    }
14379e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
143887f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
143987f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            getSelectedTimeInMillis());
1440146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1441146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1442146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
1443146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // This is the Day view.
1444146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected a free slot, then create an event.
1445146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If we selected an event, then go to the EventInfo view.
1446146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (selectedEvent == null) {
1447146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Switch to the EditEvent view
1448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long startMillis = getSelectedTimeInMillis();
1449146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
1450edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                long extraLong = 0;
1451edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                if (mSelectionAllday) {
1452edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
1453edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                }
1454edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                mController.sendEventRelatedEventWithExtra(this, EventType.CREATE_EVENT, -1,
1455edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                        startMillis, endMillis, -1, -1, extraLong, -1);
1456146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1457aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                if (mIsAccessibilityEnabled) {
1458aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    mAccessibilityMgr.interrupt();
1459aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                }
14609e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id,
146187f993ff96172f508d3f241f8a425f953c7eb739RoboErik                        selectedEvent.startMillis, selectedEvent.endMillis, 0, 0,
146287f993ff96172f508d3f241f8a425f953c7eb739RoboErik                        getSelectedTimeInMillis());
1463146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1466146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1467146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1468146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onKeyUp(int keyCode, KeyEvent event) {
1469146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = false;
1470146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long duration = event.getEventTime() - event.getDownTime();
1471146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1472146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (keyCode) {
1473146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            case KeyEvent.KEYCODE_DPAD_CENTER:
1474146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectionMode == SELECTION_HIDDEN) {
1475146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Don't do anything unless the selection is visible.
1476146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
1477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1478146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mSelectionMode == SELECTION_PRESSED) {
1480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This was the first press when there was nothing selected.
1481146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Change the selection from the "pressed" state to the
1482146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // the "selected" state.  We treat short-press and
1483146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // long-press the same here because nothing was selected.
1484146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectionMode = SELECTION_SELECTED;
1485146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
1486146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
1487146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1488146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1489146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Check the duration to determine if this was a short press
1490146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (duration < ViewConfiguration.getLongPressTimeout()) {
1491146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    switchViews(true /* trackball */);
1492146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else {
1493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectionMode = SELECTION_LONGPRESS;
1494146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    invalidate();
1495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    performLongClick();
1496146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1497146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                break;
1498ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//            case KeyEvent.KEYCODE_BACK:
1499ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                if (event.isTracking() && !event.isCanceled()) {
1500ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    mPopup.dismiss();
1501ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    mContext.finish();
1502ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                    return true;
1503ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                }
1504ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//                break;
1505146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return super.onKeyUp(keyCode, event);
1507146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1508146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1509146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
1510146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onKeyDown(int keyCode, KeyEvent event) {
1511146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionMode == SELECTION_HIDDEN) {
1512146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
1513146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_UP
1514146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
1515146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Display the selection box but don't move or select it
1516146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // on this key press.
1517146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionMode = SELECTION_SELECTED;
1518146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1519146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
1520146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
1521146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Display the selection box but don't select it
1522146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // on this key press.
1523146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectionMode = SELECTION_PRESSED;
1524146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                invalidate();
1525146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
1526146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1527146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1528146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1529146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_SELECTED;
1530146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = false;
1531146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean redraw;
1532146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectionDay = mSelectionDay;
1533146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1534146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (keyCode) {
153546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DEL:
153646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // Delete the selected event, if any
153746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                Event selectedEvent = mSelectedEvent;
153846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (selectedEvent == null) {
153946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return false;
154046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
154146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mPopup.dismiss();
154246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mLastPopupEventID = INVALID_EVENT_ID;
1543146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
154446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long begin = selectedEvent.startMillis;
154546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long end = selectedEvent.endMillis;
154646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                long id = selectedEvent.id;
154746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mDeleteEventHelper.delete(begin, end, id, -1);
1548b10f5762d0e50bac6ba31ec332e7513869b55bf3Michael Chan                return true;
154946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_ENTER:
155046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                switchViews(true /* trackball or keyboard */);
155146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return true;
155246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_BACK:
155346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (event.getRepeatCount() == 0) {
155446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    event.startTracking();
155546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
155646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
155746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onKeyDown(keyCode, event);
155846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_LEFT:
155946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
1560989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                    setSelectedEvent(mSelectedEvent.nextLeft);
156146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
156246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
156346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
156446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    selectionDay -= 1;
156546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
156646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
156746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1568146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
156946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_RIGHT:
157046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
1571989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                    setSelectedEvent(mSelectedEvent.nextRight);
157246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
157346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
157446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
157546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    selectionDay += 1;
157646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
157746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
157846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1579146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
158046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_UP:
158146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
1582989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                    setSelectedEvent(mSelectedEvent.nextUp);
1583146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
158446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
158546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
15862fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (!mSelectionAllday) {
1587989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                        setSelectedHour(mSelectionHour - 1);
158846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        adjustHourSelection();
158946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectedEvents.clear();
159046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mComputeSelectedEvents = true;
159146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
159246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
159346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
159446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1595146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
159646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case KeyEvent.KEYCODE_DPAD_DOWN:
159746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent != null) {
1598989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                    setSelectedEvent(mSelectedEvent.nextDown);
1599146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
160046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mSelectedEvent == null) {
160146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mLastPopupEventID = INVALID_EVENT_ID;
16022fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (mSelectionAllday) {
16032fda24534978f55711526dbde747aa50d68dec40RoboErik                        mSelectionAllday = false;
160446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    } else {
1605989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                        setSelectedHour(mSelectionHour + 1);
160646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        adjustHourSelection();
160746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mSelectedEvents.clear();
160846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mComputeSelectedEvents = true;
160946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
161046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
161146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                redraw = true;
161246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                break;
1613146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
161446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            default:
161546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onKeyDown(keyCode, event);
1616146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1617146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1618146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((selectionDay < mFirstJulianDay) || (selectionDay > mLastJulianDay)) {
1619d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView view = (DayView) mViewSwitcher.getNextView();
1620146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Time date = view.mBaseDate;
1621146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.set(mBaseDate);
1622146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (selectionDay < mFirstJulianDay) {
1623146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                date.monthDay -= mNumDays;
1624146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
1625146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                date.monthDay += mNumDays;
1626146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1627146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.normalize(true /* ignore isDst */);
1628989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            view.setSelectedDay(selectionDay);
1629146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1630146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            initView(view);
163183b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
163283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            Time end = new Time(date);
163383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            end.monthDay += mNumDays - 1;
16343458a1775920dda53099b7f1913cd4ec4c8b4095Michael Chan            mController.sendEvent(this, EventType.GO_TO, date, end, -1, ViewType.CURRENT);
1635146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
1636146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
163704b288958078f8b29870dcbb2239df448c559610Erik        if (mSelectionDay != selectionDay) {
163804b288958078f8b29870dcbb2239df448c559610Erik            Time date = new Time(mBaseDate);
163904b288958078f8b29870dcbb2239df448c559610Erik            date.setJulianDay(selectionDay);
164004b288958078f8b29870dcbb2239df448c559610Erik            date.hour = mSelectionHour;
164104b288958078f8b29870dcbb2239df448c559610Erik            mController.sendEvent(this, EventType.GO_TO, date, date, -1, ViewType.CURRENT);
164204b288958078f8b29870dcbb2239df448c559610Erik        }
1643989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedDay(selectionDay);
1644146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
1645146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = true;
16468307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        mUpdateToast = true;
1647146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (redraw) {
1649146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
1650146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
1651146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1652146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1653146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return super.onKeyDown(keyCode, event);
1654146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1655146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
16569ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
16579ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    @Override
16589ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    public boolean onHoverEvent(MotionEvent event) {
16599ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG) {
16609ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            int action = event.getAction();
16619ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            switch (action) {
16629ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                case MotionEvent.ACTION_HOVER_ENTER:
16639ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_HOVER_ENTER");
16649ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    break;
16659ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                case MotionEvent.ACTION_HOVER_MOVE:
16669ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_HOVER_MOVE");
16679ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    break;
16689ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                case MotionEvent.ACTION_HOVER_EXIT:
16699ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_HOVER_EXIT");
16709ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    break;
16719ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                default:
16729ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "Unknown hover event action. " + event);
16739ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
16749ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        }
16759ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
16769ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        // Mouse also generates hover events
16779ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        // Send accessibility events if accessibility and exploration are on.
16789ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (!mTouchExplorationEnabled) {
16799ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            return super.onHoverEvent(event);
1680259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1681989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) {
1682989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectionFromPosition((int) event.getX(), (int) event.getY(), true);
16839ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            invalidate();
16849ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        }
16859ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        return true;
1686259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1687259b8f9a21fffd3858218581e9daaf689b6b888fErik
16889ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    private boolean isTouchExplorationEnabled() {
1689aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        return mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled();
16909ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan    }
1691259b8f9a21fffd3858218581e9daaf689b6b888fErik
1692aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan    private void sendAccessibilityEventAsNeeded(boolean speakEvents) {
1693aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        if (!mIsAccessibilityEnabled) {
1694be569a176153e0b4b4606a022ab30cf2f5bf719dRoboErik            return;
1695be569a176153e0b4b4606a022ab30cf2f5bf719dRoboErik        }
1696989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        boolean dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility;
1697989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        boolean hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility;
1698989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        if (dayChanged || hourChanged ||
1699989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                mLastSelectedEventForAccessibility != mSelectedEventForAccessibility) {
1700989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            mLastSelectionDayForAccessibility = mSelectionDayForAccessibility;
1701989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            mLastSelectionHourForAccessibility = mSelectionHourForAccessibility;
1702989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            mLastSelectedEventForAccessibility = mSelectedEventForAccessibility;
17039ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
17049ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            StringBuilder b = new StringBuilder();
17059ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
17069ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            // Announce only the changes i.e. day or hour or both
17079ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (dayChanged) {
1708989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                b.append(getSelectedTimeForAccessibility().format("%A "));
17099ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
17109ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (hourChanged) {
1711989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                b.append(getSelectedTimeForAccessibility().format(mIs24HourFormat ? "%k" : "%l%p"));
17129ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
1713aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            if (dayChanged || hourChanged) {
1714aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                b.append(PERIOD_SPACE);
1715aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            }
17169ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
1717aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            if (speakEvents) {
1718aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                if (mEventCountTemplate == null) {
1719aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    mEventCountTemplate = mContext.getString(R.string.template_announce_item_index);
1720aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                }
1721aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan
1722aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                // Read out the relevant event(s)
1723aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                int numEvents = mSelectedEvents.size();
1724aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                if (numEvents > 0) {
1725989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                    if (mSelectedEventForAccessibility == null) {
1726aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        // Read out all the events
1727aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        int i = 1;
1728aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        for (Event calEvent : mSelectedEvents) {
1729aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            if (numEvents > 1) {
1730aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                // Read out x of numEvents if there are more than one event
1731aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                mStringBuilder.setLength(0);
1732aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                b.append(mFormatter.format(mEventCountTemplate, i++, numEvents));
1733aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                                b.append(" ");
1734aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            }
1735aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            appendEventAccessibilityString(b, calEvent);
1736aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                        }
1737aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    } else {
17389ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                        if (numEvents > 1) {
17399ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            // Read out x of numEvents if there are more than one event
17409ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            mStringBuilder.setLength(0);
1741aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                            b.append(mFormatter.format(mEventCountTemplate, mSelectedEvents
1742989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                                    .indexOf(mSelectedEventForAccessibility) + 1, numEvents));
17439ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            b.append(" ");
17449ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                        }
1745989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                        appendEventAccessibilityString(b, mSelectedEventForAccessibility);
1746259b8f9a21fffd3858218581e9daaf689b6b888fErik                    }
17479ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                } else {
1748aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                    b.append(mCreateNewEventString);
1749259b8f9a21fffd3858218581e9daaf689b6b888fErik                }
17509ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            }
17519ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
1752aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            if (dayChanged || hourChanged || speakEvents) {
1753aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                AccessibilityEvent event = AccessibilityEvent
1754d01a7b6aab2bfe79e60359137d1bfd7ec9672368Michael Chan                        .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
1755aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                CharSequence msg = b.toString();
1756aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                event.getText().add(msg);
1757aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                event.setAddedCount(msg.length());
1758aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                sendAccessibilityEventUnchecked(event);
1759aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            }
1760259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1761259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1762259b8f9a21fffd3858218581e9daaf689b6b888fErik
1763259b8f9a21fffd3858218581e9daaf689b6b888fErik    /**
1764259b8f9a21fffd3858218581e9daaf689b6b888fErik     * @param b
1765259b8f9a21fffd3858218581e9daaf689b6b888fErik     * @param calEvent
1766259b8f9a21fffd3858218581e9daaf689b6b888fErik     */
1767259b8f9a21fffd3858218581e9daaf689b6b888fErik    private void appendEventAccessibilityString(StringBuilder b, Event calEvent) {
1768259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(calEvent.getTitleAndLocation());
1769259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(PERIOD_SPACE);
1770259b8f9a21fffd3858218581e9daaf689b6b888fErik        String when;
1771259b8f9a21fffd3858218581e9daaf689b6b888fErik        int flags = DateUtils.FORMAT_SHOW_DATE;
1772259b8f9a21fffd3858218581e9daaf689b6b888fErik        if (calEvent.allDay) {
1773259b8f9a21fffd3858218581e9daaf689b6b888fErik            flags |= DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY;
1774259b8f9a21fffd3858218581e9daaf689b6b888fErik        } else {
1775259b8f9a21fffd3858218581e9daaf689b6b888fErik            flags |= DateUtils.FORMAT_SHOW_TIME;
1776259b8f9a21fffd3858218581e9daaf689b6b888fErik            if (DateFormat.is24HourFormat(mContext)) {
1777259b8f9a21fffd3858218581e9daaf689b6b888fErik                flags |= DateUtils.FORMAT_24HOUR;
1778259b8f9a21fffd3858218581e9daaf689b6b888fErik            }
1779259b8f9a21fffd3858218581e9daaf689b6b888fErik        }
1780259b8f9a21fffd3858218581e9daaf689b6b888fErik        when = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags);
1781259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(when);
1782259b8f9a21fffd3858218581e9daaf689b6b888fErik        b.append(PERIOD_SPACE);
1783259b8f9a21fffd3858218581e9daaf689b6b888fErik    }
1784259b8f9a21fffd3858218581e9daaf689b6b888fErik
17856d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    private class GotoBroadcaster implements Animation.AnimationListener {
17866d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final int mCounter;
17876d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final Time mStart;
17886d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        private final Time mEnd;
17896d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17906d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public GotoBroadcaster(Time start, Time end) {
17916d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mCounter = ++sCounter;
17926d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mStart = start;
17936d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            mEnd = end;
17946d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
17956d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
17966d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
17976d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationEnd(Animation animation) {
1798f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            DayView view = (DayView) mViewSwitcher.getCurrentView();
1799f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view.mViewStartX = 0;
1800f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view = (DayView) mViewSwitcher.getNextView();
1801f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            view.mViewStartX = 0;
1802f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan
18036d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            if (mCounter == sCounter) {
1804f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan                mController.sendEvent(this, EventType.GO_TO, mStart, mEnd, null, -1,
1805f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan                        ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null);
18066d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            }
18076d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
18086d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
18096d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
18106d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationRepeat(Animation animation) {
18116d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
18126d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
18136d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        @Override
18146d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        public void onAnimationStart(Animation animation) {
18156d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
18166d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan    }
18176d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
18182c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private View switchViews(boolean forward, float xOffSet, float width, float velocity) {
181995d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        mAnimationDistance = width - xOffSet;
182095d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        if (DEBUG) {
182195d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            Log.d(TAG, "switchViews(" + forward + ") O:" + xOffSet + " Dist:" + mAnimationDistance);
182295d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        }
182395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
1824ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float progress = Math.abs(xOffSet) / width;
1825ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (progress > 1.0f) {
1826ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            progress = 1.0f;
1827ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
1828ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1829ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float inFromXValue, inToXValue;
1830ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        float outFromXValue, outToXValue;
1831ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (forward) {
1832ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inFromXValue = 1.0f - progress;
1833ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inToXValue = 0.0f;
1834ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outFromXValue = -progress;
1835ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outToXValue = -1.0f;
1836ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        } else {
1837ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inFromXValue = progress - 1.0f;
1838ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            inToXValue = 0.0f;
1839ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outFromXValue = progress;
1840ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            outToXValue = 1.0f;
1841ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
1842ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
18436d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        final Time start = new Time(mBaseDate.timezone);
18446d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        start.set(mController.getTime());
18456d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (forward) {
18466d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            start.monthDay += mNumDays;
18476d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        } else {
18486d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            start.monthDay -= mNumDays;
18496d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
1850f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        mController.setTime(start.normalize(true));
1851f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan
1852f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan        Time newSelected = start;
18536d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
18546d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        if (mNumDays == 7) {
1855f0868f61983ff1b04a49f1b6f2ef6d49311011e8Michael Chan            newSelected = new Time(start);
18566d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan            adjustToBeginningOfWeek(start);
18576d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
18586d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
18596d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        final Time end = new Time(start);
18606d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        end.monthDay += mNumDays - 1;
18616d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
1862ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        // We have to allocate these animation objects each time we switch views
1863ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        // because that is the only way to set the animation parameters.
1864ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        TranslateAnimation inAnimation = new TranslateAnimation(
1865ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, inFromXValue,
1866ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, inToXValue,
1867ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f,
1868ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f);
1869ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1870ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        TranslateAnimation outAnimation = new TranslateAnimation(
1871ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, outFromXValue,
1872ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.RELATIVE_TO_SELF, outToXValue,
1873ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f,
1874ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                Animation.ABSOLUTE, 0.0f);
1875ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
18762c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        long duration = calculateDuration(width - Math.abs(xOffSet), width, velocity);
1877ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        inAnimation.setDuration(duration);
18782c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        inAnimation.setInterpolator(mHScrollInterpolator);
18792c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        outAnimation.setInterpolator(mHScrollInterpolator);
1880ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        outAnimation.setDuration(duration);
18816d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        outAnimation.setAnimationListener(new GotoBroadcaster(start, end));
1882ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.setInAnimation(inAnimation);
1883ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.setOutAnimation(outAnimation);
1884ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1885d190418220467c02327dd9b3ae1eae238889720bMichael Chan        DayView view = (DayView) mViewSwitcher.getCurrentView();
1886ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.cleanup();
1887ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        mViewSwitcher.showNext();
1888d190418220467c02327dd9b3ae1eae238889720bMichael Chan        view = (DayView) mViewSwitcher.getCurrentView();
18895b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        view.setSelected(newSelected, true, false);
1890ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.requestFocus();
1891ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        view.reloadEvents();
1892eaafa2b48be7194a61754604ae37b3d62e9118d8Erik        view.updateTitle();
189358f5055e1f3740382b4507e6eb88eebc2d6c21c1Michael Chan        view.restartCurrentTimeUpdates();
1894e98dd73c67205b01f5348271d9a7a630003ecf52David Brown
1895ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        return view;
1896ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    }
1897ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
1898146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // This is called after scrolling stops to move the selected hour
1899146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // to the visible part of the screen.
1900146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void resetSelectedHour() {
1901146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < mFirstHour + 1) {
1902989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedHour(mFirstHour + 1);
1903989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedEvent(null);
1904146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvents.clear();
1905146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mComputeSelectedEvents = true;
1906146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (mSelectionHour > mFirstHour + mNumHours - 3) {
1907989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedHour(mFirstHour + mNumHours - 3);
1908989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedEvent(null);
1909146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectedEvents.clear();
1910146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mComputeSelectedEvents = true;
1911146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1912146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1913146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1914146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void initFirstHour() {
1915d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mFirstHour = mSelectionHour - mNumHours / 5;
1916146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour < 0) {
1917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHour = 0;
1918146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (mFirstHour + mNumHours > 24) {
1919146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mFirstHour = 24 - mNumHours;
1920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1921146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1922146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1923146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
1924146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Recomputes the first full hour that is visible on screen after the
1925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * screen is scrolled.
1926146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
1927146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeFirstHour() {
1928146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Compute the first full hour that is visible on screen
1929146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP);
1930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY;
1931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1932146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1933146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void adjustHourSelection() {
1934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < 0) {
1935989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedHour(0);
19362fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > 0) {
1937146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
19382fda24534978f55711526dbde747aa50d68dec40RoboErik                mSelectionAllday = true;
1939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1940146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour > 23) {
1943989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedHour(23);
1944146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1945146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1946146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the selected hour is at least 2 time slots from the top and
1947146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // bottom of the screen, then don't scroll the view.
1948146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour < mFirstHour + 1) {
1949146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there are all-days events for the selected day but there
1950146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // are no more normal events earlier in the day, then jump to
1951146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // the all-day event area.
1952146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Exception 1: allow the user to scroll to 8am with the trackball
1953146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // before jumping to the all-day event area.
1954146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Exception 2: if 12am is on screen, then allow the user to select
1955146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // 12am before going up to the all-day event area.
1956146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int daynum = mSelectionDay - mFirstJulianDay;
19574696e93697ae2316ef38ba9148e35052313162aeJames Kung            if (daynum < mEarliestStartHour.length && daynum >= 0
19584696e93697ae2316ef38ba9148e35052313162aeJames Kung                    && mMaxAlldayEvents > 0
19594696e93697ae2316ef38ba9148e35052313162aeJames Kung                    && mEarliestStartHour[daynum] > mSelectionHour
1960146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    && mFirstHour > 0 && mFirstHour < 8) {
1961146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
19622fda24534978f55711526dbde747aa50d68dec40RoboErik                mSelectionAllday = true;
1963989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                setSelectedHour(mFirstHour + 1);
1964146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1965146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1966146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1967146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mFirstHour > 0) {
1968146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mFirstHour -= 1;
1969146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY -= (mCellHeight + HOUR_GAP);
1970146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (mViewStartY < 0) {
1971146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mViewStartY = 0;
1972146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1973146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1974146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1975146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1976146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1977146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionHour > mFirstHour + mNumHours - 3) {
1978146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mFirstHour < 24 - mNumHours) {
1979146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mFirstHour += 1;
1980146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY += (mCellHeight + HOUR_GAP);
1981c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                if (mViewStartY > mMaxViewStartY) {
1982c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                    mViewStartY = mMaxViewStartY;
1983146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
1984146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return;
1985146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) {
1986c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                mViewStartY = mMaxViewStartY;
1987146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
1988146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1989146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1990146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1991146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    void clearCachedEvents() {
1992146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastReloadMillis = 0;
1993146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
1994146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
1995056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Runnable mCancelCallback = new Runnable() {
1996146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
1997146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            clearCachedEvents();
1998146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
1999146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    };
2000146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
200146b05ffc746a3150b6bad5d3a72138ffa38b0a99Daisuke Miyakawa    /* package */ void reloadEvents() {
2002146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Protect against this being called before this view has been
2003146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // initialized.
2004ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        if (mContext == null) {
2005ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//            return;
2006ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        }
2007146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2008731f1cb808b7586b93615b408ab2636081fab0dcErik        // Make sure our time zones are up to date
2009731f1cb808b7586b93615b408ab2636081fab0dcErik        mTZUpdater.run();
2010731f1cb808b7586b93615b408ab2636081fab0dcErik
2011989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedEvent(null);
2012146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevSelectedEvent = null;
2013146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
2014146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2015146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // The start date is the beginning of the week at 12am
2016731f1cb808b7586b93615b408ab2636081fab0dcErik        Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater));
2017146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.set(mBaseDate);
2018146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.hour = 0;
2019146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.minute = 0;
2020146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        weekStart.second = 0;
2021146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long millis = weekStart.normalize(true /* ignore isDst */);
2022146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2023146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Avoid reloading events unnecessarily.
2024146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (millis == mLastReloadMillis) {
2025146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
2026146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2027146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mLastReloadMillis = millis;
2028146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2029146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // load events in the background
2030ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan//        mContext.startProgressSpinner();
2031146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final ArrayList<Event> events = new ArrayList<Event>();
2032b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik        mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, new Runnable() {
20334696e93697ae2316ef38ba9148e35052313162aeJames Kung
2034146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            public void run() {
20351662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                boolean fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay;
2036146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mEvents = events;
20371662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                mLoadedFirstJulianDay = mFirstJulianDay;
203808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mAllDayEvents == null) {
203908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayEvents = new ArrayList<Event>();
204008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
204108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayEvents.clear();
204208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
204308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
204408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                // Create a shorter array for all day events
204546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                for (Event e : events) {
204668655b5af2b7e5f872a103858ec58a19042123ceRoboErik                    if (e.drawAsAllday()) {
204708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                        mAllDayEvents.add(e);
204808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    }
204908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
205008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
205108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                // New events, new layouts
205208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mLayouts == null || mLayouts.length < events.size()) {
205308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mLayouts = new StaticLayout[events.size()];
205408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
205508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    Arrays.fill(mLayouts, null);
20563e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh                }
205708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
205808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) {
205908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    mAllDayLayouts = new StaticLayout[events.size()];
206008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                } else {
206108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                    Arrays.fill(mAllDayLayouts, null);
206208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                }
206308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
20642fda24534978f55711526dbde747aa50d68dec40RoboErik                computeEventRelations();
20652fda24534978f55711526dbde747aa50d68dec40RoboErik
2066146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mRemeasure = true;
2067146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mComputeSelectedEvents = true;
2068146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                recalc();
20690a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson
20700a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson                // Start animation to cross fade the events
20711662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                if (fadeinEvents) {
20721662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                    if (mEventsCrossFadeAnimation == null) {
20731662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                        mEventsCrossFadeAnimation =
20741662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                                ObjectAnimator.ofInt(DayView.this, "EventsAlpha", 0, 255);
20751662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                        mEventsCrossFadeAnimation.setDuration(EVENTS_CROSS_FADE_DURATION);
20761662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                    }
20771662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                    mEventsCrossFadeAnimation.start();
20781662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                } else{
20791662b85ef3691df66820fd2a469bcb95993c14a5Isaac Katzenelson                    invalidate();
20800a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson                }
2081146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2082146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }, mCancelCallback);
2083146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2084146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
20850a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    public void setEventsAlpha(int alpha) {
20860a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        mEventsAlpha = alpha;
20870a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        invalidate();
20880a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    }
20890a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson
20900a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    public int getEventsAlpha() {
20910a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        return mEventsAlpha;
20920a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    }
20930a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson
20940a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    public void stopEventsAnimation() {
20950a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        if (mEventsCrossFadeAnimation != null) {
20960a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson            mEventsCrossFadeAnimation.cancel();
20970a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        }
20980a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        mEventsAlpha = 255;
20990a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson    }
21000a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson
21012fda24534978f55711526dbde747aa50d68dec40RoboErik    private void computeEventRelations() {
21022fda24534978f55711526dbde747aa50d68dec40RoboErik        // Compute the layout relation between each event before measuring cell
21032fda24534978f55711526dbde747aa50d68dec40RoboErik        // width, as the cell width should be adjusted along with the relation.
21042fda24534978f55711526dbde747aa50d68dec40RoboErik        //
21052fda24534978f55711526dbde747aa50d68dec40RoboErik        // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm)
21062fda24534978f55711526dbde747aa50d68dec40RoboErik        // We should mark them as "overwapped". Though they are not overwapped logically, but
21072fda24534978f55711526dbde747aa50d68dec40RoboErik        // minimum cell height implicitly expands the cell height of A and it should look like
21082fda24534978f55711526dbde747aa50d68dec40RoboErik        // (1:00pm - 1:15pm) after the cell height adjustment.
21092fda24534978f55711526dbde747aa50d68dec40RoboErik
21102fda24534978f55711526dbde747aa50d68dec40RoboErik        // Compute the space needed for the all-day events, if any.
21112fda24534978f55711526dbde747aa50d68dec40RoboErik        // Make a pass over all the events, and keep track of the maximum
21122fda24534978f55711526dbde747aa50d68dec40RoboErik        // number of all-day events in any one day.  Also, keep track of
21132fda24534978f55711526dbde747aa50d68dec40RoboErik        // the earliest event in each day.
21142fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxAllDayEvents = 0;
21152fda24534978f55711526dbde747aa50d68dec40RoboErik        final ArrayList<Event> events = mEvents;
21162fda24534978f55711526dbde747aa50d68dec40RoboErik        final int len = events.size();
21172fda24534978f55711526dbde747aa50d68dec40RoboErik        // Num of all-day-events on each day.
21182fda24534978f55711526dbde747aa50d68dec40RoboErik        final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1];
21192fda24534978f55711526dbde747aa50d68dec40RoboErik        Arrays.fill(eventsCount, 0);
21202fda24534978f55711526dbde747aa50d68dec40RoboErik        for (int ii = 0; ii < len; ii++) {
21212fda24534978f55711526dbde747aa50d68dec40RoboErik            Event event = events.get(ii);
21222fda24534978f55711526dbde747aa50d68dec40RoboErik            if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) {
21232fda24534978f55711526dbde747aa50d68dec40RoboErik                continue;
21242fda24534978f55711526dbde747aa50d68dec40RoboErik            }
21252fda24534978f55711526dbde747aa50d68dec40RoboErik            if (event.drawAsAllday()) {
21262fda24534978f55711526dbde747aa50d68dec40RoboErik                // Count all the events being drawn as allDay events
21272fda24534978f55711526dbde747aa50d68dec40RoboErik                final int firstDay = Math.max(event.startDay, mFirstJulianDay);
21282fda24534978f55711526dbde747aa50d68dec40RoboErik                final int lastDay = Math.min(event.endDay, mLastJulianDay);
21292fda24534978f55711526dbde747aa50d68dec40RoboErik                for (int day = firstDay; day <= lastDay; day++) {
21302fda24534978f55711526dbde747aa50d68dec40RoboErik                    final int count = ++eventsCount[day - mFirstJulianDay];
21312fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (maxAllDayEvents < count) {
21322fda24534978f55711526dbde747aa50d68dec40RoboErik                        maxAllDayEvents = count;
21332fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
21342fda24534978f55711526dbde747aa50d68dec40RoboErik                }
21352fda24534978f55711526dbde747aa50d68dec40RoboErik
21362fda24534978f55711526dbde747aa50d68dec40RoboErik                int daynum = event.startDay - mFirstJulianDay;
21372fda24534978f55711526dbde747aa50d68dec40RoboErik                int durationDays = event.endDay - event.startDay + 1;
21382fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum < 0) {
21392fda24534978f55711526dbde747aa50d68dec40RoboErik                    durationDays += daynum;
21402fda24534978f55711526dbde747aa50d68dec40RoboErik                    daynum = 0;
21412fda24534978f55711526dbde747aa50d68dec40RoboErik                }
21422fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum + durationDays > mNumDays) {
21432fda24534978f55711526dbde747aa50d68dec40RoboErik                    durationDays = mNumDays - daynum;
21442fda24534978f55711526dbde747aa50d68dec40RoboErik                }
21452fda24534978f55711526dbde747aa50d68dec40RoboErik                for (int day = daynum; durationDays > 0; day++, durationDays--) {
21462fda24534978f55711526dbde747aa50d68dec40RoboErik                    mHasAllDayEvent[day] = true;
21472fda24534978f55711526dbde747aa50d68dec40RoboErik                }
21482fda24534978f55711526dbde747aa50d68dec40RoboErik            } else {
21492fda24534978f55711526dbde747aa50d68dec40RoboErik                int daynum = event.startDay - mFirstJulianDay;
21502fda24534978f55711526dbde747aa50d68dec40RoboErik                int hour = event.startTime / 60;
21512fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum >= 0 && hour < mEarliestStartHour[daynum]) {
21522fda24534978f55711526dbde747aa50d68dec40RoboErik                    mEarliestStartHour[daynum] = hour;
21532fda24534978f55711526dbde747aa50d68dec40RoboErik                }
21542fda24534978f55711526dbde747aa50d68dec40RoboErik
21552fda24534978f55711526dbde747aa50d68dec40RoboErik                // Also check the end hour in case the event spans more than
21562fda24534978f55711526dbde747aa50d68dec40RoboErik                // one day.
21572fda24534978f55711526dbde747aa50d68dec40RoboErik                daynum = event.endDay - mFirstJulianDay;
21582fda24534978f55711526dbde747aa50d68dec40RoboErik                hour = event.endTime / 60;
21592fda24534978f55711526dbde747aa50d68dec40RoboErik                if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) {
21602fda24534978f55711526dbde747aa50d68dec40RoboErik                    mEarliestStartHour[daynum] = hour;
21612fda24534978f55711526dbde747aa50d68dec40RoboErik                }
21622fda24534978f55711526dbde747aa50d68dec40RoboErik            }
21632fda24534978f55711526dbde747aa50d68dec40RoboErik        }
21642fda24534978f55711526dbde747aa50d68dec40RoboErik        mMaxAlldayEvents = maxAllDayEvents;
21652fda24534978f55711526dbde747aa50d68dec40RoboErik        initAllDayHeights();
21662fda24534978f55711526dbde747aa50d68dec40RoboErik    }
21672fda24534978f55711526dbde747aa50d68dec40RoboErik
2168146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
2169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    protected void onDraw(Canvas canvas) {
2170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mRemeasure) {
2171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            remeasure(getWidth(), getHeight());
2172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mRemeasure = false;
2173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2174c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.save();
2175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
21762fda24534978f55711526dbde747aa50d68dec40RoboErik        float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight;
2177c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // offset canvas by the current drag and header position
2178c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.translate(-mViewStartX, yTranslate);
2179c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // clip to everything below the allDay area
2180c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        Rect dest = mDestRect;
2181c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.top = (int) (mFirstCell - yTranslate);
2182c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.bottom = (int) (mViewHeight - yTranslate);
2183c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.left = 0;
2184c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        dest.right = mViewWidth;
2185c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.save();
2186c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.clipRect(dest);
2187c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // Draw the movable part of the view
2188c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        doDraw(canvas);
2189c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        // restore to having no clip
2190c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        canvas.restore();
2191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
2193c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            float xTranslate;
2194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mViewStartX > 0) {
2195c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                xTranslate = mViewWidth;
2196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
2197c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik                xTranslate = -mViewWidth;
2198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2199c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // Move the canvas around to prep it for the next view
2200c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // specifically, shift it by a screen and undo the
2201c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // yTranslation which will be redone in the nextView's onDraw().
2202c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(xTranslate, -yTranslate);
2203d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView nextView = (DayView) mViewSwitcher.getNextView();
2204146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Prevent infinite recursive calls to onDraw().
2206146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE;
2207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2208146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            nextView.onDraw(canvas);
2209c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // Move it back for this view
2210c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(-xTranslate, 0);
2211c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        } else {
2212c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // If we drew another view we already translated it back
2213c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // If we didn't draw another view we should be at the edge of the
2214c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            // screen
2215c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik            canvas.translate(mViewStartX, -yTranslate);
2216146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2217146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the fixed areas (that don't scroll) directly to the canvas.
2219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawAfterScroll(canvas);
22208307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        if (mComputeSelectedEvents && mUpdateToast) {
22218307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik            updateEventDetails();
22228307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik            mUpdateToast = false;
22238307109ac75b748ae707f4d4cb9a9d28c2dbaae6Erik        }
2224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mComputeSelectedEvents = false;
2225433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
2226433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // Draw overscroll glow
2227433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        if (!mEdgeEffectTop.isFinished()) {
2228433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (DAY_HEADER_HEIGHT != 0) {
2229433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                canvas.translate(0, DAY_HEADER_HEIGHT);
2230433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2231433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mEdgeEffectTop.draw(canvas)) {
2232433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                invalidate();
2233433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2234433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (DAY_HEADER_HEIGHT != 0) {
2235433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                canvas.translate(0, -DAY_HEADER_HEIGHT);
2236433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2237433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
2238433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        if (!mEdgeEffectBottom.isFinished()) {
2239433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            canvas.rotate(180, mViewWidth/2, mViewHeight/2);
2240433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mEdgeEffectBottom.draw(canvas)) {
2241433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                invalidate();
2242433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
2243433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
2244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        canvas.restore();
2245146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2246146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2247146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawAfterScroll(Canvas canvas) {
2248146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mPaint;
2249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect r = mRect;
2250146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2251e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        drawAllDayHighlights(r, canvas, p);
22522fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents != 0) {
22531ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p);
22548f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            drawUpperLeftCorner(r, canvas, p);
2255146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2256146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2257c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        drawScrollLine(r, canvas, p);
22583eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        drawDayHeaderLoop(r, canvas, p);
2259146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the AM and PM indicators if we're in 12 hour mode
2261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!mIs24HourFormat) {
2262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            drawAmPm(canvas, p);
2263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
226646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    // This isn't really the upper-left corner. It's the square area just
2267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // below the upper-left corner, above the hours and to the left of the
2268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // all-day area.
22698f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) {
22708f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        setupHourTextPaint(p);
22712fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
2272cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            // Draw the allDay expand/collapse icon
2273cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            if (mUseExpandIcon) {
2274cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mExpandAlldayDrawable.setBounds(mExpandAllDayRect);
2275cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mExpandAlldayDrawable.draw(canvas);
2276cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            } else {
2277cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mCollapseAlldayDrawable.setBounds(mExpandAllDayRect);
2278cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                mCollapseAlldayDrawable.draw(canvas);
2279cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik            }
22802fda24534978f55711526dbde747aa50d68dec40RoboErik        }
22818f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    }
2282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2283c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik    private void drawScrollLine(Rect r, Canvas canvas, Paint p) {
2284b03348cec4c175846f074a656db5c121d5467e2dMichael Chan        final int right = computeDayLeftPosition(mNumDays);
22858f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        final int y = mFirstCell - 1;
22868f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
2287c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        p.setAntiAlias(false);
22888f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setStyle(Style.FILL);
22898f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
22908f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setColor(mCalendarGridLineInnerHorizontalColor);
22918f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
22928f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p);
2293c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik        p.setAntiAlias(true);
2294c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik    }
2295c29b2d3342d43c2662dd2fe51a28bc7e8acd7b36Erik
2296b03348cec4c175846f074a656db5c121d5467e2dMichael Chan    // Computes the x position for the left side of the given day (base 0)
2297e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private int computeDayLeftPosition(int day) {
2298e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int effectiveWidth = mViewWidth - mHoursWidth;
2299e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        return day * effectiveWidth / mNumDays + mHoursWidth;
2300e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2301e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2302e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawAllDayHighlights(Rect r, Canvas canvas, Paint p) {
2303cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan        if (mFutureBgColor != 0) {
2304cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            // First, color the labels area light gray
2305e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.top = 0;
2306cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            r.bottom = DAY_HEADER_HEIGHT;
2307cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            r.left = 0;
2308cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            r.right = mViewWidth;
2309cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            p.setColor(mBgColor);
2310e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setStyle(Style.FILL);
2311e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(r, p);
2312cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            // and the area that says All day
2313cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            r.top = DAY_HEADER_HEIGHT;
2314cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            r.bottom = mFirstCell - 1;
2315cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            r.left = 0;
2316cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            r.right = mHoursWidth;
2317cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            canvas.drawRect(r, p);
2318cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan
2319cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            int startIndex = -1;
2320cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan
2321cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            int todayIndex = mTodayJulianDay - mFirstJulianDay;
2322cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            if (todayIndex < 0) {
2323cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                // Future
2324cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                startIndex = 0;
2325cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            } else if (todayIndex >= 1 && todayIndex + 1 < mNumDays) {
2326cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                // Multiday - tomorrow is visible.
2327cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                startIndex = todayIndex + 1;
2328cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            }
2329cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan
2330cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            if (startIndex >= 0) {
2331cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                // Draw the future highlight
2332cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                r.top = 0;
2333cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                r.bottom = mFirstCell - 1;
2334cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                r.left = computeDayLeftPosition(startIndex) + 1;
2335b03348cec4c175846f074a656db5c121d5467e2dMichael Chan                r.right = computeDayLeftPosition(mNumDays);
2336cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                p.setColor(mFutureBgColor);
2337cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                p.setStyle(Style.FILL);
2338cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan                canvas.drawRect(r, p);
2339cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            }
2340e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
234121bd42039c92232647f54461cd20708224cc7eb6Michael Chan
2342b6fb51224b578b26e503fddae2799d03a2aaad47RoboErik        if (mSelectionAllday && mSelectionMode != SELECTION_HIDDEN) {
2343e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            // Draw the selection highlight on the selected all-day area
2344e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.top = DAY_HEADER_HEIGHT + 1;
2345e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.bottom = mRect.top + mAlldayHeight + ALLDAY_TOP_MARGIN - 2;
2346e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            int daynum = mSelectionDay - mFirstJulianDay;
2347e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.left = computeDayLeftPosition(daynum) + 1;
2348e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            mRect.right = computeDayLeftPosition(daynum + 1);
2349e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setColor(mCalendarGridAreaSelected);
2350e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(mRect, p);
2351e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2352e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2353e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) {
2355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw the horizontal day background banner
23563eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // p.setColor(mCalendarDateBannerBackground);
23573eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.top = 0;
23583eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.bottom = DAY_HEADER_HEIGHT;
23593eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.left = 0;
23603eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP);
23613eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // canvas.drawRect(r, p);
23623eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        //
2363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Fill the extra space on the right side with the default background
23643eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.left = r.right;
23653eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // r.right = mViewWidth;
23663eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // p.setColor(mCalendarGridAreaBackground);
23673eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // canvas.drawRect(r, p);
2368e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) {
2369e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            return;
2370146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
23723eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTypeface(mBold);
23733eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTextAlign(Paint.Align.RIGHT);
2374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cell = mFirstJulianDay;
2375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String[] dayNames;
2377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mDateStrWidth < mCellWidth) {
2378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dayNames = mDayStrs;
2379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dayNames = mDayStrs2Letter;
2381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2383d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        p.setAntiAlias(true);
2384146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++, cell++) {
23851f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan            int dayOfWeek = day + mFirstVisibleDayOfWeek;
23861f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan            if (dayOfWeek >= 14) {
23871f711ed63be864c94259a65deef8d5cdf380b9fbMichael Chan                dayOfWeek -= 14;
2388487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan            }
2389487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan
23903eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            int color = mCalendarDateBannerTextColor;
2391071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa            if (mNumDays == 1) {
2392071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                if (dayOfWeek == Time.SATURDAY) {
2393071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_saturdayColor;
2394071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                } else if (dayOfWeek == Time.SUNDAY) {
2395071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_sundayColor;
2396071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                }
2397071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa            } else {
2398071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                final int column = day % 7;
2399071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                if (Utils.isSaturday(column, mFirstDayOfWeek)) {
2400071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_saturdayColor;
2401071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                } else if (Utils.isSunday(column, mFirstDayOfWeek)) {
2402071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                    color = mWeek_sundayColor;
2403071d56d859b0c9f9c7f002e2bee98bc1bd0fc95fDaisuke Miyakawa                }
24043eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            }
24053eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
24063eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setColor(color);
2407e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p);
2408146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
24093eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setTypeface(null);
2410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawAmPm(Canvas canvas, Paint p) {
2413d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        p.setColor(mCalendarAmPmLabel);
2414e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(AMPM_TEXT_SIZE);
2415146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTypeface(mBold);
2416146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
2417e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextAlign(Paint.Align.RIGHT);
2418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String text = mAmString;
2419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour >= 12) {
2420146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            text = mPmString;
2421146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2422146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP;
24238f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
2424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2425146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mFirstHour < 12 && mFirstHour + mNumHours > 12) {
2426146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Also draw the "PM"
2427146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            text = mPmString;
2428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP)
2429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    + 2 * mHoursTextHeight + HOUR_GAP;
24308f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
2431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2433146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2434e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawCurrentTimeLine(Rect r, final int day, final int top, Canvas canvas,
2435f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            Paint p) {
2436e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1;
2437763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1;
24383eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
2439763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.top = top - CURRENT_TIME_LINE_TOP_OFFSET;
24403eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight();
2441bd0e834aea317236d574f8738fe11a51fdc87968Erik
24423eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mCurrentTimeLine.setBounds(r);
24433eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        mCurrentTimeLine.draw(canvas);
24445b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        if (mAnimateToday) {
24455b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mCurrentTimeAnimateLine.setBounds(r);
24465b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha);
24475b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik            mCurrentTimeAnimateLine.draw(canvas);
24485b872523e28fc778a3fc34e7e9096ca1197c6509RoboErik        }
2449bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
2450bd0e834aea317236d574f8738fe11a51fdc87968Erik
2451146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void doDraw(Canvas canvas) {
2452146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint p = mPaint;
2453146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect r = mRect;
2454146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2455cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan        if (mFutureBgColor != 0) {
2456cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan            drawBgColors(r, canvas, p);
2457cba7f1fd22e1d7ee9d8d0828f37bc08512bf6b75Michael Chan        }
2458146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawGridBackground(r, canvas, p);
2459146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        drawHours(r, canvas, p);
2460146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2461146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw each day
2462146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cell = mFirstJulianDay;
2463e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
24640a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        int alpha = p.getAlpha();
24650a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        p.setAlpha(mEventsAlpha);
2466146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int day = 0; day < mNumDays; day++, cell++) {
246708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // TODO Wow, this needs cleanup. drawEvents loop through all the
246808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // events on every call.
2469e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            drawEvents(cell, day, HOUR_GAP, canvas, p);
247046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // If this is today
247146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            if (cell == mTodayJulianDay) {
24721ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
24731ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                        + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
24741ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
247546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // And the current time shows up somewhere on the screen
247646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) {
2477e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    drawCurrentTimeLine(r, day, lineY, canvas, p);
2478bd0e834aea317236d574f8738fe11a51fdc87968Erik                }
2479bd0e834aea317236d574f8738fe11a51fdc87968Erik            }
2480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2481e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
24820a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        p.setAlpha(alpha);
2483146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
248495f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        drawSelectedRect(r, canvas, p);
248595f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    }
24863eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
248795f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private void drawSelectedRect(Rect r, Canvas canvas, Paint p) {
2488146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Draw a highlight on the selected hour (if needed)
24892fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionMode != SELECTION_HIDDEN && !mSelectionAllday) {
24903937a47bc5a1afb9701e1d0565fae31f5800590dErik            int daynum = mSelectionDay - mFirstJulianDay;
2491ba902466b19b11d587468e354edc58e542707747Michael Chan            r.top = mSelectionHour * (mCellHeight + HOUR_GAP);
24921ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top + mCellHeight + HOUR_GAP;
2493d0c06c6c8815d027c595cb70b01478634a84c8e0RoboErik            r.left = computeDayLeftPosition(daynum) + 1;
2494d0c06c6c8815d027c595cb70b01478634a84c8e0RoboErik            r.right = computeDayLeftPosition(daynum + 1) + 1;
2495ba902466b19b11d587468e354edc58e542707747Michael Chan
2496ba902466b19b11d587468e354edc58e542707747Michael Chan            saveSelectionPosition(r.left, r.top, r.right, r.bottom);
24973937a47bc5a1afb9701e1d0565fae31f5800590dErik
249895f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            // Draw the highlight on the grid
24993937a47bc5a1afb9701e1d0565fae31f5800590dErik            p.setColor(mCalendarGridAreaSelected);
25003937a47bc5a1afb9701e1d0565fae31f5800590dErik            r.top += HOUR_GAP;
25013937a47bc5a1afb9701e1d0565fae31f5800590dErik            r.right -= DAY_GAP;
2502e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setAntiAlias(false);
25033937a47bc5a1afb9701e1d0565fae31f5800590dErik            canvas.drawRect(r, p);
250495f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan
2505ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            // Draw a "new event hint" on top of the highlight
2506ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            // For the week view, show a "+", for day view, show "+ New event"
2507ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            p.setColor(mNewEventHintColor);
2508ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            if (mNumDays > 1) {
2509ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setStrokeWidth(NEW_EVENT_WIDTH);
2510ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int width = r.right - r.left;
2511ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int midX = r.left + width / 2;
2512ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int midY = r.top + mCellHeight / 2;
2513ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int length = Math.min(mCellHeight, width) - NEW_EVENT_MARGIN * 2;
2514ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                length = Math.min(length, NEW_EVENT_MAX_LENGTH);
2515ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int verticalPadding = (mCellHeight - length) / 2;
2516ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                int horizontalPadding = (width - length) / 2;
2517ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                canvas.drawLine(r.left + horizontalPadding, midY, r.right - horizontalPadding,
2518ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                        midY, p);
2519ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                canvas.drawLine(midX, r.top + verticalPadding, midX, r.bottom - verticalPadding, p);
2520ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            } else {
2521ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setStyle(Paint.Style.FILL);
2522ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setTextSize(NEW_EVENT_HINT_FONT_SIZE);
2523ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setTextAlign(Paint.Align.LEFT);
2524ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                p.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
2525ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                canvas.drawText(mNewEventHintString, r.left + EVENT_TEXT_LEFT_MARGIN,
2526ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson                        r.top + Math.abs(p.getFontMetrics().ascent) + EVENT_TEXT_TOP_MARGIN , p);
2527ba25b7c5031eff91d0bd21d0eb9b581db4f52b33Isaac Katzenelson            }
2528ba902466b19b11d587468e354edc58e542707747Michael Chan        }
252995f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    }
2530146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
253195f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan    private void drawHours(Rect r, Canvas canvas, Paint p) {
25328f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        setupHourTextPaint(p);
2533146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
25348f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        int y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN;
2535146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2536146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < 24; i++) {
2537146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            String time = mHourStrs[i];
25388f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            canvas.drawText(time, HOURS_LEFT_MARGIN, y, p);
2539146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            y += mCellHeight + HOUR_GAP;
2540146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2541146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2542146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
25438f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    private void setupHourTextPaint(Paint p) {
25448f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setColor(mCalendarHourLabelColor);
2545e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextSize(HOURS_TEXT_SIZE);
2546f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan        p.setTypeface(Typeface.DEFAULT);
2547e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setTextAlign(Paint.Align.RIGHT);
25488f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        p.setAntiAlias(true);
25498f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan    }
25508f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan
2551e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawDayHeader(String dayStr, int day, int cell, Canvas canvas, Paint p) {
2552487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan        int dateNum = mFirstVisibleDate + day;
2553e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int x;
2554146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (dateNum > mMonthLength) {
2555146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            dateNum -= mMonthLength;
2556146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2557e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2558146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2559763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        int todayIndex = mTodayJulianDay - mFirstJulianDay;
25603eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw day of the month
25613eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        String dateNumStr = String.valueOf(dateNum);
25623eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        if (mNumDays > 1) {
25633eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN;
25643eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
25653eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the month
2566e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN;
2567e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setTextAlign(Align.RIGHT);
25683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DATE_HEADER_FONT_SIZE);
2569763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik
2570763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik            p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT);
25713eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dateNumStr, x, y, p);
25723eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
25733eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the week
2574e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x -= p.measureText(" " + dateNumStr);
25753eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DAY_HEADER_FONT_SIZE);
2576f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(Typeface.DEFAULT);
25773eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dayStr, x, y, p);
2578146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
2579e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            float y = ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN;
2580e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setTextAlign(Align.LEFT);
2581146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2582f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan
25833eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the week
2584e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN;
25853eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DAY_HEADER_FONT_SIZE);
2586f1495b68983082cb7d1cd625abefcd87dd58cf5fMichael Chan            p.setTypeface(Typeface.DEFAULT);
25873eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dayStr, x, y, p);
2588d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan
25893eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            // Draw day of the month
25903eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN;
25913eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            p.setTextSize(DATE_HEADER_FONT_SIZE);
2592763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik            p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT);
25933eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            canvas.drawText(dateNumStr, x, y, p);
2594d06a1bf1cf81b818b3e3dc0f81b7cb07382425f2Michael Chan        }
2595146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2596146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2597146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void drawGridBackground(Rect r, Canvas canvas, Paint p) {
2598146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint.Style savedStyle = p.getStyle();
2599146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2600b03348cec4c175846f074a656db5c121d5467e2dMichael Chan        final float stopX = computeDayLeftPosition(mNumDays);
2601000ba1109e2f4e527e9227fe341598ceeb3fbca6Michael Chan        float y = 0;
26023eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float deltaY = mCellHeight + HOUR_GAP;
260302041687c35441e8a21cb763f1fc3a26525292efDavid Brown        int linesIndex = 0;
26043eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float startY = 0;
26053eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP);
26061ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        float x = mHoursWidth;
2607146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
26083eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner horizontal grid lines
26093eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setColor(mCalendarGridLineInnerHorizontalColor);
26103eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
2611e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
26123eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        y = 0;
261302041687c35441e8a21cb763f1fc3a26525292efDavid Brown        linesIndex = 0;
26143eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        for (int hour = 0; hour <= 24; hour++) {
26158f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan            mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
261602041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = y;
261702041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopX;
261802041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = y;
26193eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            y += deltaY;
26203eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
262102041687c35441e8a21cb763f1fc3a26525292efDavid Brown        if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) {
262202041687c35441e8a21cb763f1fc3a26525292efDavid Brown            canvas.drawLines(mLines, 0, linesIndex, p);
262302041687c35441e8a21cb763f1fc3a26525292efDavid Brown            linesIndex = 0;
262402041687c35441e8a21cb763f1fc3a26525292efDavid Brown            p.setColor(mCalendarGridLineInnerVerticalColor);
262502041687c35441e8a21cb763f1fc3a26525292efDavid Brown        }
26263eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
26273eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner vertical grid lines
2628e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        for (int day = 0; day <= mNumDays; day++) {
2629e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day);
263002041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
263102041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = startY;
263202041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
263302041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopY;
26343eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
263502041687c35441e8a21cb763f1fc3a26525292efDavid Brown        canvas.drawLines(mLines, 0, linesIndex, p);
26363eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
2637146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Restore the saved style.
2638146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setStyle(savedStyle);
2639146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setAntiAlias(true);
2640146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2641146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2642e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    /**
2643e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param r
2644e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param canvas
2645e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     * @param p
2646e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik     */
2647e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawBgColors(Rect r, Canvas canvas, Paint p) {
2648e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int todayIndex = mTodayJulianDay - mFirstJulianDay;
2649e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        // Draw the hours background color
2650e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.top = mDestRect.top;
2651e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.bottom = mDestRect.bottom;
2652e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.left = 0;
2653e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        r.right = mHoursWidth;
2654e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mBgColor);
2655e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStyle(Style.FILL);
2656e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
2657e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        canvas.drawRect(r, p);
265821bd42039c92232647f54461cd20708224cc7eb6Michael Chan
265921bd42039c92232647f54461cd20708224cc7eb6Michael Chan        // Draw background for grid area
2660e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        if (mNumDays == 1 && todayIndex == 0) {
2661e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            // Draw a white background for the time later than current time
2662e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
2663e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                    + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
2664e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (lineY < mViewStartY + mViewHeight) {
2665e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                lineY = Math.max(lineY, mViewStartY);
2666e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.left = mHoursWidth;
2667e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.right = mViewWidth;
2668e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.top = lineY;
2669e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                r.bottom = mViewStartY + mViewHeight;
267021bd42039c92232647f54461cd20708224cc7eb6Michael Chan                p.setColor(mFutureBgColor);
2671e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                canvas.drawRect(r, p);
2672e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
2673e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        } else if (todayIndex >= 0 && todayIndex < mNumDays) {
267421bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Draw today with a white background for the time later than current time
267521bd42039c92232647f54461cd20708224cc7eb6Michael Chan            int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
267621bd42039c92232647f54461cd20708224cc7eb6Michael Chan                    + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
267721bd42039c92232647f54461cd20708224cc7eb6Michael Chan            if (lineY < mViewStartY + mViewHeight) {
267821bd42039c92232647f54461cd20708224cc7eb6Michael Chan                lineY = Math.max(lineY, mViewStartY);
267921bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.left = computeDayLeftPosition(todayIndex) + 1;
268021bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.right = computeDayLeftPosition(todayIndex + 1);
268121bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.top = lineY;
268221bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.bottom = mViewStartY + mViewHeight;
268321bd42039c92232647f54461cd20708224cc7eb6Michael Chan                p.setColor(mFutureBgColor);
268421bd42039c92232647f54461cd20708224cc7eb6Michael Chan                canvas.drawRect(r, p);
268521bd42039c92232647f54461cd20708224cc7eb6Michael Chan            }
268621bd42039c92232647f54461cd20708224cc7eb6Michael Chan
268721bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Paint Tomorrow and later days with future color
268821bd42039c92232647f54461cd20708224cc7eb6Michael Chan            if (todayIndex + 1 < mNumDays) {
268921bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.left = computeDayLeftPosition(todayIndex + 1) + 1;
2690b03348cec4c175846f074a656db5c121d5467e2dMichael Chan                r.right = computeDayLeftPosition(mNumDays);
269121bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.top = mDestRect.top;
269221bd42039c92232647f54461cd20708224cc7eb6Michael Chan                r.bottom = mDestRect.bottom;
269321bd42039c92232647f54461cd20708224cc7eb6Michael Chan                p.setColor(mFutureBgColor);
269421bd42039c92232647f54461cd20708224cc7eb6Michael Chan                canvas.drawRect(r, p);
269521bd42039c92232647f54461cd20708224cc7eb6Michael Chan            }
269621bd42039c92232647f54461cd20708224cc7eb6Michael Chan        } else if (todayIndex < 0) {
269721bd42039c92232647f54461cd20708224cc7eb6Michael Chan            // Future
269821bd42039c92232647f54461cd20708224cc7eb6Michael Chan            r.left = computeDayLeftPosition(0) + 1;
2699b03348cec4c175846f074a656db5c121d5467e2dMichael Chan            r.right = computeDayLeftPosition(mNumDays);
2700e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.top = mDestRect.top;
2701e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            r.bottom = mDestRect.bottom;
270221bd42039c92232647f54461cd20708224cc7eb6Michael Chan            p.setColor(mFutureBgColor);
2703e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            canvas.drawRect(r, p);
2704e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        }
2705e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
2706e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    }
2707e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
2708146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Event getSelectedEvent() {
2709146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == null) {
2710146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // There is no event at the selected hour, so create a new event.
2711146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return getNewEvent(mSelectionDay, getSelectedTimeInMillis(),
2712146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    getSelectedMinutesSinceMidnight());
2713146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2714146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return mSelectedEvent;
2715146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2716146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2717146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    boolean isEventSelected() {
2718146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return (mSelectedEvent != null);
2719146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2720146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2721146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    Event getNewEvent() {
2722146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return getNewEvent(mSelectionDay, getSelectedTimeInMillis(),
2723146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                getSelectedMinutesSinceMidnight());
2724146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2725146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2726146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    static Event getNewEvent(int julianDay, long utcMillis,
2727146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int minutesSinceMidnight) {
2728146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event event = Event.newInstance();
2729146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startDay = julianDay;
2730146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endDay = julianDay;
2731146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startMillis = utcMillis;
2732146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endMillis = event.startMillis + MILLIS_PER_HOUR;
2733146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.startTime = minutesSinceMidnight;
2734146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        event.endTime = event.startTime + MINUTES_PER_HOUR;
2735146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return event;
2736146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2737146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2738146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) {
2739146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        float maxWidthF = 0.0f;
2740146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2741146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = strings.length;
2742146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < len; i++) {
2743146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            float width = p.measureText(strings[i]);
2744146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            maxWidthF = Math.max(width, maxWidthF);
2745146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2746146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int maxWidth = (int) (maxWidthF + 0.5);
2747146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (maxWidth < currentMax) {
2748146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            maxWidth = currentMax;
2749146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2750146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return maxWidth;
2751146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2752146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2753146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void saveSelectionPosition(float left, float top, float right, float bottom) {
2754146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.left = (int) left;
2755146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.right = (int) right;
2756146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.top = (int) top;
2757146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPrevBox.bottom = (int) bottom;
2758146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2759146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2760146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private Rect getCurrentSelectionPosition() {
2761146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect box = new Rect();
2762146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        box.top = mSelectionHour * (mCellHeight + HOUR_GAP);
2763146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        box.bottom = box.top + mCellHeight + HOUR_GAP;
2764146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int daynum = mSelectionDay - mFirstJulianDay;
2765e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        box.left = computeDayLeftPosition(daynum) + 1;
2766e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        box.right = computeDayLeftPosition(daynum + 1);
2767146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return box;
2768146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2769146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
27701ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void setupTextRect(Rect r) {
27711ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom <= r.top || r.right <= r.left) {
27721ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top;
27731ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right = r.left;
277436a4668f603c432d08ad156cd7c731f9838c0212Michael Chan            return;
277536a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
277636a4668f603c432d08ad156cd7c731f9838c0212Michael Chan
27771ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) {
27781ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.top += EVENT_TEXT_TOP_MARGIN;
27791ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom -= EVENT_TEXT_BOTTOM_MARGIN;
278036a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
27811ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) {
27821ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.left += EVENT_TEXT_LEFT_MARGIN;
27831ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right -= EVENT_TEXT_RIGHT_MARGIN;
278436a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
27859c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    }
27869c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan
27871ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void setupAllDayTextRect(Rect r) {
27881ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom <= r.top || r.right <= r.left) {
27891ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom = r.top;
27901ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right = r.left;
279136a4668f603c432d08ad156cd7c731f9838c0212Michael Chan            return;
279236a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
279336a4668f603c432d08ad156cd7c731f9838c0212Michael Chan
27941ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) {
27951ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN;
27961ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN;
279736a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
27981ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) {
27991ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
28001ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN;
280136a4668f603c432d08ad156cd7c731f9838c0212Michael Chan        }
28029c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan    }
28039c1f7b6c194a42a39bacdaa25e887dc5f12ec478Michael Chan
28043e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh    /**
28053e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh     * Return the layout for a numbered event. Create it if not already existing
28063e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh     */
280708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint,
280808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            Rect r) {
280908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        if (i < 0 || i >= layouts.length) {
28100a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan            return null;
28110a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan        }
28120a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan
281308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        StaticLayout layout = layouts[i];
2814f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        // Check if we have already initialized the StaticLayout and that
2815f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        // the width hasn't changed (due to vertical resizing which causes
2816f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        // re-layout of events at min height)
28171ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        if (layout == null || r.width() != layout.getWidth()) {
2818e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            SpannableStringBuilder bob = new SpannableStringBuilder();
2819e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (event.title != null) {
28201c1987163f49158d6ea54f034c874ec3d7b5cde1RoboErik                // MAX - 1 since we add a space
28211c1987163f49158d6ea54f034c874ec3d7b5cde1RoboErik                bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN - 1));
2822e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0);
2823e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(' ');
2824e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
2825e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            if (event.location != null) {
2826e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                bob.append(drawTextSanitizer(event.location.toString(),
2827e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                        MAX_EVENT_TEXT_LEN - bob.length()));
2828e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            }
28293e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
283025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            switch (event.selfAttendeeStatus) {
283125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_INVITED:
283225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    paint.setColor(event.color);
283325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    break;
283425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_DECLINED:
2835e6109c584d27a6c25df1819a5c6ec444e789ce61Isaac Katzenelson                    paint.setColor(mEventTextColor);
28364ecf0648e68bc9339a3d64745008a4617f7beddaIsaac Katzenelson                    paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA);
2837e6109c584d27a6c25df1819a5c6ec444e789ce61Isaac Katzenelson                    break;
283825996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_NONE: // Your own events
283925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_ACCEPTED:
284025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                case Attendees.ATTENDEE_STATUS_TENTATIVE:
284125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                default:
284225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    paint.setColor(mEventTextColor);
284325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    break;
2844f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            }
2845f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan
28463e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh            // Leave a one pixel boundary on the left and right of the rectangle for the event
2847e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            layout = new StaticLayout(bob, 0, bob.length(), new TextPaint(paint), r.width(),
28481ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                    Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width());
28493e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
285008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            layouts[i] = layout;
28513e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        }
28520a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        layout.getPaint().setAlpha(mEventsAlpha);
28533e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        return layout;
28543e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh    }
28553e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
28561ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) {
28573e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh
2858146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTextSize(NORMAL_FONT_SIZE);
2859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setTextAlign(Paint.Align.LEFT);
2860146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint eventTextPaint = mEventTextPaint;
2861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
28623eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        final float startY = DAY_HEADER_HEIGHT;
28632fda24534978f55711526dbde747aa50d68dec40RoboErik        final float stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN;
2864e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        float x = 0;
286502041687c35441e8a21cb763f1fc3a26525292efDavid Brown        int linesIndex = 0;
28663eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan
28673eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Draw the inner vertical grid lines
28683eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setColor(mCalendarGridLineInnerVerticalColor);
28691ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        x = mHoursWidth;
28703eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
28711ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        // Line bounding the top of the all day area
28728f7a2b2fb82029bc23fce357d14f4484e80c13fdMichael Chan        mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
28731ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = startY;
2874b03348cec4c175846f074a656db5c121d5467e2dMichael Chan        mLines[linesIndex++] = computeDayLeftPosition(mNumDays);
28751ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        mLines[linesIndex++] = startY;
28761ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
2877e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        for (int day = 0; day <= mNumDays; day++) {
2878e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            x = computeDayLeftPosition(day);
287902041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
288002041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = startY;
288102041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = x;
288202041687c35441e8a21cb763f1fc3a26525292efDavid Brown            mLines[linesIndex++] = stopY;
28833eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        }
2884e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
288502041687c35441e8a21cb763f1fc3a26525292efDavid Brown        canvas.drawLines(mLines, 0, linesIndex, p);
2886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        p.setStyle(Style.FILL);
2887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
28883eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
2889146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int lastDay = firstDay + numDays - 1;
289008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        final ArrayList<Event> events = mAllDayEvents;
2891146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
28922fda24534978f55711526dbde747aa50d68dec40RoboErik        // Whether or not we should draw the more events text
28932fda24534978f55711526dbde747aa50d68dec40RoboErik        boolean hasMoreEvents = false;
28942fda24534978f55711526dbde747aa50d68dec40RoboErik        // size of the allDay area
28952fda24534978f55711526dbde747aa50d68dec40RoboErik        float drawHeight = mAlldayHeight;
28962fda24534978f55711526dbde747aa50d68dec40RoboErik        // max number of events being drawn in one day of the allday area
28972fda24534978f55711526dbde747aa50d68dec40RoboErik        float numRectangles = mMaxAlldayEvents;
28982fda24534978f55711526dbde747aa50d68dec40RoboErik        // Where to cut off drawn allday events
28992fda24534978f55711526dbde747aa50d68dec40RoboErik        int allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN;
29002fda24534978f55711526dbde747aa50d68dec40RoboErik        // The number of events that weren't drawn in each day
29012fda24534978f55711526dbde747aa50d68dec40RoboErik        mSkippedAlldayEvents = new int[numDays];
29022fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents &&
29032fda24534978f55711526dbde747aa50d68dec40RoboErik                mAnimateDayHeight == 0) {
29042fda24534978f55711526dbde747aa50d68dec40RoboErik            // We draw one fewer event than will fit so that more events text
29052fda24534978f55711526dbde747aa50d68dec40RoboErik            // can be drawn
29062fda24534978f55711526dbde747aa50d68dec40RoboErik            numRectangles = mMaxUnexpandedAlldayEventCount - 1;
29072fda24534978f55711526dbde747aa50d68dec40RoboErik            // We also clip the events above the more events text
29082fda24534978f55711526dbde747aa50d68dec40RoboErik            allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
29092fda24534978f55711526dbde747aa50d68dec40RoboErik            hasMoreEvents = true;
29102fda24534978f55711526dbde747aa50d68dec40RoboErik        } else if (mAnimateDayHeight != 0) {
29112fda24534978f55711526dbde747aa50d68dec40RoboErik            // clip at the end of the animating space
29122fda24534978f55711526dbde747aa50d68dec40RoboErik            allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN;
29132fda24534978f55711526dbde747aa50d68dec40RoboErik        }
29140a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson
29150a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        int alpha = eventTextPaint.getAlpha();
29160a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        eventTextPaint.setAlpha(mEventsAlpha);
2917146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
2918146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
2919146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startDay = event.startDay;
2920146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endDay = event.endDay;
2921c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (startDay > lastDay || endDay < firstDay) {
2922146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
2923c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2924c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (startDay < firstDay) {
2925146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startDay = firstDay;
2926c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2927c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            if (endDay > lastDay) {
2928146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endDay = lastDay;
2929c514df21767542285fe6cf333b148fff079a30f2Daisuke Miyakawa            }
2930146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startIndex = startDay - firstDay;
2931146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endIndex = endDay - firstDay;
29322fda24534978f55711526dbde747aa50d68dec40RoboErik            float height = mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount ? mAnimateDayEventHeight :
29332fda24534978f55711526dbde747aa50d68dec40RoboErik                    drawHeight / numRectangles;
2934146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2935146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Prevent a single event from getting too big
2936f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
2937f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
2938146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2940146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Leave a one-pixel space between the vertical day lines and the
2941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // event rectangle.
2942e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            event.left = computeDayLeftPosition(startIndex);
2943e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP;
2944146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            event.top = y + height * event.getColumn();
294525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN;
29462fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
29472fda24534978f55711526dbde747aa50d68dec40RoboErik                // check if we should skip this event. We skip if it starts
29482fda24534978f55711526dbde747aa50d68dec40RoboErik                // after the clip bound or ends after the skip bound and we're
29492fda24534978f55711526dbde747aa50d68dec40RoboErik                // not animating.
29502fda24534978f55711526dbde747aa50d68dec40RoboErik                if (event.top >= allDayEventClip) {
29512fda24534978f55711526dbde747aa50d68dec40RoboErik                    incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
29522fda24534978f55711526dbde747aa50d68dec40RoboErik                    continue;
29532fda24534978f55711526dbde747aa50d68dec40RoboErik                } else if (event.bottom > allDayEventClip) {
29542fda24534978f55711526dbde747aa50d68dec40RoboErik                    if (hasMoreEvents) {
29552fda24534978f55711526dbde747aa50d68dec40RoboErik                        incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
29562fda24534978f55711526dbde747aa50d68dec40RoboErik                        continue;
29572fda24534978f55711526dbde747aa50d68dec40RoboErik                    }
29582fda24534978f55711526dbde747aa50d68dec40RoboErik                    event.bottom = allDayEventClip;
29592fda24534978f55711526dbde747aa50d68dec40RoboErik                }
29602fda24534978f55711526dbde747aa50d68dec40RoboErik            }
296125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            Rect r = drawEventRect(event, canvas, p, eventTextPaint, (int) event.top,
296225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                    (int) event.bottom);
29631ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            setupAllDayTextRect(r);
296408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r);
29656ab3a2c7de3719a4806d6c9cd5b53cb34a5bb017Mindy Pereira            drawEventText(layout, r, canvas, r.top, r.bottom, true);
2966146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2967146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Check if this all-day event intersects the selected day
29682fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mSelectionAllday && mComputeSelectedEvents) {
2969146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (startDay <= mSelectionDay && endDay >= mSelectionDay) {
2970146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mSelectedEvents.add(event);
2971146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
2972146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
2973146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
29740a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        eventTextPaint.setAlpha(alpha);
29750a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson
29762fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) {
29772fda24534978f55711526dbde747aa50d68dec40RoboErik            // If the more allday text should be visible, draw it.
29780a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson            alpha = p.getAlpha();
29790a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson            p.setAlpha(mEventsAlpha);
29802fda24534978f55711526dbde747aa50d68dec40RoboErik            p.setColor(mMoreAlldayEventsTextAlpha << 24 & mMoreEventsTextColor);
29812fda24534978f55711526dbde747aa50d68dec40RoboErik            for (int i = 0; i < mSkippedAlldayEvents.length; i++) {
29822fda24534978f55711526dbde747aa50d68dec40RoboErik                if (mSkippedAlldayEvents[i] > 0) {
29832fda24534978f55711526dbde747aa50d68dec40RoboErik                    drawMoreAlldayEvents(canvas, mSkippedAlldayEvents[i], i, p);
29842fda24534978f55711526dbde747aa50d68dec40RoboErik                }
29852fda24534978f55711526dbde747aa50d68dec40RoboErik            }
29860a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson            p.setAlpha(alpha);
29872fda24534978f55711526dbde747aa50d68dec40RoboErik        }
2988146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
29892fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionAllday) {
2990146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Compute the neighbors for the list of all-day events that
2991146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // intersect the selected day.
2992146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeAllDayNeighbors();
2993146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
2994146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Set the selection position to zero so that when we move down
2995146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // to the normal event area, we will highlight the topmost event.
2996146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            saveSelectionPosition(0f, 0f, 0f, 0f);
2997146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
2998146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
2999146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
30002fda24534978f55711526dbde747aa50d68dec40RoboErik    // Helper method for counting the number of allday events skipped on each day
30012fda24534978f55711526dbde747aa50d68dec40RoboErik    private void incrementSkipCount(int[] counts, int startIndex, int endIndex) {
30022fda24534978f55711526dbde747aa50d68dec40RoboErik        if (counts == null || startIndex < 0 || endIndex > counts.length) {
30032fda24534978f55711526dbde747aa50d68dec40RoboErik            return;
30042fda24534978f55711526dbde747aa50d68dec40RoboErik        }
30052fda24534978f55711526dbde747aa50d68dec40RoboErik        for (int i = startIndex; i <= endIndex; i++) {
30062fda24534978f55711526dbde747aa50d68dec40RoboErik            counts[i]++;
30072fda24534978f55711526dbde747aa50d68dec40RoboErik        }
30082fda24534978f55711526dbde747aa50d68dec40RoboErik    }
30092fda24534978f55711526dbde747aa50d68dec40RoboErik
30102fda24534978f55711526dbde747aa50d68dec40RoboErik    // Draws the "box +n" text for hidden allday events
30112fda24534978f55711526dbde747aa50d68dec40RoboErik    protected void drawMoreAlldayEvents(Canvas canvas, int remainingEvents, int day, Paint p) {
3012e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
3013e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int y = (int) (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - .5f
3014e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik                * EVENT_SQUARE_WIDTH + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN);
3015763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        Rect r = mRect;
3016763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.top = y;
3017763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.left = x;
3018763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.bottom = y + EVENT_SQUARE_WIDTH;
3019763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        r.right = x + EVENT_SQUARE_WIDTH;
3020e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setColor(mMoreEventsTextColor);
3021e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
3022763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        p.setStyle(Style.STROKE);
3023e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
3024763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        canvas.drawRect(r, p);
3025e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(true);
3026763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        p.setStyle(Style.FILL);
3027763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        p.setTextSize(EVENT_TEXT_FONT_SIZE);
30282fda24534978f55711526dbde747aa50d68dec40RoboErik        String text = mResources.getQuantityString(R.plurals.month_more_events, remainingEvents);
30292fda24534978f55711526dbde747aa50d68dec40RoboErik        y += EVENT_SQUARE_WIDTH;
30302fda24534978f55711526dbde747aa50d68dec40RoboErik        x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING;
30312fda24534978f55711526dbde747aa50d68dec40RoboErik        canvas.drawText(String.format(text, remainingEvents), x, y, p);
30322fda24534978f55711526dbde747aa50d68dec40RoboErik    }
30332fda24534978f55711526dbde747aa50d68dec40RoboErik
3034146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeAllDayNeighbors() {
3035146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = mSelectedEvents.size();
3036146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (len == 0 || mSelectedEvent != null) {
3037146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3038146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3039146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3040146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear all the links
3041146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
3042146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
3043146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = null;
3044146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = null;
3045146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = null;
3046146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = null;
3047146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3048146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3049146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // For each event in the selected event list "mSelectedEvents", find
305046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        // its neighbors in the up and down directions. This could be done
3051146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // more efficiently by sorting on the Event.getColumn() field, but
3052146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // the list is expected to be very small.
3053146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3054146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Find the event in the same row as the previously selected all-day
3055146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // event, if any.
3056146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int startPosition = -1;
305768655b5af2b7e5f872a103858ec58a19042123ceRoboErik        if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) {
3058146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            startPosition = mPrevSelectedEvent.getColumn();
3059146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3060146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int maxPosition = -1;
3061146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event startEvent = null;
3062146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event maxPositionEvent = null;
3063146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
3064146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
3065146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int position = ev.getColumn();
3066146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (position == startPosition) {
3067146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEvent = ev;
3068146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (position > maxPosition) {
3069146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                maxPositionEvent = ev;
3070146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                maxPosition = position;
3071146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3072146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int jj = 0; jj < len; jj++) {
3073146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (jj == ii) {
3074146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
3075146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3076146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event neighbor = mSelectedEvents.get(jj);
3077146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborPosition = neighbor.getColumn();
3078146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighborPosition == position - 1) {
3079146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    ev.nextUp = neighbor;
3080146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighborPosition == position + 1) {
3081146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    ev.nextDown = neighbor;
3082146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3083146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3084146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3085146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (startEvent != null) {
3086989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedEvent(startEvent);
3087146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3088989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedEvent(maxPositionEvent);
3089146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3090146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3092e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik    private void drawEvents(int date, int dayIndex, int top, Canvas canvas, Paint p) {
3093146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Paint eventTextPaint = mEventTextPaint;
3094763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        int left = computeDayLeftPosition(dayIndex) + 1;
3095763b9308fa13c8c9c3b9a715f4daf8eba497b935RoboErik        int cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1;
3096146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cellHeight = mCellHeight;
3097146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3098146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Use the selected hour as the selection region
3099259b8f9a21fffd3858218581e9daaf689b6b888fErik        Rect selectionArea = mSelectionRect;
3100146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP);
3101146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.bottom = selectionArea.top + cellHeight;
3102146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.left = left;
3103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        selectionArea.right = selectionArea.left + cellWidth;
3104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
310508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        final ArrayList<Event> events = mEvents;
3106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
3107146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        EventGeometry geometry = mEventGeometry;
3108146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
31092fda24534978f55711526dbde747aa50d68dec40RoboErik        final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight;
31100a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson
31110a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        int alpha = eventTextPaint.getAlpha();
31120a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        eventTextPaint.setAlpha(mEventsAlpha);
3113146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
3114146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
3115146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
3116146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
3117146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3118146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
311908ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // Don't draw it if it is not visible
312008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            if (event.bottom < mViewStartY || event.top > viewEndY) {
312108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                continue;
312208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            }
312308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
31242fda24534978f55711526dbde747aa50d68dec40RoboErik            if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents
3125146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    && geometry.eventIntersectsSelection(event, selectionArea)) {
3126146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectedEvents.add(event);
3127146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3128146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
312925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            Rect r = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY);
31301ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            setupTextRect(r);
313108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
313208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // Don't draw text if it is not visible
313308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            if (r.top > viewEndY || r.bottom < mViewStartY) {
313408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                continue;
313508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            }
313608ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r);
313708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            // TODO: not sure why we are 4 pixels off
313846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            drawEventText(layout, r, canvas, mViewStartY + 4, mViewStartY + mViewHeight
31396ab3a2c7de3719a4806d6c9cd5b53cb34a5bb017Mindy Pereira                    - DAY_HEADER_HEIGHT - mAlldayHeight, false);
3140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
31410a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        eventTextPaint.setAlpha(alpha);
3142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
31432fda24534978f55711526dbde747aa50d68dec40RoboErik        if (date == mSelectionDay && !mSelectionAllday && isFocused()
3144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                && mSelectionMode != SELECTION_HIDDEN) {
3145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeNeighbors();
3146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3147146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Computes the "nearest" neighbor event in four directions (left, right,
3150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // up, down) for each of the events in the mSelectedEvents array.
3151146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void computeNeighbors() {
3152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int len = mSelectedEvents.size();
3153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (len == 0 || mSelectedEvent != null) {
3154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3156146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // First, clear all the links
3158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
3159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
3160146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = null;
3161146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = null;
3162146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = null;
3163146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = null;
3164146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3165146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3166146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event startEvent = mSelectedEvents.get(0);
316746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int startEventDistance1 = 100000; // any large number
316846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        int startEventDistance2 = 100000; // any large number
3169146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevLocation = FROM_NONE;
3170146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevTop;
3171146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevBottom;
3172146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevLeft;
3173146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevRight;
3174146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int prevCenter = 0;
3175146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect box = getCurrentSelectionPosition();
3176146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mPrevSelectedEvent != null) {
3177146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevTop = (int) mPrevSelectedEvent.top;
3178146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevBottom = (int) mPrevSelectedEvent.bottom;
3179146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLeft = (int) mPrevSelectedEvent.left;
3180146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevRight = (int) mPrevSelectedEvent.right;
3181146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Check if the previously selected event intersects the previous
318246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // selection box. (The previously selected event may be from a
3183146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // much older selection box.)
3184146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (prevTop >= mPrevBox.bottom || prevBottom <= mPrevBox.top
3185146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || prevRight <= mPrevBox.left || prevLeft >= mPrevBox.right) {
3186146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = null;
3187146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevTop = mPrevBox.top;
3188146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevBottom = mPrevBox.bottom;
3189146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevLeft = mPrevBox.left;
3190146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                prevRight = mPrevBox.right;
3191146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
3192146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Clip the top and bottom to the previous selection box.
3193146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (prevTop < mPrevBox.top) {
3194146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    prevTop = mPrevBox.top;
3195146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3196146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (prevBottom > mPrevBox.bottom) {
3197146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    prevBottom = mPrevBox.bottom;
3198146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3199146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3200146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3201146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Just use the previously drawn selection box
3202146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevTop = mPrevBox.top;
3203146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevBottom = mPrevBox.bottom;
3204146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLeft = mPrevBox.left;
3205146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevRight = mPrevBox.right;
3206146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3207146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3208146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Figure out where we came from and compute the center of that area.
3209146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (prevLeft >= box.right) {
3210146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was to the right of us.
3211146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_RIGHT;
3212146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevTop + prevBottom) / 2;
3213146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevRight <= box.left) {
3214146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was to the left of us.
3215146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_LEFT;
3216146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevTop + prevBottom) / 2;
3217146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevBottom <= box.top) {
3218146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was above us.
3219146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_ABOVE;
3220146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevLeft + prevRight) / 2;
3221146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if (prevTop >= box.bottom) {
3222146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // The previously selected event was below us.
3223146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevLocation = FROM_BELOW;
3224146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            prevCenter = (prevLeft + prevRight) / 2;
3225146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3226146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3227146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // For each event in the selected event list "mSelectedEvents", search
3228146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // all the other events in that list for the nearest neighbor in 4
3229146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // directions.
3230146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int ii = 0; ii < len; ii++) {
3231146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event ev = mSelectedEvents.get(ii);
3232146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startTime = ev.startTime;
3234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endTime = ev.endTime;
3235146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int left = (int) ev.left;
3236146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int right = (int) ev.right;
3237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int top = (int) ev.top;
3238146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (top < box.top) {
3239146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                top = box.top;
3240146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3241146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int bottom = (int) ev.bottom;
3242146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (bottom > box.bottom) {
3243146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                bottom = box.bottom;
3244146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
324521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//            if (false) {
324621bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL
324721bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                        | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
324821bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                if (DateFormat.is24HourFormat(mContext)) {
324921bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                    flags |= DateUtils.FORMAT_24HOUR;
325021bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                }
325121bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                String timeRange = DateUtils.formatDateRange(mContext, ev.startMillis,
325221bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                        ev.endMillis, flags);
325321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                Log.i("Cal", "left: " + left + " right: " + right + " top: " + top + " bottom: "
325421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//                        + bottom + " ev: " + timeRange + " " + ev.title);
325521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//            }
325646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int upDistanceMin = 10000; // any large number
325746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int downDistanceMin = 10000; // any large number
325846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int leftDistanceMin = 10000; // any large number
325946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            int rightDistanceMin = 10000; // any large number
3260146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event upEvent = null;
3261146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event downEvent = null;
3262146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event leftEvent = null;
3263146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event rightEvent = null;
3264146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3265146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Pick the starting event closest to the previously selected event,
326646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // if any. distance1 takes precedence over distance2.
3267146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int distance1 = 0;
3268146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int distance2 = 0;
3269146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (prevLocation == FROM_ABOVE) {
3270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (left >= prevCenter) {
3271146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = left - prevCenter;
3272146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (right <= prevCenter) {
3273146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - right;
3274146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3275146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = top - prevBottom;
3276146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_BELOW) {
3277146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (left >= prevCenter) {
3278146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = left - prevCenter;
3279146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (right <= prevCenter) {
3280146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - right;
3281146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3282146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = prevTop - bottom;
3283146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_LEFT) {
3284146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (bottom <= prevCenter) {
3285146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - bottom;
3286146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (top >= prevCenter) {
3287146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = top - prevCenter;
3288146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3289146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = left - prevRight;
3290146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (prevLocation == FROM_RIGHT) {
3291146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (bottom <= prevCenter) {
3292146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = prevCenter - bottom;
3293146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (top >= prevCenter) {
3294146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    distance1 = top - prevCenter;
3295146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3296146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                distance2 = prevLeft - right;
3297146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3298146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (distance1 < startEventDistance1
3299146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    || (distance1 == startEventDistance1 && distance2 < startEventDistance2)) {
3300146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEvent = ev;
3301146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEventDistance1 = distance1;
3302146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                startEventDistance2 = distance2;
3303146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3304146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3305146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // For each neighbor, figure out if it is above or below or left
3306146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // or right of me and compute the distance.
3307146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int jj = 0; jj < len; jj++) {
3308146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (jj == ii) {
3309146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
3310146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3311146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event neighbor = mSelectedEvents.get(jj);
3312146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborLeft = (int) neighbor.left;
3313146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int neighborRight = (int) neighbor.right;
3314146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighbor.endTime <= startTime) {
3315146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely above me.
3316146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // If we overlap the same column, then compute the distance.
3317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborLeft < right && neighborRight > left) {
3318146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int distance = startTime - neighbor.endTime;
3319146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (distance < upDistanceMin) {
3320146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            upDistanceMin = distance;
3321146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            upEvent = neighbor;
3322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else if (distance == upDistanceMin) {
3323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int center = (left + right) / 2;
3324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentDistance = 0;
3325146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentLeft = (int) upEvent.left;
3326146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentRight = (int) upEvent.right;
3327146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (currentRight <= center) {
3328146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = center - currentRight;
3329146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (currentLeft >= center) {
3330146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = currentLeft - center;
3331146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3333146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int neighborDistance = 0;
3334146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborRight <= center) {
3335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = center - neighborRight;
3336146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (neighborLeft >= center) {
3337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = neighborLeft - center;
3338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3339146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborDistance < currentDistance) {
3340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                upDistanceMin = distance;
3341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                upEvent = neighbor;
3342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighbor.startTime >= endTime) {
3346146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely below me.
3347146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // If we overlap the same column, then compute the distance.
3348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborLeft < right && neighborRight > left) {
3349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int distance = neighbor.startTime - endTime;
3350146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (distance < downDistanceMin) {
3351146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            downDistanceMin = distance;
3352146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            downEvent = neighbor;
3353146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else if (distance == downDistanceMin) {
3354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int center = (left + right) / 2;
3355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentDistance = 0;
3356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentLeft = (int) downEvent.left;
3357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int currentRight = (int) downEvent.right;
3358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (currentRight <= center) {
3359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = center - currentRight;
3360146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (currentLeft >= center) {
3361146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                currentDistance = currentLeft - center;
3362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3364146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            int neighborDistance = 0;
3365146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborRight <= center) {
3366146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = center - neighborRight;
3367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            } else if (neighborLeft >= center) {
3368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                neighborDistance = neighborLeft - center;
3369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3370146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            if (neighborDistance < currentDistance) {
3371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                downDistanceMin = distance;
3372146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                                downEvent = neighbor;
3373146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            }
3374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3378146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (neighborLeft >= right) {
3379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely to the right of me.
3380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Take the closest neighbor in the y direction.
3381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int center = (top + bottom) / 2;
3382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int distance = 0;
3383146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborBottom = (int) neighbor.bottom;
3384146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborTop = (int) neighbor.top;
3385146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborBottom <= center) {
3386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = center - neighborBottom;
3387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (neighborTop >= center) {
3388146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = neighborTop - center;
3389146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3390146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (distance < rightDistanceMin) {
3391146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        rightDistanceMin = distance;
3392146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        rightEvent = neighbor;
3393146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (distance == rightDistanceMin) {
3394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Pick the closest in the x direction
3395146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int neighborDistance = neighborLeft - right;
3396146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int currentDistance = (int) rightEvent.left - right;
3397146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (neighborDistance < currentDistance) {
3398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            rightDistanceMin = distance;
3399146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            rightEvent = neighbor;
3400146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3401146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3402146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                } else if (neighborRight <= left) {
3403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // This neighbor is entirely to the left of me.
3404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // Take the closest neighbor in the y direction.
3405146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int center = (top + bottom) / 2;
3406146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int distance = 0;
3407146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborBottom = (int) neighbor.bottom;
3408146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    int neighborTop = (int) neighbor.top;
3409146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (neighborBottom <= center) {
3410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = center - neighborBottom;
3411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (neighborTop >= center) {
3412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        distance = neighborTop - center;
3413146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (distance < leftDistanceMin) {
3415146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        leftDistanceMin = distance;
3416146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        leftEvent = neighbor;
3417146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else if (distance == leftDistanceMin) {
3418146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Pick the closest in the x direction
3419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int neighborDistance = left - neighborRight;
3420146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        int currentDistance = left - (int) leftEvent.right;
3421146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (neighborDistance < currentDistance) {
3422146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            leftDistanceMin = distance;
3423146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            leftEvent = neighbor;
3424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
3425146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
3426146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
3427146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextUp = upEvent;
3429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextDown = downEvent;
3430146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextLeft = leftEvent;
3431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            ev.nextRight = rightEvent;
3432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3433989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedEvent(startEvent);
3434146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3435146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
343625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik    private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint,
343725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik            int visibleTop, int visibleBot) {
343811ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw the Event Rect
34391ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        Rect r = mRect;
344025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN, visibleTop);
344125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN, visibleBot);
34421ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
344325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        r.right = (int) event.right;
34441ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
3445056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        int color;
3446056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        if (event == mClickedEvent) {
3447056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                color = mClickedColor;
3448056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        } else {
3449056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            color = event.color;
3450056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        }
3451056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson
3452f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        switch (event.selfAttendeeStatus) {
3453f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_INVITED:
3454056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                if (event != mClickedEvent) {
3455056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    p.setStyle(Style.STROKE);
3456056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                }
345725996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                break;
3458f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_DECLINED:
3459056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                if (event != mClickedEvent) {
3460056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    color = Utils.getDeclinedColorFromColor(color);
3461056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                }
3462f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_NONE: // Your own events
3463f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_ACCEPTED:
3464f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            case Attendees.ATTENDEE_STATUS_TENTATIVE:
3465f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan            default:
346625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik                p.setStyle(Style.FILL_AND_STROKE);
3467f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan                break;
3468f2ca00946a543a19467d5dbfa3427ecf221186d4Michael Chan        }
3469e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
3470e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        p.setAntiAlias(false);
3471e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik
3472aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        int floorHalfStroke = (int) Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f);
3473aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        int ceilHalfStroke = (int) Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f);
3474aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop);
3475aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke,
3476aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan                visibleBot);
3477aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.left += floorHalfStroke;
3478aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        r.right -= ceilHalfStroke;
3479aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
3480aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        p.setColor(color);
34810a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        int alpha = p.getAlpha();
34820a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        p.setAlpha(mEventsAlpha);
3483aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        canvas.drawRect(r, p);
34840a30ef1efee8fee2a2aecb202afb56f971f3e604Isaac Katzenelson        p.setAlpha(alpha);
34851ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        p.setStyle(Style.FILL);
3486e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
3487146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If this event is selected, then use the selection color
3488056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        if (mSelectedEvent == event && mClickedEvent != null) {
34891ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            boolean paintIt = false;
3490aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            color = 0;
3491146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectionMode == SELECTION_PRESSED) {
3492146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Also, remember the last selected event that we drew
3493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = event;
349478f58c119af15ed85a727e16cddd793ced4c809dMichael Chan                color = mPressedColor;
34951ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                paintIt = true;
3496146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mSelectionMode == SELECTION_SELECTED) {
3497146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Also, remember the last selected event that we drew
3498146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPrevSelectedEvent = event;
349978f58c119af15ed85a727e16cddd793ced4c809dMichael Chan                color = mPressedColor;
35001ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                paintIt = true;
35011ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            }
35021ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
35031ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan            if (paintIt) {
35041ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                p.setColor(color);
35051ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan                canvas.drawRect(r, p);
3506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
3507e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik            p.setAntiAlias(true);
3508146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3509146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
351011ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw cal color square border
351125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.top = (int) event.top + CALENDAR_COLOR_SQUARE_V_OFFSET;
351225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.left = (int) event.left + CALENDAR_COLOR_SQUARE_H_OFFSET;
351325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.bottom = r.top + CALENDAR_COLOR_SQUARE_SIZE + 1;
351425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.right = r.left + CALENDAR_COLOR_SQUARE_SIZE + 1;
351525996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // p.setColor(0xFFFFFFFF);
351625996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // canvas.drawRect(r, p);
35178af2529989a9b10a0bb84736695c22fc02a17a4aThe Android Open Source Project
351811ca73595534d2b2c18ab8c31a735c9e683c9379Michael Chan        // Draw cal color
351925996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.top++;
352025996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.left++;
352125996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.bottom--;
352225996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // r.right--;
352325996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // p.setColor(event.color);
352425996100d6ea61888ebe54d7708a9a371bb12a55RoboErik        // canvas.drawRect(r, p);
3525e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
35263eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan        // Setup rect for drawEventText which follows
35271ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.top = (int) event.top + EVENT_RECT_TOP_MARGIN;
35281ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN;
35291ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
35301ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN;
35311ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        return r;
3532146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3533146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3534056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson    private final Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],");
35350d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan
35360d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // Sanitize a string before passing it to drawText or else we get little
35370d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // squares. For newlines and tabs before a comma, delete the character.
35380d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    // Otherwise, just replace them with a space.
3539f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan    private String drawTextSanitizer(String string, int maxEventTextLen) {
35400d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan        Matcher m = drawTextSanitizerFilter.matcher(string);
3541f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        string = m.replaceAll(",");
3542f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan
3543f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        int len = string.length();
35441c1987163f49158d6ea54f034c874ec3d7b5cde1RoboErik        if (maxEventTextLen <= 0) {
35451c1987163f49158d6ea54f034c874ec3d7b5cde1RoboErik            string = "";
35461c1987163f49158d6ea54f034c874ec3d7b5cde1RoboErik            len = 0;
35471c1987163f49158d6ea54f034c874ec3d7b5cde1RoboErik        } else if (len > maxEventTextLen) {
3548f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            string = string.substring(0, maxEventTextLen);
3549f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan            len = maxEventTextLen;
3550f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        }
3551f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan
3552f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan        return string.replace('\n', ' ');
35530d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan    }
35540d53e947fce63b2a629ea2c32a882eb505e527f7Michael Chan
355508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan    private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top,
35566ab3a2c7de3719a4806d6c9cd5b53cb34a5bb017Mindy Pereira            int bottom, boolean center) {
35571ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging
35581ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
35591ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        int width = rect.right - rect.left;
35601ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        int height = rect.bottom - rect.top;
3561146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3562146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the rectangle is too small for text, then return
35630a0f7f3b2ca79419d417626f11b119d5a8b92f45Michael Chan        if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) {
3564f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            return;
3565f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        }
3566f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan
356708ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        int totalLineHeight = 0;
3568f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        int lineCount = eventLayout.getLineCount();
3569f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        for (int i = 0; i < lineCount; i++) {
3570f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            int lineBottom = eventLayout.getLineBottom(i);
3571f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            if (lineBottom <= height) {
357208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                totalLineHeight = lineBottom;
357308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan            } else {
357408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan                break;
3575f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan            }
3576f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan        }
3577f63bf8348e504760a12d840c7107f326c0e2cadaMichael Chan
357808ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight < top) {
3579146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3580146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3581146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
35823e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        // Use a StaticLayout to format the string.
35833e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        canvas.save();
35846ab3a2c7de3719a4806d6c9cd5b53cb34a5bb017Mindy Pereira      //  canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2));
35856ab3a2c7de3719a4806d6c9cd5b53cb34a5bb017Mindy Pereira        int padding = center? (rect.bottom - rect.top - totalLineHeight) / 2 : 0;
35866ab3a2c7de3719a4806d6c9cd5b53cb34a5bb017Mindy Pereira        canvas.translate(rect.left, rect.top + padding);
35871ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.left = 0;
35881ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.right = width;
35891ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        rect.top = 0;
359008ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        rect.bottom = totalLineHeight;
359108ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan
359208ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // There's a bug somewhere. If this rect is outside of a previous
359308ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // cliprect, this becomes a no-op. What happens is that the text draw
359408ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // past the event rect. The current fix is to not draw the staticLayout
359508ec68f143b718d300eb451047d3ba36dfae2e03Michael Chan        // at all if it is completely out of bound.
35961ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan        canvas.clipRect(rect);
35973e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        eventLayout.draw(canvas);
35983e7015267340b09d142dfcd3ff8acda257711023Shachar Shemesh        canvas.restore();
3599146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3600146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
36011ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    // This is to replace p.setStyle(Style.STROKE); canvas.drawRect() since it
36021ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan    // doesn't work well with hardware acceleration
360321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//    private void drawEmptyRect(Canvas canvas, Rect r, int color) {
360421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        int linesIndex = 0;
360521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
360621bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
360721bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
360821bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
360921bd42039c92232647f54461cd20708224cc7eb6Michael Chan//
361021bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
361121bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
361221bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
361321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
361421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//
361521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
361621bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
361721bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.left;
361821bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
361921bd42039c92232647f54461cd20708224cc7eb6Michael Chan//
362021bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
362121bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.top;
362221bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.right;
362321bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mLines[linesIndex++] = r.bottom;
362421bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        mPaint.setColor(color);
362521bd42039c92232647f54461cd20708224cc7eb6Michael Chan//        canvas.drawLines(mLines, 0, linesIndex, mPaint);
362621bd42039c92232647f54461cd20708224cc7eb6Michael Chan//    }
36271ec87639bd05857bcb2c4a0acc189c0ff413a287Michael Chan
3628146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void updateEventDetails() {
3629146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvent == null || mSelectionMode == SELECTION_HIDDEN
3630146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                || mSelectionMode == SELECTION_LONGPRESS) {
3631146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPopup.dismiss();
3632146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3633146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3634666fc5e8ddbac15c951aad738bd818ee307eae42Erik        if (mLastPopupEventID == mSelectedEvent.id) {
3635666fc5e8ddbac15c951aad738bd818ee307eae42Erik            return;
3636666fc5e8ddbac15c951aad738bd818ee307eae42Erik        }
3637666fc5e8ddbac15c951aad738bd818ee307eae42Erik
3638666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = mSelectedEvent.id;
3639146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3640146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Remove any outstanding callbacks to dismiss the popup.
3641ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        mHandler.removeCallbacks(mDismissPopup);
3642146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3643146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Event event = mSelectedEvent;
3644146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView titleView = (TextView) mPopupView.findViewById(R.id.event_title);
3645146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        titleView.setText(event.title);
3646146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3647146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ImageView imageView = (ImageView) mPopupView.findViewById(R.id.reminder_icon);
3648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView.setVisibility(event.hasAlarm ? View.VISIBLE : View.GONE);
3649146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3650146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView = (ImageView) mPopupView.findViewById(R.id.repeat_icon);
3651146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        imageView.setVisibility(event.isRepeating ? View.VISIBLE : View.GONE);
3652146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3653146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int flags;
3654146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (event.allDay) {
365546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            flags = DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_DATE
365646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL;
3657146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
3658146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE
3659146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL
3660146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
3661146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3662ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        if (DateFormat.is24HourFormat(mContext)) {
3663146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            flags |= DateUtils.FORMAT_24HOUR;
3664146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
366546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        String timeRange = Utils.formatDateRange(mContext, event.startMillis, event.endMillis,
366646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                flags);
3667146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView timeView = (TextView) mPopupView.findViewById(R.id.time);
3668146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        timeView.setText(timeRange);
3669146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3670146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        TextView whereView = (TextView) mPopupView.findViewById(R.id.where);
3671146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        final boolean empty = TextUtils.isEmpty(event.location);
3672146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        whereView.setVisibility(empty ? View.GONE : View.VISIBLE);
3673146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!empty) whereView.setText(event.location);
3674146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3675146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.LEFT, mHoursWidth, 5);
3676ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        mHandler.postDelayed(mDismissPopup, POPUP_DISMISS_DELAY);
3677146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3678146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3679146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // The following routines are called from the parent activity when certain
3680146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // touch events occur.
36814484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doDown(MotionEvent ev) {
3682146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mTouchMode = TOUCH_MODE_DOWN;
3683146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mViewStartX = 0;
3684146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mOnFlingCalled = false;
3685ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        mHandler.removeCallbacks(mContinueScroll);
3686056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        int x = (int) ev.getX();
3687056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        int y = (int) ev.getY();
3688056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson
3689056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        // Save selection information: we use setSelectionFromPosition to find the selected event
3690056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        // in order to show the "clicked" color. But since it is also setting the selected info
3691056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        // for new events, we need to restore the old info after calling the function.
3692056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        Event oldSelectedEvent = mSelectedEvent;
3693056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        int oldSelectionDay = mSelectionDay;
3694056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        int oldSelectionHour = mSelectionHour;
3695989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        if (setSelectionFromPosition(x, y, false)) {
3696056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            // If a time was selected (a blue selection box is visible) and the click location
3697056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            // is in the selected time, do not show a click on an event to prevent a situation
3698056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            // of both a selection and an event are clicked when they overlap.
3699989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            boolean pressedSelected = (mSelectionMode != SELECTION_HIDDEN)
3700056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    && oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour;
3701056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            if (!pressedSelected && mSelectedEvent != null) {
3702ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson                mSavedClickedEvent = mSelectedEvent;
370331c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson                mDownTouchTime = System.currentTimeMillis();
3704ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson                postDelayed (mSetClick,mOnDownDelay);
3705056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            } else {
3706ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson                eventClickCleanup();
3707056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            }
3708056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        }
3709056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        mSelectedEvent = oldSelectedEvent;
3710056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        mSelectionDay = oldSelectionDay;
3711056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        mSelectionHour = oldSelectionHour;
3712056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        invalidate();
3713146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3714146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
37152fda24534978f55711526dbde747aa50d68dec40RoboErik    // Kicks off all the animations when the expand allday area is tapped
37162fda24534978f55711526dbde747aa50d68dec40RoboErik    private void doExpandAllDayClick() {
37172fda24534978f55711526dbde747aa50d68dec40RoboErik        mShowAllAllDayEvents = !mShowAllAllDayEvents;
37182fda24534978f55711526dbde747aa50d68dec40RoboErik
37192fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator.setFrameDelay(0);
37202fda24534978f55711526dbde747aa50d68dec40RoboErik
37212fda24534978f55711526dbde747aa50d68dec40RoboErik        // Determine the starting height
37222fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAnimateDayHeight == 0) {
37232fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayHeight = mShowAllAllDayEvents ?
37242fda24534978f55711526dbde747aa50d68dec40RoboErik                    mAlldayHeight - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT : mAlldayHeight;
37252fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37262fda24534978f55711526dbde747aa50d68dec40RoboErik        // Cancel current animations
37272fda24534978f55711526dbde747aa50d68dec40RoboErik        mCancellingAnimations = true;
37282fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayAnimator != null) {
37292fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayAnimator.cancel();
37302fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37312fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayEventAnimator != null) {
37322fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator.cancel();
37332fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37342fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMoreAlldayEventsAnimator != null) {
37352fda24534978f55711526dbde747aa50d68dec40RoboErik            mMoreAlldayEventsAnimator.cancel();
37362fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37372fda24534978f55711526dbde747aa50d68dec40RoboErik        mCancellingAnimations = false;
37382fda24534978f55711526dbde747aa50d68dec40RoboErik        // get new animators
37392fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator = getAllDayAnimator();
37402fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayEventAnimator = getAllDayEventAnimator();
37412fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(this,
37422fda24534978f55711526dbde747aa50d68dec40RoboErik                    "moreAllDayEventsTextAlpha",
37432fda24534978f55711526dbde747aa50d68dec40RoboErik                    mShowAllAllDayEvents ? MORE_EVENTS_MAX_ALPHA : 0,
37442fda24534978f55711526dbde747aa50d68dec40RoboErik                    mShowAllAllDayEvents ? 0 : MORE_EVENTS_MAX_ALPHA);
37452fda24534978f55711526dbde747aa50d68dec40RoboErik
37462fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up delays and start the animators
37472fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator.setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
37482fda24534978f55711526dbde747aa50d68dec40RoboErik        mAlldayAnimator.start();
37492fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.setStartDelay(mShowAllAllDayEvents ? 0 : ANIMATION_DURATION);
37502fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION);
37512fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsAnimator.start();
37522fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mAlldayEventAnimator != null) {
37532fda24534978f55711526dbde747aa50d68dec40RoboErik            // This is the only animator that can return null, so check it
37542fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator
37552fda24534978f55711526dbde747aa50d68dec40RoboErik                    .setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
37562fda24534978f55711526dbde747aa50d68dec40RoboErik            mAlldayEventAnimator.start();
37572fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37582fda24534978f55711526dbde747aa50d68dec40RoboErik    }
37592fda24534978f55711526dbde747aa50d68dec40RoboErik
37602fda24534978f55711526dbde747aa50d68dec40RoboErik    /**
37612fda24534978f55711526dbde747aa50d68dec40RoboErik     * Figures out the initial heights for allDay events and space when
37622fda24534978f55711526dbde747aa50d68dec40RoboErik     * a view is being set up.
37632fda24534978f55711526dbde747aa50d68dec40RoboErik     */
37642fda24534978f55711526dbde747aa50d68dec40RoboErik    public void initAllDayHeights() {
37652fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) {
37662fda24534978f55711526dbde747aa50d68dec40RoboErik            return;
37672fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37682fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mShowAllAllDayEvents) {
37692fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
37702fda24534978f55711526dbde747aa50d68dec40RoboErik            maxADHeight = Math.min(maxADHeight,
37712fda24534978f55711526dbde747aa50d68dec40RoboErik                    (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
37722fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents;
37732fda24534978f55711526dbde747aa50d68dec40RoboErik        } else {
37742fda24534978f55711526dbde747aa50d68dec40RoboErik            mAnimateDayEventHeight = (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
37752fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37762fda24534978f55711526dbde747aa50d68dec40RoboErik    }
37772fda24534978f55711526dbde747aa50d68dec40RoboErik
37782fda24534978f55711526dbde747aa50d68dec40RoboErik    // Sets up an animator for changing the height of allday events
37792fda24534978f55711526dbde747aa50d68dec40RoboErik    private ObjectAnimator getAllDayEventAnimator() {
37802fda24534978f55711526dbde747aa50d68dec40RoboErik        // First calculate the absolute max height
37812fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
37822fda24534978f55711526dbde747aa50d68dec40RoboErik        // Now expand to fit but not beyond the absolute max
37832fda24534978f55711526dbde747aa50d68dec40RoboErik        maxADHeight =
37842fda24534978f55711526dbde747aa50d68dec40RoboErik                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
37852fda24534978f55711526dbde747aa50d68dec40RoboErik        // calculate the height of individual events in order to fit
37862fda24534978f55711526dbde747aa50d68dec40RoboErik        int fitHeight = maxADHeight / mMaxAlldayEvents;
37872fda24534978f55711526dbde747aa50d68dec40RoboErik        int currentHeight = mAnimateDayEventHeight;
37882fda24534978f55711526dbde747aa50d68dec40RoboErik        int desiredHeight =
37892fda24534978f55711526dbde747aa50d68dec40RoboErik                mShowAllAllDayEvents ? fitHeight : (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
37902fda24534978f55711526dbde747aa50d68dec40RoboErik        // if there's nothing to animate just return
37912fda24534978f55711526dbde747aa50d68dec40RoboErik        if (currentHeight == desiredHeight) {
37922fda24534978f55711526dbde747aa50d68dec40RoboErik            return null;
37932fda24534978f55711526dbde747aa50d68dec40RoboErik        }
37942fda24534978f55711526dbde747aa50d68dec40RoboErik
37952fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the animator with the calculated values
37962fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayEventHeight",
37972fda24534978f55711526dbde747aa50d68dec40RoboErik                currentHeight, desiredHeight);
37982fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.setDuration(ANIMATION_DURATION);
37992fda24534978f55711526dbde747aa50d68dec40RoboErik        return animator;
38002fda24534978f55711526dbde747aa50d68dec40RoboErik    }
38012fda24534978f55711526dbde747aa50d68dec40RoboErik
38022fda24534978f55711526dbde747aa50d68dec40RoboErik    // Sets up an animator for changing the height of the allday area
38032fda24534978f55711526dbde747aa50d68dec40RoboErik    private ObjectAnimator getAllDayAnimator() {
38042fda24534978f55711526dbde747aa50d68dec40RoboErik        // Calculate the absolute max height
38052fda24534978f55711526dbde747aa50d68dec40RoboErik        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
38062fda24534978f55711526dbde747aa50d68dec40RoboErik        // Find the desired height but don't exceed abs max
38072fda24534978f55711526dbde747aa50d68dec40RoboErik        maxADHeight =
38082fda24534978f55711526dbde747aa50d68dec40RoboErik                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
38092fda24534978f55711526dbde747aa50d68dec40RoboErik        // calculate the current and desired heights
38102fda24534978f55711526dbde747aa50d68dec40RoboErik        int currentHeight = mAnimateDayHeight != 0 ? mAnimateDayHeight : mAlldayHeight;
38112fda24534978f55711526dbde747aa50d68dec40RoboErik        int desiredHeight = mShowAllAllDayEvents ? maxADHeight :
38122fda24534978f55711526dbde747aa50d68dec40RoboErik                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1);
38132fda24534978f55711526dbde747aa50d68dec40RoboErik
38142fda24534978f55711526dbde747aa50d68dec40RoboErik        // Set up the animator with the calculated values
38152fda24534978f55711526dbde747aa50d68dec40RoboErik        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayHeight",
38162fda24534978f55711526dbde747aa50d68dec40RoboErik                currentHeight, desiredHeight);
38172fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.setDuration(ANIMATION_DURATION);
38182fda24534978f55711526dbde747aa50d68dec40RoboErik
38192fda24534978f55711526dbde747aa50d68dec40RoboErik        animator.addListener(new AnimatorListenerAdapter() {
38202fda24534978f55711526dbde747aa50d68dec40RoboErik            @Override
38212fda24534978f55711526dbde747aa50d68dec40RoboErik            public void onAnimationEnd(Animator animation) {
38222fda24534978f55711526dbde747aa50d68dec40RoboErik                if (!mCancellingAnimations) {
38232fda24534978f55711526dbde747aa50d68dec40RoboErik                    // when finished, set this to 0 to signify not animating
38242fda24534978f55711526dbde747aa50d68dec40RoboErik                    mAnimateDayHeight = 0;
3825cbc6ad106295bad0bef29cb51a2dfb56be9d6bcdRoboErik                    mUseExpandIcon = !mShowAllAllDayEvents;
38262fda24534978f55711526dbde747aa50d68dec40RoboErik                }
38272fda24534978f55711526dbde747aa50d68dec40RoboErik                mRemeasure = true;
38282fda24534978f55711526dbde747aa50d68dec40RoboErik                invalidate();
38292fda24534978f55711526dbde747aa50d68dec40RoboErik            }
38302fda24534978f55711526dbde747aa50d68dec40RoboErik        });
38312fda24534978f55711526dbde747aa50d68dec40RoboErik        return animator;
38322fda24534978f55711526dbde747aa50d68dec40RoboErik    }
38332fda24534978f55711526dbde747aa50d68dec40RoboErik
38342fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the 'box +n' alpha text used by the animator
38352fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setMoreAllDayEventsTextAlpha(int alpha) {
38362fda24534978f55711526dbde747aa50d68dec40RoboErik        mMoreAlldayEventsTextAlpha = alpha;
38372fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
38382fda24534978f55711526dbde747aa50d68dec40RoboErik    }
38392fda24534978f55711526dbde747aa50d68dec40RoboErik
38402fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the height of the allday area used by the animator
38412fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setAnimateDayHeight(int height) {
38422fda24534978f55711526dbde747aa50d68dec40RoboErik        mAnimateDayHeight = height;
38432fda24534978f55711526dbde747aa50d68dec40RoboErik        mRemeasure = true;
38442fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
38452fda24534978f55711526dbde747aa50d68dec40RoboErik    }
38462fda24534978f55711526dbde747aa50d68dec40RoboErik
38472fda24534978f55711526dbde747aa50d68dec40RoboErik    // setter for the height of allday events used by the animator
38482fda24534978f55711526dbde747aa50d68dec40RoboErik    public void setAnimateDayEventHeight(int height) {
38492fda24534978f55711526dbde747aa50d68dec40RoboErik        mAnimateDayEventHeight = height;
38502fda24534978f55711526dbde747aa50d68dec40RoboErik        mRemeasure = true;
38512fda24534978f55711526dbde747aa50d68dec40RoboErik        invalidate();
38522fda24534978f55711526dbde747aa50d68dec40RoboErik    }
38532fda24534978f55711526dbde747aa50d68dec40RoboErik
38544484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doSingleTapUp(MotionEvent ev) {
3855bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        if (!mHandleActionUp || mScrolling) {
3856d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            return;
3857d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3858d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
3859146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int x = (int) ev.getX();
3860146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int y = (int) ev.getY();
3861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectedDay = mSelectionDay;
3862146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int selectedHour = mSelectionHour;
3863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
38642fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
38652fda24534978f55711526dbde747aa50d68dec40RoboErik            // check if the tap was in the allday expansion area
38664ab51bf9fc2f3e6915705f39688e8244fc74028fRoboErik            int bottom = mFirstCell;
38672fda24534978f55711526dbde747aa50d68dec40RoboErik            if((x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight)
38682fda24534978f55711526dbde747aa50d68dec40RoboErik                    || (!mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom &&
38692fda24534978f55711526dbde747aa50d68dec40RoboErik                            y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)) {
38702fda24534978f55711526dbde747aa50d68dec40RoboErik                doExpandAllDayClick();
38712fda24534978f55711526dbde747aa50d68dec40RoboErik                return;
38722fda24534978f55711526dbde747aa50d68dec40RoboErik            }
38732fda24534978f55711526dbde747aa50d68dec40RoboErik        }
38742fda24534978f55711526dbde747aa50d68dec40RoboErik
3875989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        boolean validPosition = setSelectionFromPosition(x, y, false);
3876146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (!validPosition) {
3877360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            if (y < DAY_HEADER_HEIGHT) {
3878360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                Time selectedTime = new Time(mBaseDate);
3879360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.setJulianDay(mSelectionDay);
3880360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.hour = mSelectionHour;
3881360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                selectedTime.normalize(true /* ignore isDst */);
3882360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                mController.sendEvent(this, EventType.GO_TO, null, null, selectedTime, -1,
3883360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan                        ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null);
3884360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            }
3885146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
3886146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
3887146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3888b6fb51224b578b26e503fddae2799d03a2aaad47RoboErik        boolean hasSelection = mSelectionMode != SELECTION_HIDDEN;
388995f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        boolean pressedSelected = (hasSelection || mTouchExplorationEnabled)
389095f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                && selectedDay == mSelectionDay && selectedHour == mSelectionHour;
3891146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3892989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        if (pressedSelected && mSavedClickedEvent == null) {
3893344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            // If the tap is on an already selected hour slot, then create a new
3894344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            // event
3895edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            long extraLong = 0;
3896edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            if (mSelectionAllday) {
3897edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
3898edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            }
389995f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            mSelectionMode = SELECTION_SELECTED;
3900edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan            mController.sendEventRelatedEventWithExtra(this, EventType.CREATE_EVENT, -1,
3901edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    getSelectedTimeInMillis(), 0, (int) ev.getRawX(), (int) ev.getRawY(),
3902edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                    extraLong, -1);
390395f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        } else if (mSelectedEvent != null) {
390495f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            // If the tap is on an event, launch the "View event" view
390595f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            if (mIsAccessibilityEnabled) {
390695f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan                mAccessibilityMgr.interrupt();
390795f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            }
390895f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan
390995f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            mSelectionMode = SELECTION_HIDDEN;
39100aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson
39110aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            int yLocation =
39120aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson                (int)((mSelectedEvent.top + mSelectedEvent.bottom)/2);
39130aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            // Y location is affected by the position of the event in the scrolling
39140aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            // view (mViewStartY) and the presence of all day events (mFirstCell)
39150aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            if (!mSelectedEvent.allDay) {
39160aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson                yLocation += (mFirstCell - mViewStartY);
39170aa0c61fcd2464f034d15f33db265b6d080f4af9Isaac Katzenelson            }
3918056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson            mClickedYLocation = yLocation;
391931c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson            long clearDelay = (CLICK_DISPLAY_DURATION + mOnDownDelay) -
392031c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson                    (System.currentTimeMillis() - mDownTouchTime);
392131c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson            if (clearDelay > 0) {
392231c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson                this.postDelayed(mClearClick, clearDelay);
392331c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson            } else {
392431c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson                this.post(mClearClick);
392531c7812de1b56561e56f2af6928debd66e4a9124Isaac Katzenelson            }
3926344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan        } else {
392795f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            // Select time
3928344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            Time startTime = new Time(mBaseDate);
3929344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.setJulianDay(mSelectionDay);
3930344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.hour = mSelectionHour;
3931344c69e88c32ea40386c40d420c83bece9c2d556Michael Chan            startTime.normalize(true /* ignore isDst */);
393283b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
393383b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            Time endTime = new Time(startTime);
393483b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan            endTime.hour++;
393583b0fe36b523192a504a1d2baf2f36676a80b9d5Michael Chan
393695f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan            mSelectionMode = SELECTION_SELECTED;
393746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            mController.sendEvent(this, EventType.GO_TO, startTime, endTime, -1, ViewType.CURRENT,
393846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    CalendarController.EXTRA_GOTO_TIME, null, null);
3939146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
394095f745f00303b00549a1d59d8ccb0b82ea91c17cMichael Chan        invalidate();
3941146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3942146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
39434484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doLongPress(MotionEvent ev) {
3944ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        eventClickCleanup();
3945bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        if (mScrolling) {
3946bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan            return;
3947bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan        }
3948bbf2cbdb040108da50d763b699836e5f63fb7cceMichael Chan
3949d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        // Scale gesture in progress
3950d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mStartingSpanY != 0) {
3951d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            return;
3952d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
3953d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
395480bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        int x = (int) ev.getX();
395580bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        int y = (int) ev.getY();
395680bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan
3957989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        boolean validPosition = setSelectionFromPosition(x, y, false);
395880bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        if (!validPosition) {
395980bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan            // return if the touch wasn't on an area of concern
396080bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan            return;
396180bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan        }
396280bbecfb183210ccbe53ec99e8b3d9aab323da2bMichael Chan
3963146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_LONGPRESS;
3964146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
3965146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        performLongClick();
3966146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
3967146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
39684484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan    private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) {
396995d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan        cancelAnimation();
39704f58287a1e02325337e39073a50bbb880094099fRoboErik        if (mStartingScroll) {
39714f58287a1e02325337e39073a50bbb880094099fRoboErik            mInitialScrollX = 0;
39724f58287a1e02325337e39073a50bbb880094099fRoboErik            mInitialScrollY = 0;
39734f58287a1e02325337e39073a50bbb880094099fRoboErik            mStartingScroll = false;
39744f58287a1e02325337e39073a50bbb880094099fRoboErik        }
39756d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
39764f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollX += deltaX;
39774f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollY += deltaY;
39784f58287a1e02325337e39073a50bbb880094099fRoboErik        int distanceX = (int) mInitialScrollX;
39794f58287a1e02325337e39073a50bbb880094099fRoboErik        int distanceY = (int) mInitialScrollY;
3980146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3981a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        final float focusY = getAverageY(e2);
3982a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        if (mRecalCenterHour) {
3983a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            // Calculate the hour that correspond to the average of the Y touch points
3984a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight)
3985a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                    / (mCellHeight + DAY_GAP);
3986a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            mRecalCenterHour = false;
3987a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        }
3988a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan
3989146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If we haven't figured out the predominant scroll direction yet,
3990146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // then do it now.
3991146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mTouchMode == TOUCH_MODE_DOWN) {
3992146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int absDistanceX = Math.abs(distanceX);
3993146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int absDistanceY = Math.abs(distanceY);
3994146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mScrollStartY = mViewStartY;
3995146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPreviousDirection = 0;
3996146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
3997f9a2842a66fe858336144ba87c668efc2e18a9acMichael Chan            if (absDistanceX > absDistanceY) {
39984dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan                int slopFactor = mScaleGestureDetector.isInProgress() ? 20 : 2;
39994dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan                if (absDistanceX > mScaledPagingTouchSlop * slopFactor) {
40004dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan                    mTouchMode = TOUCH_MODE_HSCROLL;
40014dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan                    mViewStartX = distanceX;
40024dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan                    initNextView(-mViewStartX);
40034dcabc96044eb83be2320e753fee9b1dc266ec32Michael Chan                }
4004146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
4005146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mTouchMode = TOUCH_MODE_VSCROLL;
4006146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4007146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
4008146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // We are already scrolling horizontally, so check if we
4009146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // changed the direction of scrolling so that the other week
4010146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // is now visible.
4011146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartX = distanceX;
4012146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (distanceX != 0) {
4013146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                int direction = (distanceX > 0) ? 1 : -1;
4014146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (direction != mPreviousDirection) {
4015146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // The user has switched the direction of scrolling
4016146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    // so re-init the next view
4017146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    initNextView(-mViewStartX);
4018146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    mPreviousDirection = direction;
4019146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4020146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4021146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4022146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4023146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) {
4024a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            // Calculate the top of the visible region in the calendar grid.
4025a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            // Increasing/decrease this will scroll the calendar grid up/down.
4026a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            mViewStartY = (int) ((mGestureCenterHour * (mCellHeight + DAY_GAP))
4027a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                    - focusY + DAY_HEADER_HEIGHT + mAlldayHeight);
4028433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
4029433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            // If dragging while already at the end, do a glow
4030433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            final int pulledToY = (int) (mScrollStartY + deltaY);
4031433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (pulledToY < 0) {
4032433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectTop.onPull(deltaY / mViewHeight);
4033433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (!mEdgeEffectBottom.isFinished()) {
4034433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectBottom.onRelease();
4035433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
4036433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            } else if (pulledToY > mMaxViewStartY) {
4037433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectBottom.onPull(deltaY / mViewHeight);
4038433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (!mEdgeEffectTop.isFinished()) {
4039433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectTop.onRelease();
4040433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
4041433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
4042433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
4043146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mViewStartY < 0) {
4044146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = 0;
4045a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                mRecalCenterHour = true;
4046146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mViewStartY > mMaxViewStartY) {
4047146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mViewStartY = mMaxViewStartY;
4048a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                mRecalCenterHour = true;
4049a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            }
4050a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            if (mRecalCenterHour) {
4051a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                // Calculate the hour that correspond to the average of the Y touch points
4052a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight)
4053a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                        / (mCellHeight + DAY_GAP);
4054a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                mRecalCenterHour = false;
4055146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4056146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeFirstHour();
4057146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4058146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4059146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mScrolling = true;
4060146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4061d9dbab823d56bf596eedaecd0935037bd39d02c1Michael Chan        mSelectionMode = SELECTION_HIDDEN;
4062146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        invalidate();
4063146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4064146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4065a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan    private float getAverageY(MotionEvent me) {
4066a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        int count = me.getPointerCount();
4067a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        float focusY = 0;
4068a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        for (int i = 0; i < count; i++) {
4069a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            focusY += me.getY(i);
4070a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        }
4071a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        focusY /= count;
4072a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        return focusY;
4073a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan    }
4074a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan
4075f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    private void cancelAnimation() {
4076f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        Animation in = mViewSwitcher.getInAnimation();
4077f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        if (in != null) {
407895d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            // cancel() doesn't terminate cleanly.
407995d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            in.scaleCurrentDuration(0);
40806d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan        }
4081f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        Animation out = mViewSwitcher.getOutAnimation();
4082f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        if (out != null) {
408395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            // cancel() doesn't terminate cleanly.
408495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            out.scaleCurrentDuration(0);
4085f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        }
4086f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    }
4087f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan
4088f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan    private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
4089f6c785f1dacb26523f229e1b06e3a4eb6fa4ff0dMichael Chan        cancelAnimation();
40906d4ce6ed308da8b4d8f2e62660865e396713c025Michael Chan
4091146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectionMode = SELECTION_HIDDEN;
4092ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        eventClickCleanup();
4093056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson
4094146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mOnFlingCalled = true;
409590a68379c01137781249156742cfc9ba21684b83Michael Chan
409690a68379c01137781249156742cfc9ba21684b83Michael Chan        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
4097030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown            // Horizontal fling.
409811638ad4fb56b978770aebe75deac7a0e6ac60d2Erik            // initNextView(deltaX);
409990a68379c01137781249156742cfc9ba21684b83Michael Chan            mTouchMode = TOUCH_MODE_INITIAL_STATE;
410090a68379c01137781249156742cfc9ba21684b83Michael Chan            if (DEBUG) Log.d(TAG, "doFling: velocityX " + velocityX);
410190a68379c01137781249156742cfc9ba21684b83Michael Chan            int deltaX = (int) e2.getX() - (int) e1.getX();
41022c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            switchViews(deltaX < 0, mViewStartX, mViewWidth, velocityX);
4103146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mViewStartX = 0;
4104146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
4105146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4106146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
410790a68379c01137781249156742cfc9ba21684b83Michael Chan        if ((mTouchMode & TOUCH_MODE_VSCROLL) == 0) {
410890a68379c01137781249156742cfc9ba21684b83Michael Chan            if (DEBUG) Log.d(TAG, "doFling: no fling");
410990a68379c01137781249156742cfc9ba21684b83Michael Chan            return;
411090a68379c01137781249156742cfc9ba21684b83Michael Chan        }
411190a68379c01137781249156742cfc9ba21684b83Michael Chan
4112030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        // Vertical fling.
411390a68379c01137781249156742cfc9ba21684b83Michael Chan        mTouchMode = TOUCH_MODE_INITIAL_STATE;
4114030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown        mViewStartX = 0;
4115030803e32522be52a1ddeb436edbd024942d7c4fDavid Brown
41163f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        if (DEBUG) {
41173f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            Log.d(TAG, "doFling: mViewStartY" + mViewStartY + " velocityY " + velocityY);
41183f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        }
41193f6328f579b05c02606572c777541b97aa04f9f3Michael Chan
4120146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Continue scrolling vertically
41213f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScrolling = true;
41223f6328f579b05c02606572c777541b97aa04f9f3Michael Chan        mScroller.fling(0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */,
4123433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                (int) -velocityY, 0 /* minX */, 0 /* maxX */, 0 /* minY */,
4124433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE);
4125433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
4126433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // When flinging down, show a glow when it hits the end only if it
4127433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // wasn't started at the top
4128433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        if (velocityY > 0 && mViewStartY != 0) {
4129433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            mCallEdgeEffectOnAbsorb = true;
4130433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
4131433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // When flinging up, show a glow when it hits the end only if it wasn't
4132433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        // started at the bottom
4133433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        else if (velocityY < 0 && mViewStartY != mMaxViewStartY) {
4134433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            mCallEdgeEffectOnAbsorb = true;
4135433ba924de1ac26b923e2beb061812012749e4f3Michael Chan        }
4136ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        mHandler.post(mContinueScroll);
4137146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4138146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4139146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private boolean initNextView(int deltaX) {
4140146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Change the view to the previous day or week
4141d190418220467c02327dd9b3ae1eae238889720bMichael Chan        DayView view = (DayView) mViewSwitcher.getNextView();
4142146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Time date = view.mBaseDate;
4143146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        date.set(mBaseDate);
4144146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        boolean switchForward;
4145146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (deltaX > 0) {
4146146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.monthDay -= mNumDays;
4147989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            view.setSelectedDay(mSelectionDay - mNumDays);
4148146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchForward = false;
4149146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
4150146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            date.monthDay += mNumDays;
4151989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            view.setSelectedDay(mSelectionDay + mNumDays);
4152146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switchForward = true;
4153146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4154146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        date.normalize(true /* ignore isDst */);
4155146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        initView(view);
4156ad36a3c3cde7a2ec6d3a35d2529d46f03bd8d59dMichael Chan        view.layout(getLeft(), getTop(), getRight(), getBottom());
4157146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        view.reloadEvents();
4158146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return switchForward;
4159146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4160146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4161d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
4162d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public boolean onScaleBegin(ScaleGestureDetector detector) {
4163d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mHandleActionUp = false;
41642fda24534978f55711526dbde747aa50d68dec40RoboErik        float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
4165d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP);
4166d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4167d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY()));
4168d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mCellHeightBeforeScaleGesture = mCellHeight;
4169d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
41709ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG_SCALING) {
4171d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
41726f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan            Log.d(TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour
41736f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan                    + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY
41746f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan                    + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY());
4175d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
4176d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4177d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        return true;
4178d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
4179d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4180d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
4181d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public boolean onScale(ScaleGestureDetector detector) {
41826f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan        float spanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY()));
4183d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4184d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY);
4185d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4186d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mCellHeight < mMinCellHeight) {
4187d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            // If mStartingSpanY is too small, even a small increase in the
4188d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT
41896f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan            mStartingSpanY = spanY;
4190d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = mMinCellHeight;
4191d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeightBeforeScaleGesture = mMinCellHeight;
4192d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        } else if (mCellHeight > MAX_CELL_HEIGHT) {
4193d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mStartingSpanY = spanY;
4194d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight = MAX_CELL_HEIGHT;
4195d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT;
4196d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
4197d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
41982fda24534978f55711526dbde747aa50d68dec40RoboErik        int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
4199d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels;
4200d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
4201d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
42029ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG_SCALING) {
4203d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
42046f5fc3bf685b396da65290d32dd4744aa7a9fc9dMichael Chan            Log.d(TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: "
4205d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:"
4206d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    + mCellHeight + " SpanY:" + detector.getCurrentSpanY());
4207d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
4208d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4209d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        if (mViewStartY < 0) {
4210d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = 0;
4211d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
4212d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    / (float) (mCellHeight + DAY_GAP);
4213d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        } else if (mViewStartY > mMaxViewStartY) {
4214d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mViewStartY = mMaxViewStartY;
4215d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
4216d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                    / (float) (mCellHeight + DAY_GAP);
4217d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
4218d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        computeFirstHour();
4219d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4220d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mRemeasure = true;
4221d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        invalidate();
4222d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        return true;
4223d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
4224d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4225d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    // ScaleGestureDetector.OnScaleGestureListener
4226d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    public void onScaleEnd(ScaleGestureDetector detector) {
42274f58287a1e02325337e39073a50bbb880094099fRoboErik        mScrollStartY = mViewStartY;
42284f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollY = 0;
42294f58287a1e02325337e39073a50bbb880094099fRoboErik        mInitialScrollX = 0;
4230d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        mStartingSpanY = 0;
4231d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan    }
4232d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4233146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    @Override
4234146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public boolean onTouchEvent(MotionEvent ev) {
4235146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int action = ev.getAction();
42369ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan        if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount());
4237146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4238a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        if ((ev.getActionMasked() == MotionEvent.ACTION_DOWN) ||
4239a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                (ev.getActionMasked() == MotionEvent.ACTION_UP) ||
4240a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                (ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP) ||
4241a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan                (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) {
4242a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan            mRecalCenterHour = true;
4243a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan        }
4244a498f6ed3628bd36576949acd8e0afd71fca8b02Michael Chan
42451ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan        if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) {
42461ba48526c2542484d91383c21d3d8e07db87df0fMichael Chan            mScaleGestureDetector.onTouchEvent(ev);
4247d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        }
4248d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan
4249146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        switch (action) {
425046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_DOWN:
42514f58287a1e02325337e39073a50bbb880094099fRoboErik                mStartingScroll = true;
42529ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) {
42539ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                    Log.e(TAG, "ACTION_DOWN ev.getDownTime = " + ev.getDownTime() + " Cnt="
42549ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                            + ev.getPointerCount());
42559ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                }
42569ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
42572fda24534978f55711526dbde747aa50d68dec40RoboErik                int bottom = mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
42582fda24534978f55711526dbde747aa50d68dec40RoboErik                if (ev.getY() < bottom) {
42592fda24534978f55711526dbde747aa50d68dec40RoboErik                    mTouchStartedInAlldayArea = true;
42602fda24534978f55711526dbde747aa50d68dec40RoboErik                } else {
42612fda24534978f55711526dbde747aa50d68dec40RoboErik                    mTouchStartedInAlldayArea = false;
42622fda24534978f55711526dbde747aa50d68dec40RoboErik                }
4263d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                mHandleActionUp = true;
426446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
4265d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan                return true;
426646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
426746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_MOVE:
42689ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) Log.e(TAG, "ACTION_MOVE Cnt=" + ev.getPointerCount() + DayView.this);
426946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
4270146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
427146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
427246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_UP:
42739ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) Log.e(TAG, "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp);
4274433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectTop.onRelease();
4275433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mEdgeEffectBottom.onRelease();
42764f58287a1e02325337e39073a50bbb880094099fRoboErik                mStartingScroll = false;
427746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
427846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (!mHandleActionUp) {
427946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mHandleActionUp = true;
428057756df22153a2c0155a4e743c8a658caa8ada33RoboErik                    mViewStartX = 0;
428157756df22153a2c0155a4e743c8a658caa8ada33RoboErik                    invalidate();
42827abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project                    return true;
428346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
4284befa15c0a47a744a7c28a9be7b079a93e195c833Isaac Katzenelson
4285befa15c0a47a744a7c28a9be7b079a93e195c833Isaac Katzenelson                if (mOnFlingCalled) {
4286befa15c0a47a744a7c28a9be7b079a93e195c833Isaac Katzenelson                    return true;
4287befa15c0a47a744a7c28a9be7b079a93e195c833Isaac Katzenelson                }
4288befa15c0a47a744a7c28a9be7b079a93e195c833Isaac Katzenelson
4289056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                // If we were scrolling, then reset the selected hour so that it
4290056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                // is visible.
4291056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                if (mScrolling) {
4292056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    mScrolling = false;
4293056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    resetSelectedHour();
4294056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    invalidate();
4295056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                }
429684fbd42c4ea8bae2a0edbd49333cb7112bf3cd51Isaac Katzenelson
429746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
429846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    mTouchMode = TOUCH_MODE_INITIAL_STATE;
429946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) {
430046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // The user has gone beyond the threshold so switch views
430146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views");
43022c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                        switchViews(mViewStartX > 0, mViewStartX, mViewWidth, 0);
430346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mViewStartX = 0;
430446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        return true;
430546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    } else {
430646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // Not beyond the threshold so invalidate which will cause
430746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // the view to snap back. Also call recalc() to ensure
430846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        // that we have the correct starting date and title.
430946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back");
431046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        recalc();
431146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        invalidate();
431246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                        mViewStartX = 0;
431346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    }
431446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
431546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
431646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return true;
4317146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
431846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // This case isn't expected to happen.
431946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            case MotionEvent.ACTION_CANCEL:
432046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (DEBUG) Log.e(TAG, "ACTION_CANCEL");
432146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                mGestureDetector.onTouchEvent(ev);
4322146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mScrolling = false;
4323146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                resetSelectedHour();
4324146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                return true;
432546a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
432646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            default:
43279ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan                if (DEBUG) Log.e(TAG, "Not MotionEvent " + ev.toString());
432846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                if (mGestureDetector.onTouchEvent(ev)) {
432946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                    return true;
433046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                }
433146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                return super.onTouchEvent(ev);
4332146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4333146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4334146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4335146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
4336146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        MenuItem item;
4337146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4338146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If the trackball is held down, then the context menu pops up and
433946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        // we never get onKeyUp() for the long-press. So check for it here
4340146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // and change the selection to the long-press state.
4341146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectionMode != SELECTION_LONGPRESS) {
4342146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mSelectionMode = SELECTION_LONGPRESS;
4343146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
4344146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4345146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4346e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        final long startMillis = getSelectedTimeInMillis();
4347146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int flags = DateUtils.FORMAT_SHOW_TIME
4348146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                | DateUtils.FORMAT_CAP_NOON_MIDNIGHT
4349146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                | DateUtils.FORMAT_SHOW_WEEKDAY;
4350c5f92d7bdeb1950ace783cc022aa41a4b4f00cd6Erik        final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags);
4351146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        menu.setHeaderTitle(title);
4352e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4353146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numSelectedEvents = mSelectedEvents.size();
4354146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mNumDays == 1) {
4355146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Day view.
4356146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4357146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is a selected event, then allow it to be viewed and
4358146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // edited.
4359146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (numSelectedEvents >= 1) {
43604484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view);
4361146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4362146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_info_details);
4363146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4364ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                int accessLevel = getEventAccessLevel(mContext, mSelectedEvent);
4365a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel == ACCESS_LEVEL_EDIT) {
43664484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit);
4367146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4368146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_edit);
4369146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setAlphabeticShortcut('e');
4370a505627659f6bfba8869eac8089f7030f2d0d92cErik                }
4371146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4372a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel >= ACCESS_LEVEL_DELETE) {
43734484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete);
4374146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4375146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_delete);
4376146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4377146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
43784484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
4379146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4380146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_add);
4381146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setAlphabeticShortcut('n');
4382146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
4383146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                // Otherwise, if the user long-pressed on a blank hour, allow
438446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                // them to create an event. They can also do this by tapping.
43854484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
4386146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4387146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_add);
4388146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setAlphabeticShortcut('n');
4389146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4390146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
4391146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Week view.
4392e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4393146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If there is a selected event, then allow it to be viewed and
4394146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // edited.
4395146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (numSelectedEvents >= 1) {
43964484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view);
4397146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setOnMenuItemClickListener(mContextMenuHandler);
4398146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                item.setIcon(android.R.drawable.ic_menu_info_details);
4399146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4400ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan                int accessLevel = getEventAccessLevel(mContext, mSelectedEvent);
4401a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel == ACCESS_LEVEL_EDIT) {
44024484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit);
4403146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4404146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_edit);
4405146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setAlphabeticShortcut('e');
4406a505627659f6bfba8869eac8089f7030f2d0d92cErik                }
4407146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4408a505627659f6bfba8869eac8089f7030f2d0d92cErik                if (accessLevel >= ACCESS_LEVEL_DELETE) {
44094484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                    item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete);
4410146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setOnMenuItemClickListener(mContextMenuHandler);
4411146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    item.setIcon(android.R.drawable.ic_menu_delete);
4412146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
44134484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            }
4414146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
44154484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create);
44164484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setOnMenuItemClickListener(mContextMenuHandler);
44174484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setIcon(android.R.drawable.ic_menu_add);
44184484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setAlphabeticShortcut('n');
4419146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
44204484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item = menu.add(0, MENU_DAY, 0, R.string.show_day_view);
44214484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setOnMenuItemClickListener(mContextMenuHandler);
44224484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setIcon(android.R.drawable.ic_menu_day);
44234484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan            item.setAlphabeticShortcut('d');
4424146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4425146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4426146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mPopup.dismiss();
4427146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4428146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4429146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private class ContextMenuHandler implements MenuItem.OnMenuItemClickListener {
44304696e93697ae2316ef38ba9148e35052313162aeJames Kung
4431146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public boolean onMenuItemClick(MenuItem item) {
4432146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            switch (item.getItemId()) {
44334484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_VIEW: {
4434146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
4435a7694eead7cbb293990bc89c3906e17bad36194cErik                        mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT_DETAILS,
44369e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                                mSelectedEvent.id, mSelectedEvent.startMillis,
443787f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                mSelectedEvent.endMillis, 0, 0, -1);
4438146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4439146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4440146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
44414484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_EDIT: {
4442146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
44439e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                        mController.sendEventRelatedEvent(this, EventType.EDIT_EVENT,
44449e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                                mSelectedEvent.id, mSelectedEvent.startMillis,
444587f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                mSelectedEvent.endMillis, 0, 0, -1);
4446146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4447146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4448146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
44494484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_DAY: {
4450487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                    mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1,
44519e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                            ViewType.DAY);
4452146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4453146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
44544484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_AGENDA: {
4455487552be8ae3c1b148211c0fbf594d2b73727fd6Michael Chan                    mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1,
44569e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                            ViewType.AGENDA);
4457146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4458146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
44594484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_CREATE: {
4460146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long startMillis = getSelectedTimeInMillis();
4461146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS;
44629e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                    mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1,
446387f993ff96172f508d3f241f8a425f953c7eb739RoboErik                            startMillis, endMillis, 0, 0, -1);
4464146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4465146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
44664484b04707d5570a6de4a5a4b228a9bb273bbab7Michael Chan                case MENU_EVENT_DELETE: {
4467146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (mSelectedEvent != null) {
4468146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        Event selectedEvent = mSelectedEvent;
4469146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long begin = selectedEvent.startMillis;
4470146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long end = selectedEvent.endMillis;
4471146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        long id = selectedEvent.id;
44729e89dca0902d13fe27fd2680cc15cbb470e40288Michael Chan                        mController.sendEventRelatedEvent(this, EventType.DELETE_EVENT, id, begin,
447387f993ff96172f508d3f241f8a425f953c7eb739RoboErik                                end, 0, 0, -1);
4474146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4475146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    break;
4476146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4477146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                default: {
4478146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    return false;
4479146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4480146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4481146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return true;
4482146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4483146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4484146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4485a505627659f6bfba8869eac8089f7030f2d0d92cErik    private static int getEventAccessLevel(Context context, Event e) {
4486146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        ContentResolver cr = context.getContentResolver();
4487146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4488ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        int accessLevel = Calendars.CAL_ACCESS_NONE;
4489146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4490146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Get the calendar id for this event
4491146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Cursor cursor = cr.query(ContentUris.withAppendedId(Events.CONTENT_URI, e.id),
4492146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                new String[] { Events.CALENDAR_ID },
4493146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* selection */,
4494146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* selectionArgs */,
4495146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                null /* sort */);
4496e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4497e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        if (cursor == null) {
4498a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
4499146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4500e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4501e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        if (cursor.getCount() == 0) {
4502e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan            cursor.close();
4503a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
4504e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan        }
4505e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4506146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cursor.moveToFirst();
4507146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        long calId = cursor.getLong(0);
4508697bd6a8cec36cc75c72603674e57e4c6fe42dbfKen Shirriff        cursor.close();
4509146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4510146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Uri uri = Calendars.CONTENT_URI;
4511146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        String where = String.format(CALENDARS_WHERE, calId);
4512146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cursor = cr.query(uri, CALENDARS_PROJECTION, where, null, null);
4513146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
45146d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        String calendarOwnerAccount = null;
4515146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (cursor != null) {
4516146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            cursor.moveToFirst();
4517ced0eb67ef49c3203f72520c296e73d9cb4f654bMichael Chan            accessLevel = cursor.getInt(CALENDARS_INDEX_ACCESS_LEVEL);
45186d34cec523930368976bc8d358d0a6de1da3462cMichael Chan            calendarOwnerAccount = cursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT);
4519146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            cursor.close();
4520146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4521e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4522ef2add9145f058ea103ade2870a8ba899b9ed1c9RoboErik        if (accessLevel < Calendars.CAL_ACCESS_CONTRIBUTOR) {
4523a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_NONE;
45246d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        }
4525146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
45266d34cec523930368976bc8d358d0a6de1da3462cMichael Chan        if (e.guestsCanModify) {
4527a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_EDIT;
4528a505627659f6bfba8869eac8089f7030f2d0d92cErik        }
4529a505627659f6bfba8869eac8089f7030f2d0d92cErik
453046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (!TextUtils.isEmpty(calendarOwnerAccount)
453146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan                && calendarOwnerAccount.equalsIgnoreCase(e.organizer)) {
4532a505627659f6bfba8869eac8089f7030f2d0d92cErik            return ACCESS_LEVEL_EDIT;
4533146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4534146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4535a505627659f6bfba8869eac8089f7030f2d0d92cErik        return ACCESS_LEVEL_DELETE;
4536146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4537146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4538146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
4539146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position.
4540146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * If the touch position is not within the displayed grid, then this
4541146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * method returns false.
4542146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     *
4543146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param x the x position of the touch
4544146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @param y the y position of the touch
4545989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson     * @param keepOldSelection - do not change the selection info (used for invoking accessibility
4546989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson     *                           messages)
4547146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     * @return true if the touch position is valid
4548146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
4549989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private boolean setSelectionFromPosition(int x, final int y, boolean keepOldSelection) {
4550989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson
4551989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        Event savedEvent = null;
4552989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        int savedDay = 0;
4553989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        int savedHour = 0;
4554989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        boolean savedAllDay = false;
4555989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        if (keepOldSelection) {
4556989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            // Store selection info and restore it at the end. This way, we can invoke the
4557989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            // right accessibility message without affecting the selection.
4558989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            savedEvent = mSelectedEvent;
4559989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            savedDay = mSelectionDay;
4560989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            savedHour = mSelectionHour;
4561989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            savedAllDay = mSelectionAllday;
4562989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        }
4563146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (x < mHoursWidth) {
4564360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            x = mHoursWidth;
4565146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4566146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4567146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP);
4568146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (day >= mNumDays) {
4569146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            day = mNumDays - 1;
4570146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4571146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        day += mFirstJulianDay;
4572989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedDay(day);
4573360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan
4574360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        if (y < DAY_HEADER_HEIGHT) {
4575aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan            sendAccessibilityEventAsNeeded(false);
4576360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan            return false;
4577360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan        }
457846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
4579989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedHour(mFirstHour); /* First fully visible hour */
458046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
458146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan        if (y < mFirstCell) {
45822fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = true;
4583146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        } else {
458446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // y is now offset from top of the scrollable region
458555a26d7062d5aa1a1b001b7271bb137276ab807eErik            int adjustedY = y - mFirstCell;
458646a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
458755a26d7062d5aa1a1b001b7271bb137276ab807eErik            if (adjustedY < mFirstHourOffset) {
4588989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                setSelectedHour(mSelectionHour - 1); /* In the partially visible hour */
458946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            } else {
4590989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                setSelectedHour(mSelectionHour +
4591989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                        (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP));
459246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            }
459346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
45942fda24534978f55711526dbde747aa50d68dec40RoboErik            mSelectionAllday = false;
4595146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4596360c67c9227c3e75cb4ea9b7350e3ed44b504f03Michael Chan
4597146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        findSelectedEvent(x, y);
459846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan
459946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//        Log.i("Cal", "setSelectionFromPosition( " + x + ", " + y + " ) day: " + day + " hour: "
460046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                + mSelectionHour + " mFirstCell: " + mFirstCell + " mFirstHourOffset: "
460146a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                + mFirstHourOffset);
4602146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//        if (mSelectedEvent != null) {
460346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//            Log.i("Cal", "  num events: " + mSelectedEvents.size() + " event: "
460446a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                    + mSelectedEvent.title);
4605146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//            for (Event ev : mSelectedEvents) {
4606146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL
4607146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                        | DateUtils.FORMAT_CAP_NOON_MIDNIGHT;
460846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan//                String timeRange = formatDateRange(mContext, ev.startMillis, ev.endMillis, flags);
4609146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//
4610146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//                Log.i("Cal", "  " + timeRange + " " + ev.title);
4611146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//            }
4612146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project//        }
4613aeae98bf8f8c5c544f65f5b6b7f29cfd31366ad5Michael Chan        sendAccessibilityEventAsNeeded(true);
4614989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson
4615989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        // Restore old values
4616989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        if (keepOldSelection) {
4617989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            mSelectedEvent = savedEvent;
4618989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            mSelectionDay = savedDay;
4619989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            mSelectionHour = savedHour;
4620989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            mSelectionAllday = savedAllDay;
4621989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        }
4622146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        return true;
4623146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4624146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4625146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private void findSelectedEvent(int x, int y) {
4626146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int date = mSelectionDay;
4627146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int cellWidth = mCellWidth;
4628b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik        ArrayList<Event> events = mEvents;
4629146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int numEvents = events.size();
4630e9cd8f4513c141ca486bbd141f0fe34bb3c27f3aRoboErik        int left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay);
4631146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        int top = 0;
4632989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        setSelectedEvent(null);
4633146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4634146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        mSelectedEvents.clear();
46352fda24534978f55711526dbde747aa50d68dec40RoboErik        if (mSelectionAllday) {
4636146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            float yDistance;
463746a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            float minYdistance = 10000.0f; // any large number
4638146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event closestEvent = null;
46392fda24534978f55711526dbde747aa50d68dec40RoboErik            float drawHeight = mAlldayHeight;
46403eea5ad7329f522d9fe091ca51ad09b809bba34bMichael Chan            int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
46412fda24534978f55711526dbde747aa50d68dec40RoboErik            int maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount;
46422fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
46432fda24534978f55711526dbde747aa50d68dec40RoboErik                // Leave a gap for the 'box +n' text
46442fda24534978f55711526dbde747aa50d68dec40RoboErik                maxUnexpandedColumn--;
46452fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4646b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik            events = mAllDayEvents;
4647b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik            numEvents = events.size();
4648146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int i = 0; i < numEvents; i++) {
4649146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event event = events.get(i);
46502fda24534978f55711526dbde747aa50d68dec40RoboErik                if (!event.drawAsAllday() ||
46512fda24534978f55711526dbde747aa50d68dec40RoboErik                        (!mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn)) {
46522fda24534978f55711526dbde747aa50d68dec40RoboErik                    // Don't check non-allday events or events that aren't shown
4653146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    continue;
4654146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4655146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4656146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) {
4657b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik                    float numRectangles = mShowAllAllDayEvents ? mMaxAlldayEvents
4658b5e3c17201b2c08ca9f3fd33f4585e1ec8fa71b1RoboErik                            : mMaxUnexpandedAlldayEventCount;
4659146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float height = drawHeight / numRectangles;
4660f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                    if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
4661f056fac66fe2845bfd88e9571f8fceb9180264a0Michael Chan                        height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
4662146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4663146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float eventTop = yOffset + height * event.getColumn();
4664146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    float eventBottom = eventTop + height;
4665146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    if (eventTop < y && eventBottom > y) {
4666146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // If the touch is inside the event rectangle, then
4667146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // add the event.
4668146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        mSelectedEvents.add(event);
4669146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        closestEvent = event;
4670146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        break;
4671146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    } else {
4672146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        // Find the closest event
4673146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (eventTop >= y) {
4674146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            yDistance = eventTop - y;
4675146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        } else {
4676146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            yDistance = y - eventBottom;
4677146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
4678146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        if (yDistance < minYdistance) {
4679146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            minYdistance = yDistance;
4680146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                            closestEvent = event;
4681146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                        }
4682146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    }
4683146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4684146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4685989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedEvent(closestEvent);
4686146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            return;
4687146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4688146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4689146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Adjust y for the scrollable bitmap
4690146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        y += mViewStartY - mFirstCell;
4691146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4692146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Use a region around (x,y) for the selection region
4693146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        Rect region = mRect;
4694146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.left = x - 10;
4695146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.right = x + 10;
4696146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.top = y - 10;
4697146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        region.bottom = y + 10;
4698146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4699146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        EventGeometry geometry = mEventGeometry;
4700146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4701146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        for (int i = 0; i < numEvents; i++) {
4702146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event event = events.get(i);
4703146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Compute the event rectangle.
4704146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
4705146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                continue;
4706146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4707146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4708146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // If the event intersects the selection region, then add it to
4709146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // mSelectedEvents.
4710146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (geometry.eventIntersectsSelection(event, region)) {
4711146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mSelectedEvents.add(event);
4712146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4713146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4714146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4715146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // If there are any events in the selected region, then assign the
4716146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // closest one to mSelectedEvent.
4717146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mSelectedEvents.size() > 0) {
4718146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int len = mSelectedEvents.size();
4719146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            Event closestEvent = null;
472046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            float minDist = mViewWidth + mViewHeight; // some large distance
4721146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            for (int index = 0; index < len; index++) {
4722146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                Event ev = mSelectedEvents.get(index);
4723146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                float dist = geometry.pointToEvent(x, y, ev);
4724146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                if (dist < minDist) {
4725146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    minDist = dist;
4726146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                    closestEvent = ev;
4727146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                }
4728146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4729989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson            setSelectedEvent(closestEvent);
4730146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4731146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Keep the selected hour and day consistent with the selected
473246a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // event. They could be different if we touched on an empty hour
473346a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan            // slot very close to an event in the previous hour slot. In
4734146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // that case we will select the nearby event.
4735146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startDay = mSelectedEvent.startDay;
4736146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endDay = mSelectedEvent.endDay;
4737146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectionDay < startDay) {
4738989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                setSelectedDay(startDay);
4739146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else if (mSelectionDay > endDay) {
4740989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                setSelectedDay(endDay);
4741146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4742146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4743146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int startHour = mSelectedEvent.startTime / 60;
4744146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            int endHour;
4745146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mSelectedEvent.startTime < mSelectedEvent.endTime) {
4746146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endHour = (mSelectedEvent.endTime - 1) / 60;
4747146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            } else {
4748146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                endHour = mSelectedEvent.endTime / 60;
4749146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4750146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4751349dd91e8475a72eb68a5eae4482ce7e82c01d16RoboErik            if (mSelectionHour < startHour && mSelectionDay == startDay) {
4752989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                setSelectedHour(startHour);
4753349dd91e8475a72eb68a5eae4482ce7e82c01d16RoboErik            } else if (mSelectionHour > endHour && mSelectionDay == endDay) {
4754989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson                setSelectedHour(endHour);
4755146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4756146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4757146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4758146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4759146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // Encapsulates the code to continue the scrolling after the
476046a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    // finger is lifted. Instead of stopping the scroll immediately,
4761146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    // the scroll continues to "free spin" and gradually slows down.
4762146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    private class ContinueScroll implements Runnable {
47634696e93697ae2316ef38ba9148e35052313162aeJames Kung
4764146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
47653f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            mScrolling = mScrolling && mScroller.computeScrollOffset();
4766ea3f117db073ae796453479d00e9c7664d4471c5RoboErik            if (!mScrolling || mPaused) {
47673f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                resetSelectedHour();
47683f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                invalidate();
47693f6328f579b05c02606572c777541b97aa04f9f3Michael Chan                return;
4770146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4771146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
47723f6328f579b05c02606572c777541b97aa04f9f3Michael Chan            mViewStartY = mScroller.getCurrY();
4773146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4774433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mCallEdgeEffectOnAbsorb) {
4775433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (mViewStartY < 0) {
4776433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectTop.onAbsorb((int) mLastVelocity);
4777433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mCallEdgeEffectOnAbsorb = false;
4778433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                } else if (mViewStartY > mMaxViewStartY) {
4779433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mEdgeEffectBottom.onAbsorb((int) mLastVelocity);
4780433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mCallEdgeEffectOnAbsorb = false;
4781433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
4782433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                mLastVelocity = mScroller.getCurrVelocity();
4783433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            }
4784433ba924de1ac26b923e2beb061812012749e4f3Michael Chan
4785433ba924de1ac26b923e2beb061812012749e4f3Michael Chan            if (mScrollStartY == 0 || mScrollStartY == mMaxViewStartY) {
4786433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                // Allow overscroll/springback only on a fling,
4787433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                // not a pull/fling from the end
4788433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                if (mViewStartY < 0) {
4789433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mViewStartY = 0;
4790433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                } else if (mViewStartY > mMaxViewStartY) {
4791433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                    mViewStartY = mMaxViewStartY;
4792433ba924de1ac26b923e2beb061812012749e4f3Michael Chan                }
4793146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4794146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4795146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            computeFirstHour();
4796ea3f117db073ae796453479d00e9c7664d4471c5RoboErik            mHandler.post(this);
4797146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            invalidate();
4798146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4799146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4800146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4801146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    /**
4802bd0e834aea317236d574f8738fe11a51fdc87968Erik     * Cleanup the pop-up and timers.
4803146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project     */
4804146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    public void cleanup() {
4805146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        // Protect against null-pointer exceptions
4806146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        if (mPopup != null) {
4807146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            mPopup.dismiss();
4808146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4809ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        mPaused = true;
4810666fc5e8ddbac15c951aad738bd818ee307eae42Erik        mLastPopupEventID = INVALID_EVENT_ID;
481158f5055e1f3740382b4507e6eb88eebc2d6c21c1Michael Chan        if (mHandler != null) {
481258f5055e1f3740382b4507e6eb88eebc2d6c21c1Michael Chan            mHandler.removeCallbacks(mDismissPopup);
481358f5055e1f3740382b4507e6eb88eebc2d6c21c1Michael Chan            mHandler.removeCallbacks(mUpdateCurrentTime);
4814146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4815e2ae1ef8decfddcc4e5802483e92cab93c6fc67cMichael Chan
4816d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan        Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT,
4817d885c1a5876735bbf2c0086956101bd70bac37e7Michael Chan            mCellHeight);
4818056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson        // Clear all click animations
4819ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        eventClickCleanup();
48207abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project        // Turn off redraw
48217abd8566e5718c43d610e046495bcb412e87847fThe Android Open Source Project        mRemeasure = false;
482284fbd42c4ea8bae2a0edbd49333cb7112bf3cd51Isaac Katzenelson        // Turn off scrolling to make sure the view is in the correct state if we fling back to it
482384fbd42c4ea8bae2a0edbd49333cb7112bf3cd51Isaac Katzenelson        mScrolling = false;
4824146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4825146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4826ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    private void eventClickCleanup() {
4827ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        this.removeCallbacks(mClearClick);
4828ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        this.removeCallbacks(mSetClick);
4829ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        mClickedEvent = null;
4830ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson        mSavedClickedEvent = null;
4831ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson    }
4832ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson
4833989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private void setSelectedEvent(Event e) {
4834989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mSelectedEvent = e;
4835989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mSelectedEventForAccessibility = e;
4836989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    }
4837989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson
4838989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private void setSelectedHour(int h) {
4839989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mSelectionHour = h;
4840989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mSelectionHourForAccessibility = h;
4841989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    }
4842989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    private void setSelectedDay(int d) {
4843989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mSelectionDay = d;
4844989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson        mSelectionDayForAccessibility = d;
4845989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson    }
4846989a7fe5215b378bb6dac7eae7d9970056e30c4fIsaac Katzenelson
4847bd0e834aea317236d574f8738fe11a51fdc87968Erik    /**
4848bd0e834aea317236d574f8738fe11a51fdc87968Erik     * Restart the update timer
4849bd0e834aea317236d574f8738fe11a51fdc87968Erik     */
4850bd0e834aea317236d574f8738fe11a51fdc87968Erik    public void restartCurrentTimeUpdates() {
4851ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        mPaused = false;
4852ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        if (mHandler != null) {
48535d740e1320ba48ba66b72430f9896ab7d481fb54Michael Chan            mHandler.removeCallbacks(mUpdateCurrentTime);
4854ea3f117db073ae796453479d00e9c7664d4471c5RoboErik            mHandler.post(mUpdateCurrentTime);
4855ea3f117db073ae796453479d00e9c7664d4471c5RoboErik        }
4856bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
4857bd0e834aea317236d574f8738fe11a51fdc87968Erik
485846a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    @Override
485946a8b1117b0510a8dfd50586d5d22aa86b8dc1b9Michael Chan    protected void onDetachedFromWindow() {
4860146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        cleanup();
4861146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        super.onDetachedFromWindow();
4862146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4863146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project
4864146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    class DismissPopup implements Runnable {
48654696e93697ae2316ef38ba9148e35052313162aeJames Kung
4866146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        public void run() {
4867146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            // Protect against null-pointer exceptions
4868146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            if (mPopup != null) {
4869146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project                mPopup.dismiss();
4870146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project            }
4871146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project        }
4872146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project    }
4873bd0e834aea317236d574f8738fe11a51fdc87968Erik
4874bd0e834aea317236d574f8738fe11a51fdc87968Erik    class UpdateCurrentTime implements Runnable {
48754696e93697ae2316ef38ba9148e35052313162aeJames Kung
4876bd0e834aea317236d574f8738fe11a51fdc87968Erik        public void run() {
4877bd0e834aea317236d574f8738fe11a51fdc87968Erik            long currentTime = System.currentTimeMillis();
4878bd0e834aea317236d574f8738fe11a51fdc87968Erik            mCurrentTime.set(currentTime);
4879bd0e834aea317236d574f8738fe11a51fdc87968Erik            //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.)
48805d740e1320ba48ba66b72430f9896ab7d481fb54Michael Chan            if (!DayView.this.mPaused) {
4881ea3f117db073ae796453479d00e9c7664d4471c5RoboErik                mHandler.postDelayed(mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY
4882ea3f117db073ae796453479d00e9c7664d4471c5RoboErik                        - (currentTime % UPDATE_CURRENT_TIME_DELAY));
48835d740e1320ba48ba66b72430f9896ab7d481fb54Michael Chan            }
4884bd0e834aea317236d574f8738fe11a51fdc87968Erik            mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
4885bd0e834aea317236d574f8738fe11a51fdc87968Erik            invalidate();
4886bd0e834aea317236d574f8738fe11a51fdc87968Erik        }
4887bd0e834aea317236d574f8738fe11a51fdc87968Erik    }
4888ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4889ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
4890ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4891ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onSingleTapUp(MotionEvent ev) {
48929ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp");
4893d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doSingleTapUp(ev);
4894ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4895ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4896ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4897ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4898ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public void onLongPress(MotionEvent ev) {
48999ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress");
4900d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doLongPress(ev);
4901ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4902ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4903ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4904ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
49059ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onScroll");
4906ca0955e1dbd3176cde0d012552efdca7b5e336d6Isaac Katzenelson            eventClickCleanup();
49072fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mTouchStartedInAlldayArea) {
49080e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                if (Math.abs(distanceX) < Math.abs(distanceY)) {
4909056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    // Make sure that click feedback is gone when you scroll from the
4910056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    // all day area
4911056c51ec65d03c19348fb64b65a00835e5ecd600Isaac Katzenelson                    invalidate();
49120e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                    return false;
49130e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                }
49140e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                // don't scroll vertically if this started in the allday area
49150e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                distanceY = 0;
49162fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4917d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doScroll(e1, e2, distanceX, distanceY);
4918ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4919ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4920ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4921ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4922ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
49239ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onFling");
49249ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan
49252fda24534978f55711526dbde747aa50d68dec40RoboErik            if (mTouchStartedInAlldayArea) {
49260e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                if (Math.abs(velocityX) < Math.abs(velocityY)) {
49270e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                    return false;
49280e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                }
49290e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                // don't fling vertically if this started in the allday area
49300e9e8a522740e4083f944638a82d5bd7a6e89fa8Erik                velocityY = 0;
49312fda24534978f55711526dbde747aa50d68dec40RoboErik            }
4932d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doFling(e1, e2, velocityX, velocityY);
4933ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4934ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4935ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan
4936ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        @Override
4937ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        public boolean onDown(MotionEvent ev) {
49389ddc5e29f501e423e873c4a1d4cfb7e810d63ccfMichael Chan            if (DEBUG) Log.e(TAG, "GestureDetector.onDown");
4939d190418220467c02327dd9b3ae1eae238889720bMichael Chan            DayView.this.doDown(ev);
4940ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan            return true;
4941ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan        }
4942ef1a51d6dda6dd8a013bf0937f10c6eb2041b888Michael Chan    }
4943cd405a28e6821b116dcf3c531805d0c90b6cc08cErik
4944cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    @Override
4945cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    public boolean onLongClick(View v) {
494643e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        int flags = DateUtils.FORMAT_SHOW_WEEKDAY;
494743e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        long time = getSelectedTimeInMillis();
494843e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        if (!mSelectionAllday) {
494943e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik            flags |= DateUtils.FORMAT_SHOW_TIME;
495043e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        }
495143e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        if (DateFormat.is24HourFormat(mContext)) {
495243e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik            flags |= DateUtils.FORMAT_24HOUR;
495343e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        }
495443e794f69384bf8fd2370e71aceb1fb58d6cf958RoboErik        mLongPressTitle = Utils.formatDateRange(mContext, time, time, flags);
495520b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik        new AlertDialog.Builder(mContext).setTitle(mLongPressTitle)
495620b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                .setItems(mLongPressItems, new DialogInterface.OnClickListener() {
495720b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                    @Override
495820b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                    public void onClick(DialogInterface dialog, int which) {
495920b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                        if (which == 0) {
4960edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            long extraLong = 0;
4961edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            if (mSelectionAllday) {
4962edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                                extraLong = CalendarController.EXTRA_CREATE_ALL_DAY;
4963edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            }
4964edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                            mController.sendEventRelatedEventWithExtra(this,
4965edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                                    EventType.CREATE_EVENT, -1, getSelectedTimeInMillis(), 0, -1,
4966edecd9a97e02ff14bd5abeec3414e1bf3cc631dfMichael Chan                                    -1, extraLong, -1);
496720b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                        }
496820b6fb580bf09dbd610a256ffd3165c03053cb70RoboErik                    }
496973bb3ae73b067e7c926d3ac660d059b2a06309b2RoboErik                }).show().setCanceledOnTouchOutside(true);
4970cd405a28e6821b116dcf3c531805d0c90b6cc08cErik        return true;
4971cd405a28e6821b116dcf3c531805d0c90b6cc08cErik    }
49722c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
49732c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    // The rest of this file was borrowed from Launcher2 - PagedView.java
49742c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private static final int MINIMUM_SNAP_VELOCITY = 2200;
49752c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
497695d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan    private class ScrollInterpolator implements Interpolator {
49772c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        public ScrollInterpolator() {
49782c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
49792c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
49802c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        public float getInterpolation(float t) {
49812c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            t -= 1.0f;
498295d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            t = t * t * t * t * t + 1;
498395d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
498495d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            if ((1 - t) * mAnimationDistance < 1) {
498595d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan                cancelAnimation();
498695d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            }
498795d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan
498895d9f9172802f19e28b1cabf39b371c4ac343dc2Michael Chan            return t;
49892c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
49902c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
49912c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
49922c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private long calculateDuration(float delta, float width, float velocity) {
49932c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        /*
49942c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * Here we compute a "distance" that will be used in the computation of
49952c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * the overall snap duration. This is a function of the actual distance
49962c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * that needs to be traveled; we keep this value close to half screen
49972c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * size in order to reduce the variance in snap duration as a function
49982c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * of the distance the page needs to travel.
49992c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         */
50002c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        final float halfScreenSize = width / 2;
50012c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distanceRatio = delta / width;
50022c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio);
50032c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        float distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration;
50042c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
50052c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        velocity = Math.abs(velocity);
50062c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
50072c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
50082c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        /*
50092c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * we want the page's snap velocity to approximately match the velocity
50102c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * at which the user flings, so we scale the duration by a value near to
50112c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to
50122c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         * make it a little slower.
50132c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan         */
50142c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        long duration = 6 * Math.round(1000 * Math.abs(distance / velocity));
50152c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        if (DEBUG) {
50162c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan            Log.e(TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:"
50172c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + distanceRatio + " distance:" + distance + " velocity:" + velocity
50182c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + " duration:" + duration + " distanceInfluenceForSnapDuration:"
50192c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan                    + distanceInfluenceForSnapDuration);
50202c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        }
50212c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        return duration;
50222c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
50232c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan
50242c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    /*
50252c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * We want the duration of the page snap animation to be influenced by the
50262c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * distance that the screen has to travel, however, we don't want this
50272c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * duration to be effected in a purely linear fashion. Instead, we use this
50282c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * method to moderate the effect that the distance of travel has on the
50292c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     * overall snap duration.
50302c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan     */
50312c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    private float distanceInfluenceForSnapDuration(float f) {
50322c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        f -= 0.5f; // center the values about 0.
50332c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        f *= 0.3f * Math.PI / 2.0f;
50342c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan        return (float) Math.sin(f);
50352c0a3ca970425b0237cd9d99b943a9cd17529692Michael Chan    }
5036146de36083f6ce8b7e8a1f974d3990594a36bfecThe Android Open Source Project}
5037