DayView.java revision 000ba1109e2f4e527e9227fe341598ceeb3fbca6
15f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/* 25f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Copyright (C) 2007 The Android Open Source Project 35f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 45f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Licensed under the Apache License, Version 2.0 (the "License"); 50bc735ffcfb223c0186419547abaa5c84482663eChris Lattner * you may not use this file except in compliance with the License. 60bc735ffcfb223c0186419547abaa5c84482663eChris Lattner * You may obtain a copy of the License at 75f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 85f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * http://www.apache.org/licenses/LICENSE-2.0 95f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Unless required by applicable law or agreed to in writing, software 115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * distributed under the License is distributed on an "AS IS" BASIS, 125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * See the License for the specific language governing permissions and 145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * limitations under the License. 155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 177573098b83e780d1c5bea13b384b610d8f155676Steve Naroffpackage com.android.calendar; 18a95d3750441ac8ad03e36af8e6e74039c9a3109dTed Kremenek 195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport com.android.calendar.CalendarController.EventType; 209caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenekimport com.android.calendar.CalendarController.ViewType; 218ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek 225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.content.ContentResolver; 23525204a7ca5c3c0aac8166d8f27abb988a84c850Anton Korobeynikovimport android.content.ContentUris; 240a449eed1dd2439b4b9c0a6291084816eab390c1Ted Kremenekimport android.content.Context; 258189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenekimport android.content.res.Resources; 266e340496341a4704be0ede9c1ff4f8eacea7ee2cChris Lattnerimport android.content.res.TypedArray; 277573098b83e780d1c5bea13b384b610d8f155676Steve Naroffimport android.database.Cursor; 287573098b83e780d1c5bea13b384b610d8f155676Steve Naroffimport android.graphics.Canvas; 295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.graphics.Paint; 30e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishopimport android.graphics.Paint.Style; 315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.graphics.Rect; 325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.graphics.Typeface; 337ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroffimport android.graphics.drawable.Drawable; 344b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redlimport android.net.Uri; 355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.os.Handler; 360c727a35718556866a978f64ac549d9798735f08Chris Lattnerimport android.provider.Calendar.Attendees; 376a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlssonimport android.provider.Calendar.Calendars; 386c36be5b383875b490684bcf439d6d427298c1afChris Lattnerimport android.provider.Calendar.Events; 3942a509f6a4f71bb805cc4abbb26722a34dffdddeTed Kremenekimport android.text.Layout.Alignment; 40ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.text.StaticLayout; 41ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.text.TextPaint; 42ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.text.TextUtils; 43ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.text.format.DateFormat; 44ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.text.format.DateUtils; 45ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.text.format.Time; 46ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.util.Log; 47ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.ContextMenu; 48ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.ContextMenu.ContextMenuInfo; 49ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.GestureDetector; 50ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.Gravity; 51ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.KeyEvent; 52ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.LayoutInflater; 53ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.MenuItem; 54ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.MotionEvent; 55ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.ScaleGestureDetector; 56ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.View; 57ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.ViewConfiguration; 58ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.ViewGroup; 59ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.WindowManager; 60ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.animation.Animation; 61ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.view.animation.TranslateAnimation; 62ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.widget.ImageView; 63ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.widget.PopupWindow; 64ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.widget.TextView; 65ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport android.widget.ViewSwitcher; 66ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 67ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport java.util.ArrayList; 68ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport java.util.Arrays; 69ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport java.util.Calendar; 70ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport java.util.regex.Matcher; 71ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekimport java.util.regex.Pattern; 72ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 73ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek/** 74ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek * View for multi-day view. So far only 1 and 7 day have been tested. 75ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek */ 76ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenekpublic class DayView extends View implements View.OnCreateContextMenuListener, 77ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener { 78ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static String TAG = "DayView"; 79ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static boolean DEBUG = false; 80ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 81ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static float mScale = 0; // Used for supporting different screen densities 82ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event 83ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final long ANIMATION_DURATION = 400; 84ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 85ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final int MENU_AGENDA = 2; 86ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final int MENU_DAY = 3; 87ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final int MENU_EVENT_VIEW = 5; 88ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final int MENU_EVENT_CREATE = 6; 89ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final int MENU_EVENT_EDIT = 7; 90ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek private static final int MENU_EVENT_DELETE = 8; 915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int DEFAULT_CELL_HEIGHT = 64; 935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int MAX_CELL_HEIGHT = 150; 945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int MIN_Y_SPAN = 100; 955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 96f2cad8633e46cce12fc3d77c0bd451ffd7264bafDouglas Gregor private boolean mOnFlingCalled; 97f2cad8633e46cce12fc3d77c0bd451ffd7264bafDouglas Gregor /** 98f2cad8633e46cce12fc3d77c0bd451ffd7264bafDouglas Gregor * ID of the last event which was displayed with the toast popup. 99f2cad8633e46cce12fc3d77c0bd451ffd7264bafDouglas Gregor * 100f2cad8633e46cce12fc3d77c0bd451ffd7264bafDouglas Gregor * This is used to prevent popping up multiple quick views for the same event, especially 101f2cad8633e46cce12fc3d77c0bd451ffd7264bafDouglas Gregor * during calendar syncs. This becomes valid when an event is selected, either by default 1025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * on starting calendar or by scrolling to an event. It becomes invalid when the user 1035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * explicitly scrolls to an empty time slot, changes views, or deletes the event. 1045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 1055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private long mLastPopupEventID; 1068189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 1078189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek protected Context mContext; 1088189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 1098189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final String[] CALENDARS_PROJECTION = new String[] { 1108189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek Calendars._ID, // 0 1118189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek Calendars.ACCESS_LEVEL, // 1 1128189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek Calendars.OWNER_ACCOUNT, // 2 1138189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek }; 1148189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int CALENDARS_INDEX_ACCESS_LEVEL = 1; 1158189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2; 1168189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final String CALENDARS_WHERE = Calendars._ID + "=%d"; 1178189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 1188189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final String[] ATTENDEES_PROJECTION = new String[] { 1198189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek Attendees._ID, // 0 1208189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek Attendees.ATTENDEE_RELATIONSHIP, // 1 1218189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek }; 1228189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int ATTENDEES_INDEX_RELATIONSHIP = 1; 1238189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=%d"; 1248189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 1258189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int FROM_NONE = 0; 1268189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int FROM_ABOVE = 1; 1278189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int FROM_BELOW = 2; 1288189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int FROM_LEFT = 4; 1298189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int FROM_RIGHT = 8; 1308189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 1318189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int ACCESS_LEVEL_NONE = 0; 1328189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int ACCESS_LEVEL_DELETE = 1; 133e2dedf8f61b8f306f704781456b482eb61871e8eDouglas Gregor private static final int ACCESS_LEVEL_EDIT = 2; 134fb523e16dd1f860ff02a3ae03e5e3e25327a5860Chris Lattner 135fb523e16dd1f860ff02a3ae03e5e3e25327a5860Chris Lattner private static int mHorizontalSnapBackThreshold = 128; 136e2dedf8f61b8f306f704781456b482eb61871e8eDouglas Gregor private static int HORIZONTAL_FLING_THRESHOLD = 75; 137fb523e16dd1f860ff02a3ae03e5e3e25327a5860Chris Lattner 138e2dedf8f61b8f306f704781456b482eb61871e8eDouglas Gregor private ContinueScroll mContinueScroll = new ContinueScroll(); 139025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 140025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor // Make this visible within the package for more informative debugging 141025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor Time mBaseDate; 142025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private Time mCurrentTime; 143025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor //Update the current time line every five minutes if the window is left open that long 144025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private static final int UPDATE_CURRENT_TIME_DELAY = 300000; 1459c1863ef36a74e8203f00289d19856ad956f48b9Ted Kremenek private UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime(); 1469c1863ef36a74e8203f00289d19856ad956f48b9Ted Kremenek private int mTodayJulianDay; 1479c1863ef36a74e8203f00289d19856ad956f48b9Ted Kremenek 14827f8a28bee33bb0e857cfe1a61c281bbc234b338Ted Kremenek private Typeface mBold = Typeface.DEFAULT_BOLD; 1499c1863ef36a74e8203f00289d19856ad956f48b9Ted Kremenek private int mFirstJulianDay; 150025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private int mLastJulianDay; 151025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 152025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private int mMonthLength; 153025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private int mFirstVisibleDate; 154025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private int mFirstVisibleDayOfWeek; 1555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int[] mEarliestStartHour; // indexed by the week day offset 1565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private boolean[] mHasAllDayEvent; // indexed by the week day offset 1575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private Runnable mTZUpdater = new Runnable() { 1595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @Override 16027f8a28bee33bb0e857cfe1a61c281bbc234b338Ted Kremenek public void run() { 16124e1e707b4c362f18e371e2bbf054a8345b57bfaChris Lattner String tz = Utils.getTimeZone(mContext, this); 1625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mBaseDate.timezone = tz; 1635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mBaseDate.normalize(true); 1645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mCurrentTime.switchTimezone(tz); 165b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff invalidate(); 166b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 167b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff }; 168b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 169b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff /** 170311ff02fae0392bee6abe7723cdf5a69b2899a47Chris Lattner * This variable helps to avoid unnecessarily reloading events by keeping 171311ff02fae0392bee6abe7723cdf5a69b2899a47Chris Lattner * track of the start millis parameter used for the most recent loading 1725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * of events. If the next reload matches this, then the events are not 1735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * reloaded. To force a reload, set this to zero (this is set to zero 1745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * in the method clearCachedEvents()). 1755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 1765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private long mLastReloadMillis; 1776000dace22f110d8768476989313e9d981d690d0Chris Lattner 1786000dace22f110d8768476989313e9d981d690d0Chris Lattner private ArrayList<Event> mEvents = new ArrayList<Event>(); 1796000dace22f110d8768476989313e9d981d690d0Chris Lattner private ArrayList<Event> mAllDayEvents = new ArrayList<Event>(); 1806000dace22f110d8768476989313e9d981d690d0Chris Lattner private StaticLayout[] mLayouts = null; 1815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private StaticLayout[] mAllDayLayouts = null; 182e300c870f08d08badf2ebcb53ded49f304af37fcChris Lattner private int mSelectionDay; // Julian day 1836000dace22f110d8768476989313e9d981d690d0Chris Lattner private int mSelectionHour; 1846000dace22f110d8768476989313e9d981d690d0Chris Lattner 1856000dace22f110d8768476989313e9d981d690d0Chris Lattner boolean mSelectionAllDay; 186e300c870f08d08badf2ebcb53ded49f304af37fcChris Lattner 1876000dace22f110d8768476989313e9d981d690d0Chris Lattner /** Width of a day or non-conflicting event */ 1886000dace22f110d8768476989313e9d981d690d0Chris Lattner private int mCellWidth; 1896000dace22f110d8768476989313e9d981d690d0Chris Lattner 1906000dace22f110d8768476989313e9d981d690d0Chris Lattner // Pre-allocate these objects and re-use them 191071e4da10b07ca5278a7ce9dcabd853e0d57e5a4Mike Stump private Rect mRect = new Rect(); 192071e4da10b07ca5278a7ce9dcabd853e0d57e5a4Mike Stump private Rect mDestRect = new Rect(); 1935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private Paint mPaint = new Paint(); 194d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek private Paint mEventTextPaint = new Paint(); 195d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek private Paint mSelectionPaint = new Paint(); 196d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek private float[] mLines; 197d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek 1985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mFirstDayOfWeek; // First day of the week 1998297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 20077ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private PopupWindow mPopup; 201d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private View mPopupView; 202d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek 203d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek // The number of milliseconds to show the popup window 204d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private static final int POPUP_DISMISS_DELAY = 3000; 205d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private DismissPopup mDismissPopup = new DismissPopup(); 206d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek 207d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private boolean mRemeasure = true; 20877ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek 20977ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private final EventLoader mEventLoader; 21077ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek protected final EventGeometry mEventGeometry; 2119caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek 2129caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek private static final float GRID_LINE_INNER_WIDTH = 1; 21377ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private static final float GRID_LINE_WIDTH = 5; 2148297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 2158297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private static final int DAY_GAP = 1; 21677ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private static final int HOUR_GAP = 1; 21777ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private static int SINGLE_ALLDAY_HEIGHT = 34; 2189caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek private static int MAX_ALLDAY_HEIGHT = 105; 21977ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private static int ALLDAY_TOP_MARGIN = 3; 22077ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34; 22177ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek 2229caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek private static final int HOURS_LEFT_MARGIN = 2; 22377ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private static final int HOURS_RIGHT_MARGIN = 4; 2240b7489194f9f89fac39d57211c1e7953ae50251fDouglas Gregor private static final int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN; 2252dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek 226e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop private static int CURRENT_TIME_LINE_HEIGHT = 2; 227e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop private static int CURRENT_TIME_LINE_BORDER_WIDTH = 1; 228ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek private static final int CURRENT_TIME_LINE_SIDE_BUFFER = 2; 2292dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek 2302dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek /* package */ static final int MINUTES_PER_HOUR = 60; 2312dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24; 2325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000; 2335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000); 2345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24; 2355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static final int DAY_HEADER_ALPHA = 0x26000000; 2375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static final int DAY_HEADER_TODAY_ALPHA = 0x99000000; 2385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static float DAY_HEADER_ONE_DAY_LEFT_MARGIN = -12; 2395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static float DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5; 2409653db7bd0e3665b955a0445859285f2e1e7dacdDouglas Gregor private static float DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6; 24181c018d9482e7cc2addadc6202dcf162a01faefdChris Lattner private static float DAY_HEADER_LEFT_MARGIN = 5; 2425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static float DAY_HEADER_RIGHT_MARGIN = 7; 2439653db7bd0e3665b955a0445859285f2e1e7dacdDouglas Gregor private static float DAY_HEADER_BOTTOM_MARGIN = 3; 2448ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek private static float DAY_HEADER_FONT_SIZE = 14; 2458ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek private static float DATE_HEADER_FONT_SIZE = 24; 2465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static float NORMAL_FONT_SIZE = 12; 24784f2170062014d268951902164bed0d8bdea0e82Douglas Gregor private static float EVENT_TEXT_FONT_SIZE = 12; 24884f2170062014d268951902164bed0d8bdea0e82Douglas Gregor private static float HOURS_FONT_SIZE = 12; 24984f2170062014d268951902164bed0d8bdea0e82Douglas Gregor private static float AMPM_FONT_SIZE = 9; 2508e355f296a3ba45b94de49dbb18ae6a6e67df04cTed Kremenek private static int MIN_CELL_WIDTH_FOR_TEXT = 27; 2518e355f296a3ba45b94de49dbb18ae6a6e67df04cTed Kremenek private static final int MAX_EVENT_TEXT_LEN = 500; 2527e24e82a70a2c681f4291a3397bcd1e1005f251aChris Lattner private static float MIN_EVENT_HEIGHT = 15.0F; // in pixels 253fe95deaf66e4fbd82d44b5f6afa8162fa69cb85cChris Lattner private static int CALENDAR_COLOR_SQUARE_SIZE = 10; 2547e24e82a70a2c681f4291a3397bcd1e1005f251aChris Lattner private static int CALENDAR_COLOR_SQUARE_V_OFFSET = -1; 255fe95deaf66e4fbd82d44b5f6afa8162fa69cb85cChris Lattner private static int CALENDAR_COLOR_SQUARE_H_OFFSET = -3; 2568ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek private static int EVENT_RECT_TOP_MARGIN = -1; 257655137a6c19110620d82d5f03cdcaec3a48c059fTed Kremenek private static int EVENT_RECT_BOTTOM_MARGIN = -1; 2587e24e82a70a2c681f4291a3397bcd1e1005f251aChris Lattner private static int EVENT_RECT_LEFT_MARGIN = -1; 2597e24e82a70a2c681f4291a3397bcd1e1005f251aChris Lattner private static int EVENT_RECT_RIGHT_MARGIN = -1; 260e66a8cf9117e5fb95a05ff76ec06615e63dd5adeChris Lattner private static int EVENT_TEXT_TOP_MARGIN = 2; 261e66a8cf9117e5fb95a05ff76ec06615e63dd5adeChris Lattner private static int EVENT_TEXT_BOTTOM_MARGIN = 3; 262e66a8cf9117e5fb95a05ff76ec06615e63dd5adeChris Lattner private static int EVENT_TEXT_LEFT_MARGIN = 11; 26384f2170062014d268951902164bed0d8bdea0e82Douglas Gregor private static int EVENT_TEXT_RIGHT_MARGIN = 4; 264b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN; 26581c018d9482e7cc2addadc6202dcf162a01faefdChris Lattner private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN; 26684f2170062014d268951902164bed0d8bdea0e82Douglas Gregor private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN; 26781c018d9482e7cc2addadc6202dcf162a01faefdChris Lattner private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN; 26884f2170062014d268951902164bed0d8bdea0e82Douglas Gregor 26984f2170062014d268951902164bed0d8bdea0e82Douglas Gregor private static int mSelectionColor; 2708ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek private static int mPressedColor; 27181c018d9482e7cc2addadc6202dcf162a01faefdChris Lattner private static int mSelectedEventTextColor; 27281c018d9482e7cc2addadc6202dcf162a01faefdChris Lattner private static int mEventTextColor; 2735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int mWeek_saturdayColor; 2745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int mWeek_sundayColor; 2755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int mCalendarDateBannerTextColor; 2765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// private static int mCalendarAllDayBackground; 2775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int mCalendarAmPmLabel; 2788297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek// private static int mCalendarDateBannerBackground; 27914f8b4ff660bcaa763974b8d0fae81857c594495Ted Kremenek// private static int mCalendarDateSelected; 2808297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek// private static int mCalendarGridAreaBackground; 2818297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private static int mCalendarGridAreaSelected; 2820965f446e7685dc01fc0b5e718610530eed3cc63Ted Kremenek private static int mCalendarGridLineHorizontalColor; 2830632dd6fe068011af5710c0d6a745724021ff620Chris Lattner private static int mCalendarGridLineVerticalColor; 2840632dd6fe068011af5710c0d6a745724021ff620Chris Lattner private static int mCalendarGridLineInnerHorizontalColor; 2858a0d2660b6890ed95d34f37d142c0c52ac490194Ted Kremenek private static int mCalendarGridLineInnerVerticalColor; 2868ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek// private static int mCalendarHourBackground; 2878ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek private static int mCalendarHourLabel; 2888ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek// private static int mCalendarHourSelected; 2898ffb159441e923322bef6b5dee1aaf24c738d75eTed Kremenek 2908a0d2660b6890ed95d34f37d142c0c52ac490194Ted Kremenek private int mViewStartX; 29114f8b4ff660bcaa763974b8d0fae81857c594495Ted Kremenek private int mViewStartY; 292ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek private int mMaxViewStartY; 293e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop private int mViewHeight; 2945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mViewWidth; 2955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mGridAreaHeight; 2965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int mCellHeight = 0; // shared among all DayViews 2975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static int mMinCellHeight = 32; 2985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mScrollStartY; 2995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mPreviousDirection; 3005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 3025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Vertical distance or span between the two touch points at the start of a 303025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor * scaling gesture 304025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor */ 305025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private float mStartingSpanY = 0; 3065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** Height of 1 hour in pixels at the start of a scaling gesture */ 307025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private int mCellHeightBeforeScaleGesture; 308b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff /** The hour at the center two touch points */ 309507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff private float mGestureCenterHour = 0; 3105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 3115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Flag to decide whether to handle the up event. Cases where up events 3125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * should be ignored are 1) right after a scale gesture and 2) finger was 3135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * down before app launch 3145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 3158297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private boolean mHandleActionUp = true; 3168297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 3178297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private int mHoursTextHeight; 3188297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private int mAllDayHeight; 3194927be6102784fc69c50234f7e2253283b3e99e4Ted Kremenek private static int DAY_HEADER_HEIGHT = 45; 320ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek /** 321e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop * Max of all day events in a given day in this view. 3225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 3235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mMaxAllDayEvents; 3245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer protected int mNumDays = 7; 3265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mNumHours = 10; 3278189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 3288189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek /** Width of the time line (list of hours) to the left. */ 329b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private int mHoursWidth; 3305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private int mDateStrWidth; 3318189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private int mFirstCell; 3328189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private int mFirstHour = -1; 3338189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private int mFirstHourOffset; 334026dc96ac6ece60da5e1b98e2a71bd0ff0939fd8Chris Lattner private String[] mHourStrs; 335026dc96ac6ece60da5e1b98e2a71bd0ff0939fd8Chris Lattner private String[] mDayStrs; 336026dc96ac6ece60da5e1b98e2a71bd0ff0939fd8Chris Lattner private String[] mDayStrs2Letter; 337026dc96ac6ece60da5e1b98e2a71bd0ff0939fd8Chris Lattner private boolean mIs24HourFormat; 338026dc96ac6ece60da5e1b98e2a71bd0ff0939fd8Chris Lattner 339026dc96ac6ece60da5e1b98e2a71bd0ff0939fd8Chris Lattner private ArrayList<Event> mSelectedEvents = new ArrayList<Event>(); 340026dc96ac6ece60da5e1b98e2a71bd0ff0939fd8Chris Lattner private boolean mComputeSelectedEvents; 3418189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private Event mSelectedEvent; 342025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private Event mPrevSelectedEvent; 343025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private Rect mPrevBox = new Rect(); 344025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor protected final Resources mResources; 345025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor protected final Drawable mCurrentTimeLine; 346025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor protected final Drawable mTodayHeaderDrawable; 347025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor protected final Drawable mEventBoxDrawable; 3488189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private String mAmString; 3498189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private String mPmString; 350025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor private DeleteEventHelper mDeleteEventHelper; 351025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 3528189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private ContextMenuHandler mContextMenuHandler = new ContextMenuHandler(); 3538189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 3548189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek ScaleGestureDetector mScaleGestureDetector; 3558189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 3565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 3578189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek * The initial state of the touch mode when we enter this view. 3588189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek */ 3598189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int TOUCH_MODE_INITIAL_STATE = 0; 3608189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 3618189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek /** 3628189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek * Indicates we just received the touch event and we are waiting to see if 3638189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek * it is a tap or a scroll gesture. 3648189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek */ 3658189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int TOUCH_MODE_DOWN = 1; 3668189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 3678189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek /** 3688189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek * Indicates the touch gesture is a vertical scroll 3694ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek */ 3708189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int TOUCH_MODE_VSCROLL = 0x20; 3718189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 3724ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek /** 3738189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek * Indicates the touch gesture is a horizontal scroll 3748189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek */ 3758189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private static final int TOUCH_MODE_HSCROLL = 0x40; 376b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 3778189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek private int mTouchMode = TOUCH_MODE_INITIAL_STATE; 3788189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek 3798189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek /** 3808189cde56b4f6f938cd65f53c932fe1860d0204cTed Kremenek * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS. 381b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff */ 382b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private static final int SELECTION_HIDDEN = 0; 383b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet 38454395d440dc82a5e51b945c6c2a7f4bc4bea0358Ted Kremenek private static final int SELECTION_SELECTED = 2; 385cc326204dd97771c336b9aab3b9963ea30d69c29Ted Kremenek private static final int SELECTION_LONGPRESS = 3; 386025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 387cc326204dd97771c336b9aab3b9963ea30d69c29Ted Kremenek private int mSelectionMode = SELECTION_HIDDEN; 388025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 38954395d440dc82a5e51b945c6c2a7f4bc4bea0358Ted Kremenek private boolean mScrolling = false; 3905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private CalendarController mController; 3925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private ViewSwitcher mViewSwitcher; 3935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private GestureDetector mGestureDetector; 3948297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 3958297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public DayView(Context context, CalendarController controller, 3968297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) { 3978297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek super(context); 3982dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek if (mScale == 0) { 399ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mScale = getContext().getResources().getDisplayMetrics().density; 400e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop if (mScale != 1) { 4015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer SINGLE_ALLDAY_HEIGHT *= mScale; 4025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer ALLDAY_TOP_MARGIN *= mScale; 403c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale; 404c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson 405103fc81f12aa635aa0a573c94b1aceb496b4e587Ted Kremenek NORMAL_FONT_SIZE *= mScale; 406c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson EVENT_TEXT_FONT_SIZE *= mScale; 407c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson HOURS_FONT_SIZE *= mScale; 408c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson AMPM_FONT_SIZE *= mScale; 409103fc81f12aa635aa0a573c94b1aceb496b4e587Ted Kremenek MIN_CELL_WIDTH_FOR_TEXT *= mScale; 410d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek MIN_EVENT_HEIGHT *= mScale; 411c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson 412c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson HORIZONTAL_FLING_THRESHOLD *= mScale; 413c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson 414c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson CURRENT_TIME_LINE_HEIGHT *= mScale; 415c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson CURRENT_TIME_LINE_BORDER_WIDTH *= mScale; 416c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson 417c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson MIN_Y_SPAN *= mScale; 418d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek MAX_CELL_HEIGHT *= mScale; 419d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek DEFAULT_CELL_HEIGHT *= mScale; 420b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff DAY_HEADER_HEIGHT *= mScale; 421b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff DAY_HEADER_LEFT_MARGIN *= mScale; 422edcc752060be38bfa7e7b32691c0cc4d843622a8Ted Kremenek DAY_HEADER_RIGHT_MARGIN *= mScale; 423c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson DAY_HEADER_BOTTOM_MARGIN *= mScale; 424c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale; 425c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale; 426c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale; 427c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson DAY_HEADER_FONT_SIZE *= mScale; 4283fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner DATE_HEADER_FONT_SIZE *= mScale; 4293fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner CALENDAR_COLOR_SQUARE_SIZE *= mScale; 430c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson EVENT_TEXT_TOP_MARGIN *= mScale; 431c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson EVENT_TEXT_BOTTOM_MARGIN *= mScale; 432c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson EVENT_TEXT_LEFT_MARGIN *= mScale; 433d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek EVENT_TEXT_RIGHT_MARGIN *= mScale; 434d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek EVENT_ALL_DAY_TEXT_TOP_MARGIN *= mScale; 435d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN *= mScale; 436b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff EVENT_ALL_DAY_TEXT_LEFT_MARGIN *= mScale; 4373fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner EVENT_ALL_DAY_TEXT_RIGHT_MARGIN *= mScale; 4385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer EVENT_RECT_TOP_MARGIN *= mScale; 43924e1e707b4c362f18e371e2bbf054a8345b57bfaChris Lattner EVENT_RECT_BOTTOM_MARGIN *= mScale; 440d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek EVENT_RECT_LEFT_MARGIN *= mScale; 44124e1e707b4c362f18e371e2bbf054a8345b57bfaChris Lattner EVENT_RECT_RIGHT_MARGIN *= mScale; 442d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek } 443d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek } 444b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 445d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mResources = context.getResources(); 446025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_week_holo_light); 447025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light); 448025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mEventBoxDrawable = mResources.getDrawable(R.drawable.panel_month_event_holo_light); 449025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mEventLoader = eventLoader; 450764a7ce5217f9569e100a3445f47496ee82daf86Chris Lattner mEventGeometry = new EventGeometry(); 451025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT); 452025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mEventGeometry.setHourGap(HOUR_GAP); 453d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mContext = context; 454d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mDeleteEventHelper = new DeleteEventHelper(context, null, false /* don't exit when done */); 455d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mLastPopupEventID = INVALID_EVENT_ID; 456025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mController = controller; 45751b09f2c528c8460b5465c676173324e44176d62Devang Patel mViewSwitcher = viewSwitcher; 45851b09f2c528c8460b5465c676173324e44176d62Devang Patel mGestureDetector = new GestureDetector(context, new CalendarGestureListener()); 45951b09f2c528c8460b5465c676173324e44176d62Devang Patel mScaleGestureDetector = new ScaleGestureDetector(getContext(), this); 46051b09f2c528c8460b5465c676173324e44176d62Devang Patel mNumDays = numDays; 46151b09f2c528c8460b5465c676173324e44176d62Devang Patel if (mCellHeight == 0) { 46251b09f2c528c8460b5465c676173324e44176d62Devang Patel mCellHeight = Utils.getSharedPreference(mContext, 46351b09f2c528c8460b5465c676173324e44176d62Devang Patel GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT); 4645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 46520dabe8b8ff7ed6d2d0158fee43755a4bc7642c3Chris Lattner 46620dabe8b8ff7ed6d2d0158fee43755a4bc7642c3Chris Lattner init(context); 46720dabe8b8ff7ed6d2d0158fee43755a4bc7642c3Chris Lattner } 46820dabe8b8ff7ed6d2d0158fee43755a4bc7642c3Chris Lattner 46920dabe8b8ff7ed6d2d0158fee43755a4bc7642c3Chris Lattner private void init(Context context) { 4703fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner setFocusable(true); 4713fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner 4723fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner // Allow focus in touch mode so that we can do keyboard shortcuts 47391ee0140ecb60b5c1402edc9e577257636c4ca60Chris Lattner // even after we've entered touch mode. 4743fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner setFocusableInTouchMode(true); 4753fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner setClickable(true); 4763fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner setOnCreateContextMenuListener(this); 477b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 4785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mFirstDayOfWeek = Utils.getFirstDayOfWeek(context); 4795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater)); 4815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer long currentTime = System.currentTimeMillis(); 482d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mCurrentTime.set(currentTime); 483d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek //The % makes it go off at the next increment of 5 minutes. 484d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek postDelayed(mUpdateCurrentTime, 485d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek UPDATE_CURRENT_TIME_DELAY - (currentTime % UPDATE_CURRENT_TIME_DELAY)); 4869eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); 487ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek 488e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mWeek_saturdayColor = mResources.getColor(R.color.week_saturday); 4895f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mWeek_sundayColor = mResources.getColor(R.color.week_sunday); 4905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color); 491c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson// mCalendarAllDayBackground = mResources.getColor(R.color.calendar_all_day_background); 492d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label); 4936c36be5b383875b490684bcf439d6d427298c1afChris Lattner// mCalendarDateBannerBackground = mResources.getColor(R.color.calendar_date_banner_background); 4943fb94a4918bd427fdb12df997dd87fd1017f0388Chris Lattner// mCalendarDateSelected = mResources.getColor(R.color.calendar_date_selected); 4955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer// mCalendarGridAreaBackground = mResources.getColor(R.color.calendar_grid_area_background); 496edcc752060be38bfa7e7b32691c0cc4d843622a8Ted Kremenek mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected); 497d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mCalendarGridLineHorizontalColor = mResources.getColor(R.color.calendar_grid_line_horizontal_color); 498025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mCalendarGridLineVerticalColor = mResources.getColor(R.color.calendar_grid_line_vertical_color); 499025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mCalendarGridLineInnerHorizontalColor = mResources.getColor(R.color.calendar_grid_line_inner_horizontal_color); 500025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mCalendarGridLineInnerVerticalColor = mResources.getColor(R.color.calendar_grid_line_inner_vertical_color); 501025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor// mCalendarHourBackground = mResources.getColor(R.color.calendar_hour_background); 502d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mCalendarHourLabel = mResources.getColor(R.color.calendar_hour_label); 50351b09f2c528c8460b5465c676173324e44176d62Devang Patel// mCalendarHourSelected = mResources.getColor(R.color.calendar_hour_selected); 504025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mSelectionColor = mResources.getColor(R.color.selection); 505025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mPressedColor = mResources.getColor(R.color.pressed); 5066c36be5b383875b490684bcf439d6d427298c1afChris Lattner mSelectedEventTextColor = mResources.getColor(R.color.calendar_event_selected_text_color); 507025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mEventTextColor = mResources.getColor(R.color.calendar_event_text_color); 5085f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 509b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mEventTextPaint.setColor(mEventTextColor); 510b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE); 511b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mEventTextPaint.setTextAlign(Paint.Align.LEFT); 5125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mEventTextPaint.setAntiAlias(true); 5135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color); 5155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Paint p = mSelectionPaint; 516d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek p.setColor(gridLineColor); 517d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek p.setStyle(Style.FILL); 518d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek p.setAntiAlias(false); 519d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek 5209eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek p = mPaint; 521ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek p.setAntiAlias(true); 522e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop 5235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Allocate space for 2 weeks worth of weekday names so that we can 5245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // easily start the week display at any week day. 5255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDayStrs = new String[14]; 5265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Also create an array of 2-letter abbreviations. 528b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mDayStrs2Letter = new String[14]; 5295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 530b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { 531b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff int index = i - Calendar.SUNDAY; 532b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // e.g. Tue for Tuesday 5331de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM); 5341de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor mDayStrs[index + 7] = mDayStrs[index]; 5351de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor // e.g. Tu for Tuesday 5365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT); 5375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // If we don't have 2-letter day strings, fall back to 1-letter. 5391de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor if (mDayStrs2Letter[index].equals(mDayStrs[index])) { 5405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST); 5415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDayStrs2Letter[index + 7] = mDayStrs2Letter[index]; 5445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 545b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 546b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // Figure out how much space we need for the 3-letter abbrev names 547b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // in the worst case. 548b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff p.setTextSize(DATE_HEADER_FONT_SIZE); 5495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer p.setTypeface(mBold); 5505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer String[] dateStrs = {" 28", " 30"}; 5515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDateStrWidth = computeMaxStringWidth(0, dateStrs, p); 5525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer p.setTextSize(DAY_HEADER_FONT_SIZE); 5538297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p); 5548297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 5558297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek p.setTextSize(HOURS_FONT_SIZE); 5568297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek p.setTypeface(null); 557b15132fda52046ba65eafd6f4b448f7aa16b7e8dTed Kremenek updateIs24HourFormat(); 558ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek 559e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mAmString = DateUtils.getAMPMString(Calendar.AM); 5605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPmString = DateUtils.getAMPMString(Calendar.PM); 5615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer String[] ampm = {mAmString, mPmString}; 5625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer p.setTextSize(AMPM_FONT_SIZE); 5635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHoursWidth = computeMaxStringWidth(mHoursWidth, ampm, p); 5645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHoursWidth += HOURS_MARGIN; 5655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5668297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek LayoutInflater inflater; 5678297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 568b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mPopupView = inflater.inflate(R.layout.bubble_event, null); 5695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPopupView.setLayoutParams(new ViewGroup.LayoutParams( 570b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff ViewGroup.LayoutParams.MATCH_PARENT, 571b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff ViewGroup.LayoutParams.WRAP_CONTENT)); 5728297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mPopup = new PopupWindow(context); 5738297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mPopup.setContentView(mPopupView); 5748297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek Resources.Theme dialogTheme = getResources().newTheme(); 575b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff dialogTheme.applyStyle(android.R.style.Theme_Dialog, true); 5768297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] { 5775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer android.R.attr.windowBackground }); 578025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mPopup.setBackgroundDrawable(ta.getDrawable(0)); 579025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor ta.recycle(); 580025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 5818297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Enable touching the popup window 582025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mPopupView.setOnClickListener(this); 5838297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 584025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater)); 5858297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek long millis = System.currentTimeMillis(); 586025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor mBaseDate.set(millis); 5875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5888297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mEarliestStartHour = new int[mNumDays]; 5898297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mHasAllDayEvent = new boolean[mNumDays]; 5908297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 591b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // mLines is the array of points used with Canvas.drawLines() in 592025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor // drawGridBackground() and drawAllDayEvents(). Its size depends 593025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor // on the max number of lines that can ever be drawn by any single 594025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor // drawLines() call in either of those methods. 595b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff final int maxGridLines = (24 + 1) // max horizontal lines we might draw 596b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff + (mNumDays + 1); // max vertical lines we might draw 597b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mLines = new float[maxGridLines * 4]; 598b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 599b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 600b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff /** 6015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * This is called when the popup window is pressed. 6025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 6035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public void onClick(View v) { 6045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (v == mPopupView) { 6055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Pretend it was a trackball click because that will always 6068297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // jump to the "View event" screen. 6078297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek switchViews(true /* trackball */); 6088297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 6098297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 6104210f3dfd73ad3482f9cfa0a382e1fd78f22976dTed Kremenek 611ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek public void updateIs24HourFormat() { 612e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mIs24HourFormat = DateFormat.is24HourFormat(mContext); 6135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm; 6145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 6165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 6175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Returns the start of the selected time in milliseconds since the epoch. 6188297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek * 6198297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek * @return selected time in UTC milliseconds since the epoch. 620c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson */ 621c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson long getSelectedTimeInMillis() { 6229dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff Time time = new Time(mBaseDate); 6235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer time.setJulianDay(mSelectionDay); 6248297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek time.hour = mSelectionHour; 6258297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 6268297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // We ignore the "isDst" field because we want normalize() to figure 6278297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // out the correct DST value and not adjust the selected time based 6285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // on the current setting of DST. 629025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor return time.normalize(true /* ignore isDst */); 630025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor } 631025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 6328297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek Time getSelectedTime() { 6338297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek Time time = new Time(mBaseDate); 634c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson time.setJulianDay(mSelectionDay); 635c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson time.hour = mSelectionHour; 6368297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 637025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor // We ignore the "isDst" field because we want normalize() to figure 6388297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // out the correct DST value and not adjust the selected time based 639025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor // on the current setting of DST. 640c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson time.normalize(true /* ignore isDst */); 641025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor return time; 642025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor } 643025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 644025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor /** 645c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson * Returns the start of the selected time in minutes since midnight, 6469dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff * local time. The derived class must ensure that this is consistent 6479dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff * with the return value from getSelectedTimeInMillis(). 6489dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff */ 6499dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff int getSelectedMinutesSinceMidnight() { 650c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson return mSelectionHour * MINUTES_PER_HOUR; 651c2ee10d79f70036af652a395ac1f8273f3d04e12Douglas Gregor } 652c2ee10d79f70036af652a395ac1f8273f3d04e12Douglas Gregor 653c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson public void setSelectedDay(Time time) { 654c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson mBaseDate.set(time); 6559dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff mSelectionHour = mBaseDate.hour; 6569dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff mSelectedEvent = null; 6579dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff mPrevSelectedEvent = null; 6585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer long millis = mBaseDate.toMillis(false /* use isDst */); 6595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mSelectionDay = Time.getJulianDay(millis, mBaseDate.gmtoff); 6605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mSelectedEvents.clear(); 6615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mComputeSelectedEvents = true; 6628297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 6638297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek recalc(); 6648297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 6658297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Don't draw the selection box if we are going to the "current" time 6669eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek long currMillis = System.currentTimeMillis(); 667ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek boolean recent = (currMillis - 10000) < millis && millis < currMillis; 668e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mSelectionMode = recent ? SELECTION_HIDDEN : SELECTION_SELECTED; 6695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mRemeasure = true; 6705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer invalidate(); 6715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 6735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public Time getSelectedDay() { 6745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Time time = new Time(mBaseDate); 6758297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek time.setJulianDay(mSelectionDay); 6768297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek time.hour = mSelectionHour; 677b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 6785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // We ignore the "isDst" field because we want normalize() to figure 679b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // out the correct DST value and not adjust the selected time based 6808297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // on the current setting of DST. 6818297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek time.normalize(true /* ignore isDst */); 682b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return time; 6838297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 6845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 685d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor /** 686d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor * return a negative number if "time" is comes before the visible time 687d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor * range, a positive number if "time" is after the visible time range, and 0 6888297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek * if it is in the visible time range. 6898297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek */ 690d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor public int compareToVisibleTimeRange(Time time) { 6918297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 6928297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek int savedHour = mBaseDate.hour; 693d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor int savedMinute = mBaseDate.minute; 694d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor int savedSec = mBaseDate.second; 695d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor 696d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor mBaseDate.hour = 0; 697b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mBaseDate.minute = 0; 698b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mBaseDate.second = 0; 699b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 700b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff if (DEBUG) { 7015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Log.d(TAG, "Begin " + mBaseDate.toString()); 7025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Log.d(TAG, "Diff " + time.toString()); 7035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 7045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 7058297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Compare beginning of range 7068297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek int diff = Time.compare(time, mBaseDate); 7078297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (diff > 0) { 7088297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Compare end of range 7095572b94b76a78a35bac32d1ef04907c7a651adbaTed Kremenek mBaseDate.monthDay += mNumDays; 710ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mBaseDate.normalize(true); 711e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop diff = Time.compare(time, mBaseDate); 7125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 7135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (DEBUG) Log.d(TAG, "End " + mBaseDate.toString()); 7145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 7155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mBaseDate.monthDay -= mNumDays; 7165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mBaseDate.normalize(true); 7178297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (diff < 0) { 7188297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // in visible time 719b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff diff = 0; 7205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else if (diff == 0) { 721b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // Midnight of following day 722b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff diff = 1; 7238297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 7248297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 725b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 7268297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (DEBUG) Log.d(TAG, "Diff: " + diff); 72767d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor 72867d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor mBaseDate.hour = savedHour; 72967d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor mBaseDate.minute = savedMinute; 7305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mBaseDate.second = savedSec; 7318297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek return diff; 7328297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 73367d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor 7348297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private void recalc() { 7358297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Set the base date to the beginning of the week if we are displaying 73667d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // 7 days at a time. 73767d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor if (mNumDays == 7) { 73867d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor int dayOfWeek = mBaseDate.weekDay; 73967d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor int diff = dayOfWeek - mFirstDayOfWeek; 740b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff if (diff != 0) { 741b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff if (diff < 0) { 742b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff diff += 7; 743b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 7445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mBaseDate.monthDay -= diff; 7455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mBaseDate.normalize(true /* ignore isDst */); 7465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 7475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 7488297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 7498297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek final long start = mBaseDate.toMillis(false /* use isDst */); 7508297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff); 7518297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mLastJulianDay = mFirstJulianDay + mNumDays - 1; 752e3299ef345bb8a71901108bf83a824591eae6c1aTed Kremenek 753ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY); 754e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mFirstVisibleDate = mBaseDate.monthDay; 7555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mFirstVisibleDayOfWeek = mBaseDate.weekDay; 7565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 7575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 7585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @Override 7595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer protected void onSizeChanged(int width, int height, int oldw, int oldh) { 7605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mViewWidth = width; 7615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mViewHeight = height; 7625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int gridAreaWidth = width - mHoursWidth; 7638297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays; 7648297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 765b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // This would be about 1 day worth in a 7 day view 7665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHorizontalSnapBackThreshold = width / 7; 767b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 768b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff Paint p = new Paint(); 769af458c9f658335bdea482395ff4a25c75935e129Chris Lattner p.setTextSize(HOURS_FONT_SIZE); 770af458c9f658335bdea482395ff4a25c75935e129Chris Lattner mHoursTextHeight = (int) Math.abs(p.ascent()); 771af458c9f658335bdea482395ff4a25c75935e129Chris Lattner remeasure(width, height); 772af458c9f658335bdea482395ff4a25c75935e129Chris Lattner } 773b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 774af458c9f658335bdea482395ff4a25c75935e129Chris Lattner /** 7755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Measures the space needed for various parts of the view after 77667d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor * loading new events. This can change if there are all-day events. 77767d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor */ 77867d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor private void remeasure(int width, int height) { 7798297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 7808297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // First, clear the array of earliest start times, and the array 7818297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // indicating presence of an all-day event. 7828297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek for (int day = 0; day < mNumDays; day++) { 7838297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mEarliestStartHour[day] = 25; // some big number 7848297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mHasAllDayEvent[day] = false; 7858297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 7868297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 7878297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Compute the layout relation between each event before measuring cell 788b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // width, as the cell width should be adjusted along with the relation. 78967d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // 79067d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm) 79167d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // We should mark them as "overwapped". Though they are not overwapped logically, but 79267d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // minimum cell height implicitly expands the cell height of A and it should look like 79367d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // (1:00pm - 1:15pm) after the cell height adjustment. 79467d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor 79567d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // Compute the space needed for the all-day events, if any. 79667d8249924ef38a5375ff9c92cd21c1854d6ababDouglas Gregor // Make a pass over all the events, and keep track of the maximum 797b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // number of all-day events in any one day. Also, keep track of 798b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // the earliest event in each day. 799b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff int maxAllDayEvents = 0; 8005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final ArrayList<Event> events = mEvents; 8015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int len = events.size(); 8025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Num of all-day-events on each day. 8035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1]; 8048297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek Arrays.fill(eventsCount, 0); 8058297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek for (int ii = 0; ii < len; ii++) { 8068297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek Event event = events.get(ii); 8078297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) { 80807ba046c7d7bbc1f42b86b3ed012eec8fc7849daTed Kremenek continue; 809ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 810e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop if (event.allDay) { 8115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int firstDay = Math.max(event.startDay, mFirstJulianDay); 8120196cab54007ff072ec2642da8911c6b7e8d3fb5Fariborz Jahanian final int lastDay = Math.min(event.endDay, mLastJulianDay); 8135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer for (int day = firstDay; day <= lastDay; day++) { 8145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int count = ++eventsCount[day - mFirstJulianDay]; 8155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (maxAllDayEvents < count) { 8165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer maxAllDayEvents = count; 817507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff } 81861f62165220e75694fe333179c78815e2e48d71fTed Kremenek } 8195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 82061f62165220e75694fe333179c78815e2e48d71fTed Kremenek int daynum = event.startDay - mFirstJulianDay; 82161f62165220e75694fe333179c78815e2e48d71fTed Kremenek int durationDays = event.endDay - event.startDay + 1; 8225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (daynum < 0) { 8231de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor durationDays += daynum; 8241de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor daynum = 0; 8251de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor } 8265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (daynum + durationDays > mNumDays) { 8271de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor durationDays = mNumDays - daynum; 8281de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor } 8291de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor for (int day = daynum; durationDays > 0; day++, durationDays--) { 8301de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor mHasAllDayEvent[day] = true; 8311de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor } 8321de05feeeafe5b215fe7617594a7076a5192a6e2Douglas Gregor } else { 833b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff int daynum = event.startDay - mFirstJulianDay; 834507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff int hour = event.startTime / 60; 83561f62165220e75694fe333179c78815e2e48d71fTed Kremenek if (daynum >= 0 && hour < mEarliestStartHour[daynum]) { 836507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff mEarliestStartHour[daynum] = hour; 8375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 8385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 8395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Also check the end hour in case the event spans more than 8405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // one day. 8418297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek daynum = event.endDay - mFirstJulianDay; 8428297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek hour = event.endTime / 60; 8438297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) { 8448297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mEarliestStartHour[daynum] = hour; 8453f0767b74b16b37bcce60ff457609161a0665784Ted Kremenek } 846ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 847e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop } 8485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mMaxAllDayEvents = maxAllDayEvents; 8495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 8505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Calculate mAllDayHeight 8515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mFirstCell = DAY_HEADER_HEIGHT; 8525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int allDayHeight = 0; 8531060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek if (maxAllDayEvents > 0) { 854225a2d946f73aed3b38f265fcb4e7d7d88136928Ted Kremenek // If there is at most one all-day event per day, then use less 8557d5c2f241c74e5f8d9ec492e8f9f268e5e9ae41fDouglas Gregor // space (but more than the space for a single event). 8565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (maxAllDayEvents == 1) { 8571060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek allDayHeight = SINGLE_ALLDAY_HEIGHT; 8581060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek } else { 8597d5c2f241c74e5f8d9ec492e8f9f268e5e9ae41fDouglas Gregor // Allow the all-day area to grow in height depending on the 8607d5c2f241c74e5f8d9ec492e8f9f268e5e9ae41fDouglas Gregor // number of all-day events we need to show, up to a limit. 8617d5c2f241c74e5f8d9ec492e8f9f268e5e9ae41fDouglas Gregor allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; 8627d5c2f241c74e5f8d9ec492e8f9f268e5e9ae41fDouglas Gregor if (allDayHeight > MAX_ALLDAY_HEIGHT) { 8635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer allDayHeight = MAX_ALLDAY_HEIGHT; 8641060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek } 8651060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek } 8667d5c2f241c74e5f8d9ec492e8f9f268e5e9ae41fDouglas Gregor mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN; 867b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } else { 868b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mSelectionAllDay = false; 8695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 8705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mAllDayHeight = allDayHeight; 8715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 8725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mGridAreaHeight = height - mFirstCell; 8735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 8748297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // The min is where 24 hours cover the entire visible area 8758297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mMinCellHeight = (height - DAY_HEADER_HEIGHT) / 24 - HOUR_GAP; 8768297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (mCellHeight < mMinCellHeight) { 8778297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mCellHeight = mMinCellHeight; 878225a2d946f73aed3b38f265fcb4e7d7d88136928Ted Kremenek } 879ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek 880e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mNumHours = mGridAreaHeight / mCellHeight; 8815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mEventGeometry.setHourHeight(mCellHeight); 8825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 8835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final long minimumDurationMillis = (long) 8845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)); 8855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer Event.computePositions(events, minimumDurationMillis); 8865f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 887507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff // Compute the top of our reachable view 8885f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; 889507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff if (DEBUG) { 890b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff Log.e(TAG, "mViewStartY: " + mViewStartY); 891d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY); 892d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor } 893d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor if (mViewStartY > mMaxViewStartY) { 894d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor mViewStartY = mMaxViewStartY; 895d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor computeFirstHour(); 896d921cf976b4769af8d06d6763a2547dadf7940abDouglas Gregor } 897507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff 898507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff if (mFirstHour == -1) { 899507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff initFirstHour(); 9005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mFirstHourOffset = 0; 9015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 9025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 9035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // When we change the base date, the number of all-day events may 9048297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // change and that changes the cell height. When we switch dates, 9058297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // we use the mFirstHourOffset from the previous view, but that may 9068297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // be too large for the new view if the cell height is smaller. 9078297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (mFirstHourOffset >= mCellHeight + HOUR_GAP) { 90896f22423c52c22d263b4e5f7ee762122b607459eTed Kremenek mFirstHourOffset = mCellHeight + HOUR_GAP - 1; 909ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 910e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset; 9115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 9125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP); 9135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer //When we get new events we don't want to dismiss the popup unless the event changes 9145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) { 9155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPopup.dismiss(); 916507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff } 9175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPopup.setWidth(eventAreaWidth - 20); 918507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); 919b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 920025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor 921025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor /** 922025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor * Initialize the state for another view. The given view is one that has 923025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor * its own bitmap and will use an animation to replace the current view. 924025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor * The current view and new view are either both Week views or both Day 925025452fa0eda63e150cfaeebe64f0a19c96b3a06Douglas Gregor * views. They differ in their base date. 926507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff * 927b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff * @param view the view to initialize. 9285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 9295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private void initView(DayView view) { 9305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer view.mSelectionHour = mSelectionHour; 9315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer view.mSelectedEvents.clear(); 9328297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek view.mComputeSelectedEvents = true; 9338297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek view.mFirstHour = mFirstHour; 9348297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek view.mFirstHourOffset = mFirstHourOffset; 9358297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek view.remeasure(getWidth(), getHeight()); 9369eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek 937ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek view.mSelectedEvent = null; 938e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop view.mPrevSelectedEvent = null; 9395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer view.mFirstDayOfWeek = mFirstDayOfWeek; 9405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (view.mEvents.size() > 0) { 9415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer view.mSelectionAllDay = mSelectionAllDay; 942c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner } else { 943c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner view.mSelectionAllDay = false; 944c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner } 945c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner 946c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner // Redraw the screen so that the selection box will be redrawn. We may 947c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner // have scrolled to a different part of the day in some other view 948c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner // so the selection box in this view may no longer be visible. 949c8edf6bd5f76bf50411d037c83d36847edfa68f0Chris Lattner view.recalc(); 9505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 9515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 9521060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek /** 953507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff * Switch to another view based on what was selected (an event or a free 9545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * slot) and how it was selected (by touch or by trackball). 955507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff * 9561060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek * @param trackBallSelection true if the selection was made using the 9570de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor * trackball. 9580de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor */ 9590de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor private void switchViews(boolean trackBallSelection) { 9600de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor Event selectedEvent = mSelectedEvent; 9611060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek 9621060aff23f72135f8b50034a1e80f16725ebc56cTed Kremenek mPopup.dismiss(); 9630de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor mLastPopupEventID = INVALID_EVENT_ID; 9640de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor if (mNumDays > 1) { 9650de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor // This is the Week view. 9660de9d8857b715c2f45c987651f4ce06d73330d93Douglas Gregor // With touch, we always switch to Day/Agenda View 967b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // With track ball, if we selected a free slot, then create an event. 968507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff // If we selected a specific event, switch to EventInfo view. 9695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (trackBallSelection) { 9705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (selectedEvent == null) { 9715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Switch to the EditEvent view 9725f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer long startMillis = getSelectedTimeInMillis(); 9735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS; 9748297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1, 9758297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek startMillis, endMillis, 0, 0); 9768297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } else { 9778297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Switch to the EventInfo view 9782dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id, 979ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek selectedEvent.startMillis, selectedEvent.endMillis, 0, 0); 980e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop } 9815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else { 9825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // This was a touch selection. If the touch selected a single 983fe795956194141c91ae555985c9b930595bff43fChris Lattner // unambiguous event, then view that event. Otherwise go to 984fe795956194141c91ae555985c9b930595bff43fChris Lattner // Day/Agenda view. 985fe795956194141c91ae555985c9b930595bff43fChris Lattner if (mSelectedEvents.size() == 1) { 986fe795956194141c91ae555985c9b930595bff43fChris Lattner mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id, 9876a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson selectedEvent.startMillis, selectedEvent.endMillis, 0, 0); 988b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson } 989dfab34a696d1dba8622248c31aaf605906cb6109Anders Carlsson } 99039c47b56f45437bbc49c9568b7308a400234a730Anders Carlsson } else { 99139c47b56f45437bbc49c9568b7308a400234a730Anders Carlsson // This is the Day view. 992b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson // If we selected a free slot, then create an event. 993b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson // If we selected an event, then go to the EventInfo view. 994b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson if (selectedEvent == null) { 995b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson // Switch to the EditEvent view 996b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson long startMillis = getSelectedTimeInMillis(); 997ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS; 998b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson 999b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1, startMillis, 1000fe795956194141c91ae555985c9b930595bff43fChris Lattner endMillis, 0, 0); 1001dfab34a696d1dba8622248c31aaf605906cb6109Anders Carlsson } else { 1002dfab34a696d1dba8622248c31aaf605906cb6109Anders Carlsson mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, selectedEvent.id, 1003dfab34a696d1dba8622248c31aaf605906cb6109Anders Carlsson selectedEvent.startMillis, selectedEvent.endMillis, 0, 0); 1004db6ed1786bf460e1143f67d14bf2d71ad9856f81Chris Lattner } 1005db6ed1786bf460e1143f67d14bf2d71ad9856f81Chris Lattner } 1006b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson } 1007cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor 1008cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor @Override 1009cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor public boolean onKeyUp(int keyCode, KeyEvent event) { 1010cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor mScrolling = false; 1011cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor long duration = event.getEventTime() - event.getDownTime(); 1012cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor 1013cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor switch (keyCode) { 1014cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor case KeyEvent.KEYCODE_DPAD_CENTER: 101539c47b56f45437bbc49c9568b7308a400234a730Anders Carlsson if (mSelectionMode == SELECTION_HIDDEN) { 1016cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor // Don't do anything unless the selection is visible. 1017dfab34a696d1dba8622248c31aaf605906cb6109Anders Carlsson break; 1018cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor } 1019dfab34a696d1dba8622248c31aaf605906cb6109Anders Carlsson 1020458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner if (mSelectionMode == SELECTION_PRESSED) { 1021458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // This was the first press when there was nothing selected. 1022458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // Change the selection from the "pressed" state to the 1023458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // the "selected" state. We treat short-press and 1024cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor // long-press the same here because nothing was selected. 1025cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor mSelectionMode = SELECTION_SELECTED; 1026458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner invalidate(); 1027458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner break; 1028458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } 1029458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner 1030458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // Check the duration to determine if this was a short press 1031458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner if (duration < ViewConfiguration.getLongPressTimeout()) { 1032458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner switchViews(true /* trackball */); 1033458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } else { 1034458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner mSelectionMode = SELECTION_LONGPRESS; 1035458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner invalidate(); 1036458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner performLongClick(); 1037458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } 1038458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner break; 1039458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner// case KeyEvent.KEYCODE_BACK: 1040458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner// if (event.isTracking() && !event.isCanceled()) { 1041458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner// mPopup.dismiss(); 1042458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner// mContext.finish(); 1043458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner// return true; 1044458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner// } 1045458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner// break; 1046458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } 1047458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner return super.onKeyUp(keyCode, event); 1048458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } 1049458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner 1050458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner @Override 1051458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner public boolean onKeyDown(int keyCode, KeyEvent event) { 1052458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner if (mSelectionMode == SELECTION_HIDDEN) { 1053458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner if (keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT 1054458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_UP 1055458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { 1056458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // Display the selection box but don't move or select it 1057458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // on this key press. 1058458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner mSelectionMode = SELECTION_SELECTED; 1059458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner invalidate(); 1060458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner return true; 1061458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { 1062458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // Display the selection box but don't select it 1063458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner // on this key press. 1064458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner mSelectionMode = SELECTION_PRESSED; 1065458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner invalidate(); 1066458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner return true; 1067458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } 1068458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner } 1069458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner 1070458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner mSelectionMode = SELECTION_SELECTED; 1071458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner mScrolling = false; 1072fb5058ef67c054296c88db18ab1b3717845cb71dChris Lattner boolean redraw; 1073fb5058ef67c054296c88db18ab1b3717845cb71dChris Lattner int selectionDay = mSelectionDay; 1074458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner 1075458cd9c8a79b25b87dcea43c9d97a4c59f194799Chris Lattner switch (keyCode) { 1076b327793860737d1c103a73aeda8057dd628a101dChris Lattner case KeyEvent.KEYCODE_DEL: 1077b327793860737d1c103a73aeda8057dd628a101dChris Lattner // Delete the selected event, if any 1078b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson Event selectedEvent = mSelectedEvent; 1079ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek if (selectedEvent == null) { 1080ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek return false; 1081ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek } 1082ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mPopup.dismiss(); 1083ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mLastPopupEventID = INVALID_EVENT_ID; 1084b327793860737d1c103a73aeda8057dd628a101dChris Lattner 1085b327793860737d1c103a73aeda8057dd628a101dChris Lattner long begin = selectedEvent.startMillis; 1086b327793860737d1c103a73aeda8057dd628a101dChris Lattner long end = selectedEvent.endMillis; 1087b327793860737d1c103a73aeda8057dd628a101dChris Lattner long id = selectedEvent.id; 1088b327793860737d1c103a73aeda8057dd628a101dChris Lattner mDeleteEventHelper.delete(begin, end, id, -1); 1089b327793860737d1c103a73aeda8057dd628a101dChris Lattner return true; 1090ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek case KeyEvent.KEYCODE_ENTER: 1091ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek switchViews(true /* trackball or keyboard */); 1092b327793860737d1c103a73aeda8057dd628a101dChris Lattner return true; 1093b327793860737d1c103a73aeda8057dd628a101dChris Lattner case KeyEvent.KEYCODE_BACK: 1094b327793860737d1c103a73aeda8057dd628a101dChris Lattner if (event.getRepeatCount() == 0) { 1095b327793860737d1c103a73aeda8057dd628a101dChris Lattner event.startTracking(); 1096b327793860737d1c103a73aeda8057dd628a101dChris Lattner return true; 1097ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek } 1098b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson return super.onKeyDown(keyCode, event); 1099b327793860737d1c103a73aeda8057dd628a101dChris Lattner case KeyEvent.KEYCODE_DPAD_LEFT: 1100b327793860737d1c103a73aeda8057dd628a101dChris Lattner if (mSelectedEvent != null) { 1101b327793860737d1c103a73aeda8057dd628a101dChris Lattner mSelectedEvent = mSelectedEvent.nextLeft; 1102b327793860737d1c103a73aeda8057dd628a101dChris Lattner } 1103b327793860737d1c103a73aeda8057dd628a101dChris Lattner if (mSelectedEvent == null) { 1104b327793860737d1c103a73aeda8057dd628a101dChris Lattner mLastPopupEventID = INVALID_EVENT_ID; 1105b327793860737d1c103a73aeda8057dd628a101dChris Lattner selectionDay -= 1; 1106b327793860737d1c103a73aeda8057dd628a101dChris Lattner } 1107b327793860737d1c103a73aeda8057dd628a101dChris Lattner redraw = true; 1108b327793860737d1c103a73aeda8057dd628a101dChris Lattner break; 1109b327793860737d1c103a73aeda8057dd628a101dChris Lattner 111085759278332404e96d4bb89d0e976e46158cd026Chris Lattner case KeyEvent.KEYCODE_DPAD_RIGHT: 111185759278332404e96d4bb89d0e976e46158cd026Chris Lattner if (mSelectedEvent != null) { 111285759278332404e96d4bb89d0e976e46158cd026Chris Lattner mSelectedEvent = mSelectedEvent.nextRight; 111385759278332404e96d4bb89d0e976e46158cd026Chris Lattner } 1114b327793860737d1c103a73aeda8057dd628a101dChris Lattner if (mSelectedEvent == null) { 1115b327793860737d1c103a73aeda8057dd628a101dChris Lattner mLastPopupEventID = INVALID_EVENT_ID; 1116b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson selectionDay += 1; 1117ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek } 1118ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek redraw = true; 1119ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek break; 1120ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 1121b327793860737d1c103a73aeda8057dd628a101dChris Lattner case KeyEvent.KEYCODE_DPAD_UP: 1122b327793860737d1c103a73aeda8057dd628a101dChris Lattner if (mSelectedEvent != null) { 1123b327793860737d1c103a73aeda8057dd628a101dChris Lattner mSelectedEvent = mSelectedEvent.nextUp; 1124b327793860737d1c103a73aeda8057dd628a101dChris Lattner } 1125b327793860737d1c103a73aeda8057dd628a101dChris Lattner if (mSelectedEvent == null) { 1126b327793860737d1c103a73aeda8057dd628a101dChris Lattner mLastPopupEventID = INVALID_EVENT_ID; 1127ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek if (!mSelectionAllDay) { 1128ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mSelectionHour -= 1; 1129b327793860737d1c103a73aeda8057dd628a101dChris Lattner adjustHourSelection(); 1130ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mSelectedEvents.clear(); 1131ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mComputeSelectedEvents = true; 1132b327793860737d1c103a73aeda8057dd628a101dChris Lattner } 1133b327793860737d1c103a73aeda8057dd628a101dChris Lattner } 1134ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek redraw = true; 1135b327793860737d1c103a73aeda8057dd628a101dChris Lattner break; 1136b327793860737d1c103a73aeda8057dd628a101dChris Lattner 1137b327793860737d1c103a73aeda8057dd628a101dChris Lattner case KeyEvent.KEYCODE_DPAD_DOWN: 1138b327793860737d1c103a73aeda8057dd628a101dChris Lattner if (mSelectedEvent != null) { 1139cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor mSelectedEvent = mSelectedEvent.nextDown; 1140cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor } 1141cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor if (mSelectedEvent == null) { 1142cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor mLastPopupEventID = INVALID_EVENT_ID; 1143cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor if (mSelectionAllDay) { 1144cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor mSelectionAllDay = false; 1145cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor } else { 114610ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner mSelectionHour++; 114710ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner adjustHourSelection(); 114810ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner mSelectedEvents.clear(); 114910ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner mComputeSelectedEvents = true; 115010ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner } 115110ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner } 115210ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner redraw = true; 115310ca96ae9aed6906c3302403ef1a146a8d4c6b74Chris Lattner break; 1154d04c6e23f2e10eeb9936778d67f4a1c4a14cc4f6Anders Carlsson 1155b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson default: 1156b235fc2cf37621c7fc6511bb2b8788c95f9fb9fcAnders Carlsson return super.onKeyDown(keyCode, event); 1157d04c6e23f2e10eeb9936778d67f4a1c4a14cc4f6Anders Carlsson } 1158cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor 1159cd7d5a9dc558178ed7a66032f888781b3c592e4fDouglas Gregor if ((selectionDay < mFirstJulianDay) || (selectionDay > mLastJulianDay)) { 1160fe795956194141c91ae555985c9b930595bff43fChris Lattner DayView view = (DayView) mViewSwitcher.getNextView(); 1161fe795956194141c91ae555985c9b930595bff43fChris Lattner Time date = view.mBaseDate; 1162fe795956194141c91ae555985c9b930595bff43fChris Lattner date.set(mBaseDate); 1163fe795956194141c91ae555985c9b930595bff43fChris Lattner if (selectionDay < mFirstJulianDay) { 1164fe795956194141c91ae555985c9b930595bff43fChris Lattner date.monthDay -= mNumDays; 1165fe795956194141c91ae555985c9b930595bff43fChris Lattner } else { 1166fe795956194141c91ae555985c9b930595bff43fChris Lattner date.monthDay += mNumDays; 1167ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek } 1168ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek date.normalize(true /* ignore isDst */); 1169ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek view.mSelectionDay = selectionDay; 1170ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 1171ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek initView(view); 1172ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 1173ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek Time end = new Time(date); 1174ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek end.monthDay += mNumDays - 1; 1175ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek Log.d(TAG, "onKeyDown"); 1176ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mController.sendEvent(this, EventType.GO_TO, date, end, -1, ViewType.CURRENT); 1177ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek return true; 1178ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek } 1179ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mSelectionDay = selectionDay; 1180ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mSelectedEvents.clear(); 1181ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek mComputeSelectedEvents = true; 1182ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 1183ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek if (redraw) { 1184ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek invalidate(); 1185ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek return true; 1186ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek } 1187ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek 1188ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek return super.onKeyDown(keyCode, event); 1189ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek } 1190ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek 1191ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek private View switchViews(boolean forward, float xOffSet, float width) { 1192ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek if (DEBUG) Log.d(TAG, "switchViews(" + forward + ")..."); 1193ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek float progress = Math.abs(xOffSet) / width; 1194ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek if (progress > 1.0f) { 1195ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek progress = 1.0f; 1196ce2fc3a343ea6098a96d587071cee7299f11957aTed Kremenek } 1197ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek 119845b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson float inFromXValue, inToXValue; 119945b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson float outFromXValue, outToXValue; 120045b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson if (forward) { 120145b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson inFromXValue = 1.0f - progress; 120245b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson inToXValue = 0.0f; 120345b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson outFromXValue = -progress; 120445b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson outToXValue = -1.0f; 120545b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson } else { 120645b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson inFromXValue = progress - 1.0f; 120745b050e72d058131e6f169fe54888bb91a003fb5Anders Carlsson inToXValue = 0.0f; 1208ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek outFromXValue = progress; 1209ec2f7dccb1f30ae137f74e764e44c2332b0a2ec0Ted Kremenek outToXValue = 1.0f; 1210fe795956194141c91ae555985c9b930595bff43fChris Lattner } 1211fe795956194141c91ae555985c9b930595bff43fChris Lattner 12121f85acd5ff54345788b93111197088160fe9f06eTed Kremenek // We have to allocate these animation objects each time we switch views 12131f85acd5ff54345788b93111197088160fe9f06eTed Kremenek // because that is the only way to set the animation parameters. 1214e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop TranslateAnimation inAnimation = new TranslateAnimation( 1215fe795956194141c91ae555985c9b930595bff43fChris Lattner Animation.RELATIVE_TO_SELF, inFromXValue, 121678492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek Animation.RELATIVE_TO_SELF, inToXValue, 121778492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek Animation.ABSOLUTE, 0.0f, 121878492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek Animation.ABSOLUTE, 0.0f); 121978492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek 122078492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek TranslateAnimation outAnimation = new TranslateAnimation( 122178492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek Animation.RELATIVE_TO_SELF, outFromXValue, 122278492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek Animation.RELATIVE_TO_SELF, outToXValue, 122378492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek Animation.ABSOLUTE, 0.0f, 122478492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek Animation.ABSOLUTE, 0.0f); 122578492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek 122678492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek // Reduce the animation duration based on how far we have already swiped. 122778492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek long duration = (long) (ANIMATION_DURATION * (1.0f - progress)); 122878492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek inAnimation.setDuration(duration); 122978492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek outAnimation.setDuration(duration); 123078492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek mViewSwitcher.setInAnimation(inAnimation); 123178492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek mViewSwitcher.setOutAnimation(outAnimation); 123278492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek 123378492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek DayView view = (DayView) mViewSwitcher.getCurrentView(); 123478492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek view.cleanup(); 123578492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek mViewSwitcher.showNext(); 123678492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek view = (DayView) mViewSwitcher.getCurrentView(); 123778492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek view.requestFocus(); 123878492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek view.reloadEvents(); 123978492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek 124078492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek // Update the mini-month (but defer it until the animation 124178492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek // completes, to avoid stutter.) 124278492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek final Time start = new Time(view.mBaseDate); 124378492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek final Time end = new Time(view.mBaseDate); 124478492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek end.monthDay += mNumDays; 124578492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek end.normalize(true); 124678492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek postDelayed(new Runnable() { 124778492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek public void run() { 124878492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek mController.sendEvent(this, EventType.GO_TO, 124978492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek start, end, -1, ViewType.CURRENT); 125078492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek } 125178492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek }, duration); 125278492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek 125378492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek return view; 125478492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek } 125578492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek 1256e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop // This is called after scrolling stops to move the selected hour 125778492dae00fb85e0da0f966df4745edafdafb66cTed Kremenek // to the visible part of the screen. 1258b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian private void resetSelectedHour() { 1259a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek if (mSelectionHour < mFirstHour + 1) { 1260a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek mSelectionHour = mFirstHour + 1; 1261b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mSelectedEvent = null; 12627ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff mSelectedEvents.clear(); 12637ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff mComputeSelectedEvents = true; 12643b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian } else if (mSelectionHour > mFirstHour + mNumHours - 3) { 1265b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mSelectionHour = mFirstHour + mNumHours - 3; 1266af526779b36f43f59e47cfc5b10bb0c48562f4a9Ted Kremenek mSelectedEvent = null; 1267af526779b36f43f59e47cfc5b10bb0c48562f4a9Ted Kremenek mSelectedEvents.clear(); 1268a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek mComputeSelectedEvents = true; 1269a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek } 1270af526779b36f43f59e47cfc5b10bb0c48562f4a9Ted Kremenek } 1271b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 1272a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek private void initFirstHour() { 12737ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff mFirstHour = mSelectionHour - mNumHours / 5; 127493b2bdb866d49e3dab4623a822db281042e87382Daniel Dunbar if (mFirstHour < 0) { 1275b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mFirstHour = 0; 1276606efdfeb9eef70871eb62a26dfd115e209e899cFariborz Jahanian } else if (mFirstHour + mNumHours > 24) { 1277606efdfeb9eef70871eb62a26dfd115e209e899cFariborz Jahanian mFirstHour = 24 - mNumHours; 1278ff981021554a4965943ca2d19019cea811614602Ted Kremenek } 1279ff981021554a4965943ca2d19019cea811614602Ted Kremenek } 1280ff981021554a4965943ca2d19019cea811614602Ted Kremenek 1281ff981021554a4965943ca2d19019cea811614602Ted Kremenek /** 1282ff981021554a4965943ca2d19019cea811614602Ted Kremenek * Recomputes the first full hour that is visible on screen after the 1283ff981021554a4965943ca2d19019cea811614602Ted Kremenek * screen is scrolled. 1284ff981021554a4965943ca2d19019cea811614602Ted Kremenek */ 1285ff981021554a4965943ca2d19019cea811614602Ted Kremenek private void computeFirstHour() { 12867ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff // Compute the first full hour that is visible on screen 12877ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP); 12887ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY; 12897ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff } 12907ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff 129193b2bdb866d49e3dab4623a822db281042e87382Daniel Dunbar private void adjustHourSelection() { 12927794cb85d394750db0631c02b7aa7837ae56764cFariborz Jahanian if (mSelectionHour < 0) { 12937573098b83e780d1c5bea13b384b610d8f155676Steve Naroff mSelectionHour = 0; 12947573098b83e780d1c5bea13b384b610d8f155676Steve Naroff if (mMaxAllDayEvents > 0) { 1295b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mPrevSelectedEvent = null; 12963b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian mSelectionAllDay = true; 1297b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 1298be4b3332c2d17c03af6d015d9e8e2cc524806e6eSteve Naroff } 12997ba138abd329e591a8f6d5001f60dd7082f71b3bSteve Naroff 1300be4b3332c2d17c03af6d015d9e8e2cc524806e6eSteve Naroff if (mSelectionHour > 23) { 1301b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mSelectionHour = 23; 1302a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek } 1303b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 1304a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek // If the selected hour is at least 2 time slots from the top and 1305b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // bottom of the screen, then don't scroll the view. 1306b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (mSelectionHour < mFirstHour + 1) { 1307b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // If there are all-days events for the selected day but there 1308af526779b36f43f59e47cfc5b10bb0c48562f4a9Ted Kremenek // are no more normal events earlier in the day, then jump to 1309af526779b36f43f59e47cfc5b10bb0c48562f4a9Ted Kremenek // the all-day event area. 1310e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop // Exception 1: allow the user to scroll to 8am with the trackball 1311b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // before jumping to the all-day event area. 1312b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // Exception 2: if 12am is on screen, then allow the user to select 1313a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek // 12am before going up to the all-day event area. 1314a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek int daynum = mSelectionDay - mFirstJulianDay; 131504be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek if (mMaxAllDayEvents > 0 && mEarliestStartHour[daynum] > mSelectionHour 131604be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek && mFirstHour > 0 && mFirstHour < 8) { 131704be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek mPrevSelectedEvent = null; 1318a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek mSelectionAllDay = true; 1319a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek mSelectionHour = mFirstHour + 1; 132004be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek return; 13217794cb85d394750db0631c02b7aa7837ae56764cFariborz Jahanian } 132204be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek 132304be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek if (mFirstHour > 0) { 132404be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek mFirstHour -= 1; 132504be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek mViewStartY -= (mCellHeight + HOUR_GAP); 132604be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek if (mViewStartY < 0) { 132704be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek mViewStartY = 0; 132804be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek } 132904be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek return; 1330a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek } 133104be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek } 1332a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek 133304be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek if (mSelectionHour > mFirstHour + mNumHours - 3) { 133404be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek if (mFirstHour < 24 - mNumHours) { 133504be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek mFirstHour += 1; 133604be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek mViewStartY += (mCellHeight + HOUR_GAP); 133704be5aae2dc17292e2007401599638f3c6ba30deTed Kremenek if (mViewStartY > mMaxViewStartY) { 1338e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop mViewStartY = mMaxViewStartY; 1339b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 1340b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian return; 1341a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) { 1342b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mViewStartY = mMaxViewStartY; 1343a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek } 1344b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 134589079eac7d6e3091b74b477a4a5ff0cdaf35496cFariborz Jahanian } 134689079eac7d6e3091b74b477a4a5ff0cdaf35496cFariborz Jahanian 1347b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian void clearCachedEvents() { 13489c1efff14b0e001f73c875ed5b184fb3569d641dTed Kremenek mLastReloadMillis = 0; 1349b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 1350a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek 1351bd49a647afd9cc534fef13cadf652d4e9c396e2bFariborz Jahanian private Runnable mCancelCallback = new Runnable() { 1352bd49a647afd9cc534fef13cadf652d4e9c396e2bFariborz Jahanian public void run() { 1353a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek clearCachedEvents(); 1354b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 1355b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian }; 1356b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 1357ccdbc5c6f14965d91a352d114c109a9d4d9552b8Steve Naroff /* package */ void reloadEvents() { 1358b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // Protect against this being called before this view has been 1359b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // initialized. 13608aab17e82aab6b3f266ef1884253226d2cb8b8d8Fariborz Jahanian// if (mContext == null) { 13618aab17e82aab6b3f266ef1884253226d2cb8b8d8Fariborz Jahanian// return; 1362a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek// } 1363a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek 13647573098b83e780d1c5bea13b384b610d8f155676Steve Naroff // Make sure our time zones are up to date 1365a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek mTZUpdater.run(); 1366a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek 13677573098b83e780d1c5bea13b384b610d8f155676Steve Naroff mSelectedEvent = null; 1368a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek mPrevSelectedEvent = null; 1369a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek mSelectedEvents.clear(); 13707573098b83e780d1c5bea13b384b610d8f155676Steve Naroff 1371a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek // The start date is the beginning of the week at 12am 1372a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater)); 13737573098b83e780d1c5bea13b384b610d8f155676Steve Naroff weekStart.set(mBaseDate); 1374b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian weekStart.hour = 0; 1375b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian weekStart.minute = 0; 1376b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian weekStart.second = 0; 1377b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian long millis = weekStart.normalize(true /* ignore isDst */); 1378b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 1379a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek // Avoid reloading events unnecessarily. 1380b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (millis == mLastReloadMillis) { 1381a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek return; 1382b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 1383b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mLastReloadMillis = millis; 1384b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 13859c1efff14b0e001f73c875ed5b184fb3569d641dTed Kremenek // load events in the background 13869c1efff14b0e001f73c875ed5b184fb3569d641dTed Kremenek// mContext.startProgressSpinner(); 1387e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop final ArrayList<Event> events = new ArrayList<Event>(); 1388b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mEventLoader.loadEventsInBackground(mNumDays, events, millis, new Runnable() { 1389fe795956194141c91ae555985c9b930595bff43fChris Lattner public void run() { 1390c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian mEvents = events; 139178a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian if (mAllDayEvents == null) { 1392bd144b7ac70cc96cb1ff822347c2bb9b330729ecFariborz Jahanian mAllDayEvents = new ArrayList<Event>(); 139378a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian } else { 1394c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian mAllDayEvents.clear(); 1395c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian } 1396c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian 1397a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian // Create a shorter array for all day events 1398a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian for(Event e : events) { 1399c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian if (e.allDay) { 1400c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian mAllDayEvents.add(e); 1401c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian } 1402a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian } 1403c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian 1404a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian // New events, new layouts 1405a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian if (mLayouts == null || mLayouts.length < events.size()) { 1406a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian mLayouts = new StaticLayout[events.size()]; 1407a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian } else { 1408a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian Arrays.fill(mLayouts, null); 1409c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian } 141078a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian 141178a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) { 141278a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian mAllDayLayouts = new StaticLayout[events.size()]; 141378a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian } else { 141478a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian Arrays.fill(mAllDayLayouts, null); 141578a677bbb5fa115fa0995b5783adeeefad67167eFariborz Jahanian } 1416c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian 1417a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian mRemeasure = true; 1418a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian mComputeSelectedEvents = true; 1419a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian recalc(); 1420a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian// mContext.stopProgressSpinner(); 1421a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian invalidate(); 1422a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian } 1423c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian }, mCancelCallback); 1424c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian } 1425a0f55792409289d1d343023fa8292cff6355e538Fariborz Jahanian 1426c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian @Override 1427c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian protected void onDraw(Canvas canvas) { 1428c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian if (mRemeasure) { 1429c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian remeasure(getWidth(), getHeight()); 1430c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian mRemeasure = false; 1431c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian } 1432c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian canvas.save(); 1433fa3ee8e6776634caf064ba5928ca7699d317a280Fariborz Jahanian 1434fa3ee8e6776634caf064ba5928ca7699d317a280Fariborz Jahanian float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAllDayHeight; 1435fa3ee8e6776634caf064ba5928ca7699d317a280Fariborz Jahanian // offset canvas by the current drag and header position 1436c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian canvas.translate(-mViewStartX, yTranslate); 1437e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop // clip to everything below the allDay area 1438e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop Rect dest = mDestRect; 1439c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian dest.top = (int) (mFirstCell - yTranslate); 1440c385c90c68dfa376650e2facfbb444b2ec9bd110Fariborz Jahanian dest.bottom = (int) (mViewHeight - yTranslate); 1441a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek dest.left = 0; 1442a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek dest.right = mViewWidth; 144339f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian canvas.save(); 144439f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian canvas.clipRect(dest); 144539f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // Draw the movable part of the view 1446a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek doDraw(canvas); 1447a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek // restore to having no clip 144839f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian canvas.restore(); 144939f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian 145039f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { 1451e0def7589a8afa8a6acef13476bb3f882c104b91Chris Lattner float xTranslate; 1452e0def7589a8afa8a6acef13476bb3f882c104b91Chris Lattner if (mViewStartX > 0) { 145339f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian xTranslate = mViewWidth; 145420ebf8f15df48f27ce2bf7c1b3ae87565235600fSteve Naroff } else { 145520ebf8f15df48f27ce2bf7c1b3ae87565235600fSteve Naroff xTranslate = -mViewWidth; 145620ebf8f15df48f27ce2bf7c1b3ae87565235600fSteve Naroff } 1457db6ed1786bf460e1143f67d14bf2d71ad9856f81Chris Lattner // Move the canvas around to prep it for the next view 1458db6ed1786bf460e1143f67d14bf2d71ad9856f81Chris Lattner // specifically, shift it by a screen and undo the 145939f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // yTranslation which will be redone in the nextView's onDraw(). 146039f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian canvas.translate(xTranslate, -yTranslate); 146139f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian DayView nextView = (DayView) mViewSwitcher.getNextView(); 1462a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek 146339f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // Prevent infinite recursive calls to onDraw(). 1464a526c5c67e5a0473c340903ee542ce570119665fTed Kremenek nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE; 146539f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian 146639f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian nextView.onDraw(canvas); 146739f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // Move it back for this view 14685bdd4e300b432246b9d261702828e9875414128bTed Kremenek canvas.translate(-xTranslate, 0); 14695bdd4e300b432246b9d261702828e9875414128bTed Kremenek } else { 1470e2563ca02a519c2ad6d64dfed87d6e86c5d3c072Sam Bishop // If we drew another view we already translated it back 147139f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // If we didn't draw another view we should be at the edge of the 147239f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // screen 14734b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl canvas.translate(mViewStartX, -yTranslate); 14744b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl } 14754b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 14764b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl // Draw the fixed areas (that don't scroll) directly to the canvas. 14774b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl drawAfterScroll(canvas); 14784b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl mComputeSelectedEvents = false; 14794b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl canvas.restore(); 14804b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl } 14814b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 14824b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl private void drawAfterScroll(Canvas canvas) { 14834b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl Paint p = mPaint; 14844b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl Rect r = mRect; 14854b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 14864b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl if (mMaxAllDayEvents != 0) { 14874b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p); 14884b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl// drawUpperLeftCorner(r, canvas, p); 14894b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl } 14904b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 14914b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl drawScrollLine(r, canvas, p); 14924b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 14934b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl drawDayHeaderLoop(r, canvas, p); 14944b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 14954b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl // Draw the AM and PM indicators if we're in 12 hour mode 14964b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl if (!mIs24HourFormat) { 14974b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl drawAmPm(canvas, p); 14984b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl } 14994b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 15004b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl // Update the popup window showing the event details, but only if 15014b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl // we are not scrolling and we have focus. 15024b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl if (!mScrolling && isFocused()) { 15034b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl updateEventDetails(); 15044b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl } 15054b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl } 15064b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl 15074b07b2968f87f3cd5a3d8c76145f1cbfd718d42dSebastian Redl // This isn't really the upper-left corner. It's the square area just 15088351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // below the upper-left corner, above the hours and to the left of the 15098351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // all-day area. 15108351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) { 15118351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// p.setColor(mCalendarHourBackground); 15128351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// r.top = DAY_HEADER_HEIGHT; 15138351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// r.bottom = r.top + mAllDayHeight + ALLDAY_TOP_MARGIN; 15148351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// r.left = 0; 15158351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// r.right = mHoursWidth; 15168351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// canvas.drawRect(r, p); 15178351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl// } 15188351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl 15198351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl private void drawScrollLine(Rect r, Canvas canvas, Paint p) { 15208351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl p.setColor(mCalendarGridLineInnerHorizontalColor); 15218351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl p.setAntiAlias(false); 15228351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl 15238351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl int y = mFirstCell - 1; 15248351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl canvas.drawLine(0, y, mHoursWidth + (mCellWidth + DAY_GAP) * mNumDays, y, p); 15258351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl p.setAntiAlias(true); 15268351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl } 15278351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl 15288351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) { 15298351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // Draw the horizontal day background banner 15308351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // p.setColor(mCalendarDateBannerBackground); 15318351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // r.top = 0; 15328351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // r.bottom = DAY_HEADER_HEIGHT; 15338351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // r.left = 0; 15348351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP); 15358351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // canvas.drawRect(r, p); 15368351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // 15378351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // Fill the extra space on the right side with the default background 15388351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // r.left = r.right; 15398351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // r.right = mViewWidth; 15408351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // p.setColor(mCalendarGridAreaBackground); 15418351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl // canvas.drawRect(r, p); 15428351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl 15438351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl int todayNum = mTodayJulianDay - mFirstJulianDay; 15448351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl if (mNumDays > 1) { 15458351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl r.top = 0; 15468351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl r.bottom = DAY_HEADER_HEIGHT; 15478351da06ca3082dfd49dd8e3c1785a986920f57cSebastian Redl 15485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Highlight today 15495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (mFirstJulianDay <= mTodayJulianDay 15505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer && mTodayJulianDay < (mFirstJulianDay + mNumDays)) { 1551 r.left = mHoursWidth + todayNum * (mCellWidth + DAY_GAP); 1552 r.right = r.left + mCellWidth; 1553 mTodayHeaderDrawable.setBounds(r); 1554 mTodayHeaderDrawable.draw(canvas); 1555 } 1556 1557 // Draw a highlight on the selected day (if any), but only if we are 1558 // displaying more than one day. 1559 // 1560 // int selectedDayNum = mSelectionDay - mFirstJulianDay; 1561 // if (mSelectionMode != SELECTION_HIDDEN && selectedDayNum >= 0 1562 // && selectedDayNum < mNumDays) { 1563 // p.setColor(mCalendarDateSelected); 1564 // r.left = mHoursWidth + selectedDayNum * (mCellWidth + DAY_GAP); 1565 // r.right = r.left + mCellWidth; 1566 // canvas.drawRect(r, p); 1567 // } 1568 } 1569 1570 p.setTypeface(mBold); 1571 p.setTextAlign(Paint.Align.RIGHT); 1572 float x = mHoursWidth; 1573 int deltaX = mCellWidth + DAY_GAP; 1574 int cell = mFirstJulianDay; 1575 1576 String[] dayNames; 1577 if (mDateStrWidth < mCellWidth) { 1578 dayNames = mDayStrs; 1579 } else { 1580 dayNames = mDayStrs2Letter; 1581 } 1582 1583 p.setAntiAlias(true); 1584 for (int day = 0; day < mNumDays; day++, cell++) { 1585 int dayOfWeek = day + mFirstVisibleDayOfWeek; 1586 if (dayOfWeek >= 14) { 1587 dayOfWeek -= 14; 1588 } 1589 1590 int color = mCalendarDateBannerTextColor; 1591 if (mNumDays == 1) { 1592 if (dayOfWeek == Time.SATURDAY) { 1593 color = mWeek_saturdayColor; 1594 } else if (dayOfWeek == Time.SUNDAY) { 1595 color = mWeek_sundayColor; 1596 } 1597 } else { 1598 final int column = day % 7; 1599 if (Utils.isSaturday(column, mFirstDayOfWeek)) { 1600 color = mWeek_saturdayColor; 1601 } else if (Utils.isSunday(column, mFirstDayOfWeek)) { 1602 color = mWeek_sundayColor; 1603 } 1604 } 1605 1606 color &= 0x00FFFFFF; 1607 if (todayNum == day) { 1608 color |= DAY_HEADER_TODAY_ALPHA; 1609 } else { 1610 color |= DAY_HEADER_ALPHA; 1611 } 1612 1613 p.setColor(color); 1614 drawDayHeader(dayNames[dayOfWeek], day, cell, x, canvas, p); 1615 x += deltaX; 1616 } 1617 p.setTypeface(null); 1618 } 1619 1620 private void drawAmPm(Canvas canvas, Paint p) { 1621 p.setColor(mCalendarAmPmLabel); 1622 p.setTextSize(AMPM_FONT_SIZE); 1623 p.setTypeface(mBold); 1624 p.setAntiAlias(true); 1625 mPaint.setTextAlign(Paint.Align.RIGHT); 1626 String text = mAmString; 1627 if (mFirstHour >= 12) { 1628 text = mPmString; 1629 } 1630 int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP; 1631 int right = mHoursWidth - HOURS_RIGHT_MARGIN; 1632 canvas.drawText(text, right, y, p); 1633 1634 if (mFirstHour < 12 && mFirstHour + mNumHours > 12) { 1635 // Also draw the "PM" 1636 text = mPmString; 1637 y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) 1638 + 2 * mHoursTextHeight + HOUR_GAP; 1639 canvas.drawText(text, right, y, p); 1640 } 1641 } 1642 1643 private void drawCurrentTimeLine(Rect r, final int left, final int top, Canvas canvas, 1644 Paint p) { 1645 r.left = left - CURRENT_TIME_LINE_SIDE_BUFFER; 1646 r.right = left + mCellWidth + DAY_GAP + CURRENT_TIME_LINE_SIDE_BUFFER; 1647 1648 r.top = top - mCurrentTimeLine.getIntrinsicHeight() / 2; 1649 r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight(); 1650 1651 mCurrentTimeLine.setBounds(r); 1652 mCurrentTimeLine.draw(canvas); 1653 } 1654 1655 private void doDraw(Canvas canvas) { 1656 Paint p = mPaint; 1657 Rect r = mRect; 1658 1659 drawGridBackground(r, canvas, p); 1660 drawHours(r, canvas, p); 1661 1662 // Draw each day 1663 int x = mHoursWidth; 1664 int deltaX = mCellWidth + DAY_GAP; 1665 int cell = mFirstJulianDay; 1666 for (int day = 0; day < mNumDays; day++, cell++) { 1667 // TODO Wow, this needs cleanup. drawEvents loop through all the 1668 // events on every call. 1669 drawEvents(cell, x, HOUR_GAP, canvas, p); 1670 //If this is today 1671 if(cell == mTodayJulianDay) { 1672 int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP) 1673 + ((mCurrentTime.minute * mCellHeight) / 60) + 1; 1674 1675 //And the current time shows up somewhere on the screen 1676 if(lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) { 1677 drawCurrentTimeLine(r, x, lineY, canvas, p); 1678 } 1679 } 1680 x += deltaX; 1681 } 1682 } 1683 1684 private void drawHours(Rect r, Canvas canvas, Paint p) { 1685 // Comment out as the background will be a drawable 1686 1687 // Draw the background for the hour labels 1688 // p.setColor(mCalendarHourBackground); 1689 // r.top = 0; 1690 // r.bottom = 24 * (mCellHeight + HOUR_GAP) + HOUR_GAP; 1691 // r.left = 0; 1692 // r.right = mHoursWidth; 1693 // canvas.drawRect(r, p); 1694 1695 // Fill the bottom left corner with the default grid background 1696 // r.top = r.bottom; 1697 // r.bottom = mBitmapHeight; 1698 // p.setColor(mCalendarGridAreaBackground); 1699 // canvas.drawRect(r, p); 1700 1701 // Draw a highlight on the selected hour (if needed) 1702 if (mSelectionMode != SELECTION_HIDDEN && !mSelectionAllDay) { 1703 // p.setColor(mCalendarHourSelected); 1704 int daynum = mSelectionDay - mFirstJulianDay; 1705 r.top = mSelectionHour * (mCellHeight + HOUR_GAP); 1706 r.bottom = r.top + mCellHeight + HOUR_GAP; 1707 r.left = mHoursWidth + daynum * (mCellWidth + DAY_GAP); 1708 r.right = r.left + mCellWidth + DAY_GAP; 1709 1710 // Draw a border around the highlighted grid hour. 1711 // drawEmptyRect(canvas, r, mSelectionPaint.getColor()); 1712 saveSelectionPosition(r.left, r.top, r.right, r.bottom); 1713 1714 // Also draw the highlight on the grid 1715 p.setColor(mCalendarGridAreaSelected); 1716 r.top += HOUR_GAP; 1717 r.right -= DAY_GAP; 1718 canvas.drawRect(r, p); 1719 } 1720 1721 p.setColor(mCalendarHourLabel); 1722 p.setTextSize(HOURS_FONT_SIZE); 1723 p.setTypeface(mBold); 1724 p.setTextAlign(Paint.Align.RIGHT); 1725 p.setAntiAlias(true); 1726 1727 int right = mHoursWidth - HOURS_RIGHT_MARGIN; 1728 int y = HOUR_GAP + mHoursTextHeight; 1729 1730 for (int i = 0; i < 24; i++) { 1731 String time = mHourStrs[i]; 1732 canvas.drawText(time, right, y, p); 1733 y += mCellHeight + HOUR_GAP; 1734 } 1735 } 1736 1737 private void drawDayHeader(String dayStr, int day, int cell, float x, Canvas canvas, Paint p) { 1738 int dateNum = mFirstVisibleDate + day; 1739 if (dateNum > mMonthLength) { 1740 dateNum -= mMonthLength; 1741 } 1742 1743 // Draw day of the month 1744 String dateNumStr = String.valueOf(dateNum); 1745 if (mNumDays > 1) { 1746 float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN; 1747 1748 // Draw day of the month 1749 x += mCellWidth - DAY_HEADER_RIGHT_MARGIN; 1750 p.setTextSize(DATE_HEADER_FONT_SIZE); 1751 canvas.drawText(dateNumStr, x, y, p); 1752 1753 // Draw day of the week 1754 x -= p.measureText(dateNumStr) + DAY_HEADER_LEFT_MARGIN; 1755 p.setTextSize(DAY_HEADER_FONT_SIZE); 1756 canvas.drawText(dayStr, x, y, p); 1757 } else { 1758 float y = DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN; 1759 p.setTextAlign(Paint.Align.LEFT); 1760 1761 // Draw day of the week 1762 x += DAY_HEADER_ONE_DAY_LEFT_MARGIN; 1763 p.setTextSize(DAY_HEADER_FONT_SIZE); 1764 canvas.drawText(dayStr, x, y, p); 1765 1766 // Draw day of the month 1767 x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN; 1768 p.setTextSize(DATE_HEADER_FONT_SIZE); 1769 canvas.drawText(dateNumStr, x, y, p); 1770 } 1771 } 1772 1773 private void drawGridBackground(Rect r, Canvas canvas, Paint p) { 1774 Paint.Style savedStyle = p.getStyle(); 1775 1776 // Draw the outer horizontal grid lines 1777 p.setColor(mCalendarGridLineHorizontalColor); 1778 p.setStyle(Style.FILL); 1779 1780 p.setAntiAlias(false); 1781 final float startX = 0; 1782 final float stopX = mHoursWidth + (mCellWidth + DAY_GAP) * mNumDays; 1783 float y = 0; 1784 final float deltaY = mCellHeight + HOUR_GAP; 1785 p.setStrokeWidth(GRID_LINE_WIDTH); 1786 int linesIndex = 0; 1787 for (int hour = 0; hour <= 24; hour++) { 1788 mLines[linesIndex++] = startX; 1789 mLines[linesIndex++] = y; 1790 mLines[linesIndex++] = stopX; 1791 mLines[linesIndex++] = y; 1792 y += deltaY; 1793 } 1794 if (mCalendarGridLineVerticalColor != mCalendarGridLineHorizontalColor) { 1795 canvas.drawLines(mLines, 0, linesIndex, p); 1796 linesIndex = 0; 1797 p.setColor(mCalendarGridLineVerticalColor); 1798 } 1799 1800 // Draw the outer vertical grid lines 1801 final float startY = 0; 1802 final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP); 1803 final float deltaX = mCellWidth + DAY_GAP; 1804 float x = mHoursWidth; 1805 for (int day = 0; day < mNumDays; day++) { 1806 x += deltaX; 1807 mLines[linesIndex++] = x; 1808 mLines[linesIndex++] = startY; 1809 mLines[linesIndex++] = x; 1810 mLines[linesIndex++] = stopY; 1811 } 1812 canvas.drawLines(mLines, 0, linesIndex, p); 1813 1814 // Draw the inner horizontal grid lines 1815 p.setColor(mCalendarGridLineInnerHorizontalColor); 1816 p.setStrokeWidth(GRID_LINE_INNER_WIDTH); 1817 y = 0; 1818 linesIndex = 0; 1819 for (int hour = 0; hour <= 24; hour++) { 1820 mLines[linesIndex++] = startX; 1821 mLines[linesIndex++] = y; 1822 mLines[linesIndex++] = stopX; 1823 mLines[linesIndex++] = y; 1824 y += deltaY; 1825 } 1826 if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) { 1827 canvas.drawLines(mLines, 0, linesIndex, p); 1828 linesIndex = 0; 1829 p.setColor(mCalendarGridLineInnerVerticalColor); 1830 } 1831 1832 // Draw the inner vertical grid lines 1833 x = mHoursWidth; 1834 for (int day = 0; day < mNumDays; day++) { 1835 x += deltaX; 1836 mLines[linesIndex++] = x; 1837 mLines[linesIndex++] = startY; 1838 mLines[linesIndex++] = x; 1839 mLines[linesIndex++] = stopY; 1840 } 1841 canvas.drawLines(mLines, 0, linesIndex, p); 1842 1843 // Restore the saved style. 1844 p.setStyle(savedStyle); 1845 p.setAntiAlias(true); 1846 } 1847 1848 Event getSelectedEvent() { 1849 if (mSelectedEvent == null) { 1850 // There is no event at the selected hour, so create a new event. 1851 return getNewEvent(mSelectionDay, getSelectedTimeInMillis(), 1852 getSelectedMinutesSinceMidnight()); 1853 } 1854 return mSelectedEvent; 1855 } 1856 1857 boolean isEventSelected() { 1858 return (mSelectedEvent != null); 1859 } 1860 1861 Event getNewEvent() { 1862 return getNewEvent(mSelectionDay, getSelectedTimeInMillis(), 1863 getSelectedMinutesSinceMidnight()); 1864 } 1865 1866 static Event getNewEvent(int julianDay, long utcMillis, 1867 int minutesSinceMidnight) { 1868 Event event = Event.newInstance(); 1869 event.startDay = julianDay; 1870 event.endDay = julianDay; 1871 event.startMillis = utcMillis; 1872 event.endMillis = event.startMillis + MILLIS_PER_HOUR; 1873 event.startTime = minutesSinceMidnight; 1874 event.endTime = event.startTime + MINUTES_PER_HOUR; 1875 return event; 1876 } 1877 1878 private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) { 1879 float maxWidthF = 0.0f; 1880 1881 int len = strings.length; 1882 for (int i = 0; i < len; i++) { 1883 float width = p.measureText(strings[i]); 1884 maxWidthF = Math.max(width, maxWidthF); 1885 } 1886 int maxWidth = (int) (maxWidthF + 0.5); 1887 if (maxWidth < currentMax) { 1888 maxWidth = currentMax; 1889 } 1890 return maxWidth; 1891 } 1892 1893 private void saveSelectionPosition(float left, float top, float right, float bottom) { 1894 mPrevBox.left = (int) left; 1895 mPrevBox.right = (int) right; 1896 mPrevBox.top = (int) top; 1897 mPrevBox.bottom = (int) bottom; 1898 } 1899 1900 private Rect getCurrentSelectionPosition() { 1901 Rect box = new Rect(); 1902 box.top = mSelectionHour * (mCellHeight + HOUR_GAP); 1903 box.bottom = box.top + mCellHeight + HOUR_GAP; 1904 int daynum = mSelectionDay - mFirstJulianDay; 1905 box.left = mHoursWidth + daynum * (mCellWidth + DAY_GAP); 1906 box.right = box.left + mCellWidth + DAY_GAP; 1907 return box; 1908 } 1909 1910 private void setupTextRect(Rect r) { 1911 if (r.bottom <= r.top || r.right <= r.left) { 1912 r.bottom = r.top; 1913 r.right = r.left; 1914 return; 1915 } 1916 1917 if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) { 1918 r.top += EVENT_TEXT_TOP_MARGIN; 1919 r.bottom -= EVENT_TEXT_BOTTOM_MARGIN; 1920 } 1921 if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) { 1922 r.left += EVENT_TEXT_LEFT_MARGIN; 1923 r.right -= EVENT_TEXT_RIGHT_MARGIN; 1924 } 1925 } 1926 1927 private void setupAllDayTextRect(Rect r) { 1928 if (r.bottom <= r.top || r.right <= r.left) { 1929 r.bottom = r.top; 1930 r.right = r.left; 1931 return; 1932 } 1933 1934 if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) { 1935 r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN; 1936 r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN; 1937 } 1938 if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) { 1939 r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN; 1940 r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN; 1941 } 1942 } 1943 1944 /** 1945 * Return the layout for a numbered event. Create it if not already existing 1946 */ 1947 private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint, 1948 Rect r) { 1949 if (i < 0 || i >= layouts.length) { 1950 return null; 1951 } 1952 1953 StaticLayout layout = layouts[i]; 1954 // Check if we have already initialized the StaticLayout and that 1955 // the width hasn't changed (due to vertical resizing which causes 1956 // re-layout of events at min height) 1957 if (layout == null || r.width() != layout.getWidth()) { 1958 String text = drawTextSanitizer(event.getTitleAndLocation(), MAX_EVENT_TEXT_LEN); 1959 1960 // Leave a one pixel boundary on the left and right of the rectangle for the event 1961 layout = new StaticLayout(text, 0, text.length(), new TextPaint(paint), r.width(), 1962 Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width()); 1963 1964 layouts[i] = layout; 1965 } 1966 1967 return layout; 1968 } 1969 1970 private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) { 1971 if (mSelectionAllDay) { 1972 // Draw the highlight on the selected all-day area 1973 mRect.top = DAY_HEADER_HEIGHT + 1; 1974 mRect.bottom = mRect.top + mAllDayHeight + ALLDAY_TOP_MARGIN - 2; 1975 int daynum = mSelectionDay - mFirstJulianDay; 1976 mRect.left = mHoursWidth + daynum * (mCellWidth + DAY_GAP); 1977 mRect.right = mRect.left + mCellWidth + DAY_GAP - 1; 1978 p.setColor(mCalendarGridAreaSelected); 1979 canvas.drawRect(mRect, p); 1980 } 1981 1982 p.setTextSize(NORMAL_FONT_SIZE); 1983 p.setTextAlign(Paint.Align.LEFT); 1984 Paint eventTextPaint = mEventTextPaint; 1985 1986 // Draw the background for the all-day events area 1987 // r.top = DAY_HEADER_HEIGHT; 1988 // r.bottom = r.top + mAllDayHeight + ALLDAY_TOP_MARGIN; 1989 // r.left = mHoursWidth; 1990 // r.right = r.left + mNumDays * (mCellWidth + DAY_GAP); 1991 // p.setColor(mCalendarAllDayBackground); 1992 // canvas.drawRect(r, p); 1993 1994 // Fill the extra space on the right side with the default background 1995 // r.left = r.right; 1996 // r.right = mViewWidth; 1997 // p.setColor(mCalendarGridAreaBackground); 1998 // canvas.drawRect(r, p); 1999 2000 // Draw the outer vertical grid lines 2001 p.setColor(mCalendarGridLineVerticalColor); 2002 p.setStyle(Style.FILL); 2003 p.setStrokeWidth(GRID_LINE_WIDTH); 2004 p.setAntiAlias(false); 2005 final float startY = DAY_HEADER_HEIGHT; 2006 final float stopY = startY + mAllDayHeight + ALLDAY_TOP_MARGIN; 2007 final float deltaX = mCellWidth + DAY_GAP; 2008 float x = mHoursWidth; 2009 int linesIndex = 0; 2010 // Line bounding the top of the all day area 2011 mLines[linesIndex++] = 0; 2012 mLines[linesIndex++] = startY; 2013 mLines[linesIndex++] = mHoursWidth + deltaX * mNumDays; 2014 mLines[linesIndex++] = startY; 2015 2016 for (int day = 0; day < mNumDays; day++) { 2017 x += deltaX; 2018 mLines[linesIndex++] = x; 2019 mLines[linesIndex++] = startY; 2020 mLines[linesIndex++] = x; 2021 mLines[linesIndex++] = stopY; 2022 } 2023 canvas.drawLines(mLines, 0, linesIndex, p); 2024 2025 // Draw the inner vertical grid lines 2026 p.setColor(mCalendarGridLineInnerVerticalColor); 2027 x = mHoursWidth; 2028 p.setStrokeWidth(GRID_LINE_INNER_WIDTH); 2029 linesIndex = 0; 2030 // Line bounding the top of the all day area 2031 mLines[linesIndex++] = 0; 2032 mLines[linesIndex++] = startY; 2033 mLines[linesIndex++] = mHoursWidth + (deltaX) * mNumDays; 2034 mLines[linesIndex++] = startY; 2035 2036 for (int day = 0; day < mNumDays; day++) { 2037 x += deltaX; 2038 mLines[linesIndex++] = x; 2039 mLines[linesIndex++] = startY; 2040 mLines[linesIndex++] = x; 2041 mLines[linesIndex++] = stopY; 2042 } 2043 canvas.drawLines(mLines, 0, linesIndex, p); 2044 2045 p.setAntiAlias(true); 2046 p.setStyle(Style.FILL); 2047 2048 int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; 2049 float left = mHoursWidth; 2050 int lastDay = firstDay + numDays - 1; 2051 final ArrayList<Event> events = mAllDayEvents; 2052 int numEvents = events.size(); 2053 float drawHeight = mAllDayHeight; 2054 float numRectangles = mMaxAllDayEvents; 2055 for (int i = 0; i < numEvents; i++) { 2056 Event event = events.get(i); 2057 int startDay = event.startDay; 2058 int endDay = event.endDay; 2059 if (startDay > lastDay || endDay < firstDay) { 2060 continue; 2061 } 2062 if (startDay < firstDay) { 2063 startDay = firstDay; 2064 } 2065 if (endDay > lastDay) { 2066 endDay = lastDay; 2067 } 2068 int startIndex = startDay - firstDay; 2069 int endIndex = endDay - firstDay; 2070 float height = drawHeight / numRectangles; 2071 2072 // Prevent a single event from getting too big 2073 if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { 2074 height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; 2075 } 2076 2077 // Leave a one-pixel space between the vertical day lines and the 2078 // event rectangle. 2079 event.left = left + startIndex * (mCellWidth + DAY_GAP); 2080 event.right = left + endIndex * (mCellWidth + DAY_GAP) + mCellWidth; 2081 event.top = y + height * event.getColumn(); 2082 event.bottom = event.top + height; 2083 2084 Rect r = drawEventRect(event, canvas, p, eventTextPaint); 2085 setupAllDayTextRect(r); 2086 StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r); 2087 drawEventText(layout, r, canvas, r.top, r.bottom); 2088 2089 // Check if this all-day event intersects the selected day 2090 if (mSelectionAllDay && mComputeSelectedEvents) { 2091 if (startDay <= mSelectionDay && endDay >= mSelectionDay) { 2092 mSelectedEvents.add(event); 2093 } 2094 } 2095 } 2096 2097 if (mSelectionAllDay) { 2098 // Compute the neighbors for the list of all-day events that 2099 // intersect the selected day. 2100 computeAllDayNeighbors(); 2101 2102 // Set the selection position to zero so that when we move down 2103 // to the normal event area, we will highlight the topmost event. 2104 saveSelectionPosition(0f, 0f, 0f, 0f); 2105 } 2106 } 2107 2108 private void computeAllDayNeighbors() { 2109 int len = mSelectedEvents.size(); 2110 if (len == 0 || mSelectedEvent != null) { 2111 return; 2112 } 2113 2114 // First, clear all the links 2115 for (int ii = 0; ii < len; ii++) { 2116 Event ev = mSelectedEvents.get(ii); 2117 ev.nextUp = null; 2118 ev.nextDown = null; 2119 ev.nextLeft = null; 2120 ev.nextRight = null; 2121 } 2122 2123 // For each event in the selected event list "mSelectedEvents", find 2124 // its neighbors in the up and down directions. This could be done 2125 // more efficiently by sorting on the Event.getColumn() field, but 2126 // the list is expected to be very small. 2127 2128 // Find the event in the same row as the previously selected all-day 2129 // event, if any. 2130 int startPosition = -1; 2131 if (mPrevSelectedEvent != null && mPrevSelectedEvent.allDay) { 2132 startPosition = mPrevSelectedEvent.getColumn(); 2133 } 2134 int maxPosition = -1; 2135 Event startEvent = null; 2136 Event maxPositionEvent = null; 2137 for (int ii = 0; ii < len; ii++) { 2138 Event ev = mSelectedEvents.get(ii); 2139 int position = ev.getColumn(); 2140 if (position == startPosition) { 2141 startEvent = ev; 2142 } else if (position > maxPosition) { 2143 maxPositionEvent = ev; 2144 maxPosition = position; 2145 } 2146 for (int jj = 0; jj < len; jj++) { 2147 if (jj == ii) { 2148 continue; 2149 } 2150 Event neighbor = mSelectedEvents.get(jj); 2151 int neighborPosition = neighbor.getColumn(); 2152 if (neighborPosition == position - 1) { 2153 ev.nextUp = neighbor; 2154 } else if (neighborPosition == position + 1) { 2155 ev.nextDown = neighbor; 2156 } 2157 } 2158 } 2159 if (startEvent != null) { 2160 mSelectedEvent = startEvent; 2161 } else { 2162 mSelectedEvent = maxPositionEvent; 2163 } 2164 } 2165 2166 private void drawEvents(int date, int left, int top, Canvas canvas, Paint p) { 2167 Paint eventTextPaint = mEventTextPaint; 2168 int cellWidth = mCellWidth; 2169 int cellHeight = mCellHeight; 2170 2171 // Use the selected hour as the selection region 2172 Rect selectionArea = mRect; 2173 selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP); 2174 selectionArea.bottom = selectionArea.top + cellHeight; 2175 selectionArea.left = left; 2176 selectionArea.right = selectionArea.left + cellWidth; 2177 2178 final ArrayList<Event> events = mEvents; 2179 int numEvents = events.size(); 2180 EventGeometry geometry = mEventGeometry; 2181 2182 final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAllDayHeight; 2183 for (int i = 0; i < numEvents; i++) { 2184 Event event = events.get(i); 2185 if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { 2186 continue; 2187 } 2188 2189 // Don't draw it if it is not visible 2190 if (event.bottom < mViewStartY || event.top > viewEndY) { 2191 continue; 2192 } 2193 2194 if (date == mSelectionDay && !mSelectionAllDay && mComputeSelectedEvents 2195 && geometry.eventIntersectsSelection(event, selectionArea)) { 2196 mSelectedEvents.add(event); 2197 } 2198 2199 Rect r = drawEventRect(event, canvas, p, eventTextPaint); 2200 setupTextRect(r); 2201 2202 // Don't draw text if it is not visible 2203 if (r.top > viewEndY || r.bottom < mViewStartY) { 2204 continue; 2205 } 2206 StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r); 2207 // TODO: not sure why we are 4 pixels off 2208 drawEventText(layout, r, canvas, mViewStartY + 4, 2209 mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAllDayHeight); 2210 } 2211 2212 if (date == mSelectionDay && !mSelectionAllDay && isFocused() 2213 && mSelectionMode != SELECTION_HIDDEN) { 2214 computeNeighbors(); 2215 } 2216 } 2217 2218 // Computes the "nearest" neighbor event in four directions (left, right, 2219 // up, down) for each of the events in the mSelectedEvents array. 2220 private void computeNeighbors() { 2221 int len = mSelectedEvents.size(); 2222 if (len == 0 || mSelectedEvent != null) { 2223 return; 2224 } 2225 2226 // First, clear all the links 2227 for (int ii = 0; ii < len; ii++) { 2228 Event ev = mSelectedEvents.get(ii); 2229 ev.nextUp = null; 2230 ev.nextDown = null; 2231 ev.nextLeft = null; 2232 ev.nextRight = null; 2233 } 2234 2235 Event startEvent = mSelectedEvents.get(0); 2236 int startEventDistance1 = 100000; // any large number 2237 int startEventDistance2 = 100000; // any large number 2238 int prevLocation = FROM_NONE; 2239 int prevTop; 2240 int prevBottom; 2241 int prevLeft; 2242 int prevRight; 2243 int prevCenter = 0; 2244 Rect box = getCurrentSelectionPosition(); 2245 if (mPrevSelectedEvent != null) { 2246 prevTop = (int) mPrevSelectedEvent.top; 2247 prevBottom = (int) mPrevSelectedEvent.bottom; 2248 prevLeft = (int) mPrevSelectedEvent.left; 2249 prevRight = (int) mPrevSelectedEvent.right; 2250 // Check if the previously selected event intersects the previous 2251 // selection box. (The previously selected event may be from a 2252 // much older selection box.) 2253 if (prevTop >= mPrevBox.bottom || prevBottom <= mPrevBox.top 2254 || prevRight <= mPrevBox.left || prevLeft >= mPrevBox.right) { 2255 mPrevSelectedEvent = null; 2256 prevTop = mPrevBox.top; 2257 prevBottom = mPrevBox.bottom; 2258 prevLeft = mPrevBox.left; 2259 prevRight = mPrevBox.right; 2260 } else { 2261 // Clip the top and bottom to the previous selection box. 2262 if (prevTop < mPrevBox.top) { 2263 prevTop = mPrevBox.top; 2264 } 2265 if (prevBottom > mPrevBox.bottom) { 2266 prevBottom = mPrevBox.bottom; 2267 } 2268 } 2269 } else { 2270 // Just use the previously drawn selection box 2271 prevTop = mPrevBox.top; 2272 prevBottom = mPrevBox.bottom; 2273 prevLeft = mPrevBox.left; 2274 prevRight = mPrevBox.right; 2275 } 2276 2277 // Figure out where we came from and compute the center of that area. 2278 if (prevLeft >= box.right) { 2279 // The previously selected event was to the right of us. 2280 prevLocation = FROM_RIGHT; 2281 prevCenter = (prevTop + prevBottom) / 2; 2282 } else if (prevRight <= box.left) { 2283 // The previously selected event was to the left of us. 2284 prevLocation = FROM_LEFT; 2285 prevCenter = (prevTop + prevBottom) / 2; 2286 } else if (prevBottom <= box.top) { 2287 // The previously selected event was above us. 2288 prevLocation = FROM_ABOVE; 2289 prevCenter = (prevLeft + prevRight) / 2; 2290 } else if (prevTop >= box.bottom) { 2291 // The previously selected event was below us. 2292 prevLocation = FROM_BELOW; 2293 prevCenter = (prevLeft + prevRight) / 2; 2294 } 2295 2296 // For each event in the selected event list "mSelectedEvents", search 2297 // all the other events in that list for the nearest neighbor in 4 2298 // directions. 2299 for (int ii = 0; ii < len; ii++) { 2300 Event ev = mSelectedEvents.get(ii); 2301 2302 int startTime = ev.startTime; 2303 int endTime = ev.endTime; 2304 int left = (int) ev.left; 2305 int right = (int) ev.right; 2306 int top = (int) ev.top; 2307 if (top < box.top) { 2308 top = box.top; 2309 } 2310 int bottom = (int) ev.bottom; 2311 if (bottom > box.bottom) { 2312 bottom = box.bottom; 2313 } 2314 if (false) { 2315 int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL 2316 | DateUtils.FORMAT_CAP_NOON_MIDNIGHT; 2317 if (DateFormat.is24HourFormat(mContext)) { 2318 flags |= DateUtils.FORMAT_24HOUR; 2319 } 2320 String timeRange = DateUtils.formatDateRange(mContext, 2321 ev.startMillis, ev.endMillis, flags); 2322 Log.i("Cal", "left: " + left + " right: " + right + " top: " + top 2323 + " bottom: " + bottom + " ev: " + timeRange + " " + ev.title); 2324 } 2325 int upDistanceMin = 10000; // any large number 2326 int downDistanceMin = 10000; // any large number 2327 int leftDistanceMin = 10000; // any large number 2328 int rightDistanceMin = 10000; // any large number 2329 Event upEvent = null; 2330 Event downEvent = null; 2331 Event leftEvent = null; 2332 Event rightEvent = null; 2333 2334 // Pick the starting event closest to the previously selected event, 2335 // if any. distance1 takes precedence over distance2. 2336 int distance1 = 0; 2337 int distance2 = 0; 2338 if (prevLocation == FROM_ABOVE) { 2339 if (left >= prevCenter) { 2340 distance1 = left - prevCenter; 2341 } else if (right <= prevCenter) { 2342 distance1 = prevCenter - right; 2343 } 2344 distance2 = top - prevBottom; 2345 } else if (prevLocation == FROM_BELOW) { 2346 if (left >= prevCenter) { 2347 distance1 = left - prevCenter; 2348 } else if (right <= prevCenter) { 2349 distance1 = prevCenter - right; 2350 } 2351 distance2 = prevTop - bottom; 2352 } else if (prevLocation == FROM_LEFT) { 2353 if (bottom <= prevCenter) { 2354 distance1 = prevCenter - bottom; 2355 } else if (top >= prevCenter) { 2356 distance1 = top - prevCenter; 2357 } 2358 distance2 = left - prevRight; 2359 } else if (prevLocation == FROM_RIGHT) { 2360 if (bottom <= prevCenter) { 2361 distance1 = prevCenter - bottom; 2362 } else if (top >= prevCenter) { 2363 distance1 = top - prevCenter; 2364 } 2365 distance2 = prevLeft - right; 2366 } 2367 if (distance1 < startEventDistance1 2368 || (distance1 == startEventDistance1 && distance2 < startEventDistance2)) { 2369 startEvent = ev; 2370 startEventDistance1 = distance1; 2371 startEventDistance2 = distance2; 2372 } 2373 2374 // For each neighbor, figure out if it is above or below or left 2375 // or right of me and compute the distance. 2376 for (int jj = 0; jj < len; jj++) { 2377 if (jj == ii) { 2378 continue; 2379 } 2380 Event neighbor = mSelectedEvents.get(jj); 2381 int neighborLeft = (int) neighbor.left; 2382 int neighborRight = (int) neighbor.right; 2383 if (neighbor.endTime <= startTime) { 2384 // This neighbor is entirely above me. 2385 // If we overlap the same column, then compute the distance. 2386 if (neighborLeft < right && neighborRight > left) { 2387 int distance = startTime - neighbor.endTime; 2388 if (distance < upDistanceMin) { 2389 upDistanceMin = distance; 2390 upEvent = neighbor; 2391 } else if (distance == upDistanceMin) { 2392 int center = (left + right) / 2; 2393 int currentDistance = 0; 2394 int currentLeft = (int) upEvent.left; 2395 int currentRight = (int) upEvent.right; 2396 if (currentRight <= center) { 2397 currentDistance = center - currentRight; 2398 } else if (currentLeft >= center) { 2399 currentDistance = currentLeft - center; 2400 } 2401 2402 int neighborDistance = 0; 2403 if (neighborRight <= center) { 2404 neighborDistance = center - neighborRight; 2405 } else if (neighborLeft >= center) { 2406 neighborDistance = neighborLeft - center; 2407 } 2408 if (neighborDistance < currentDistance) { 2409 upDistanceMin = distance; 2410 upEvent = neighbor; 2411 } 2412 } 2413 } 2414 } else if (neighbor.startTime >= endTime) { 2415 // This neighbor is entirely below me. 2416 // If we overlap the same column, then compute the distance. 2417 if (neighborLeft < right && neighborRight > left) { 2418 int distance = neighbor.startTime - endTime; 2419 if (distance < downDistanceMin) { 2420 downDistanceMin = distance; 2421 downEvent = neighbor; 2422 } else if (distance == downDistanceMin) { 2423 int center = (left + right) / 2; 2424 int currentDistance = 0; 2425 int currentLeft = (int) downEvent.left; 2426 int currentRight = (int) downEvent.right; 2427 if (currentRight <= center) { 2428 currentDistance = center - currentRight; 2429 } else if (currentLeft >= center) { 2430 currentDistance = currentLeft - center; 2431 } 2432 2433 int neighborDistance = 0; 2434 if (neighborRight <= center) { 2435 neighborDistance = center - neighborRight; 2436 } else if (neighborLeft >= center) { 2437 neighborDistance = neighborLeft - center; 2438 } 2439 if (neighborDistance < currentDistance) { 2440 downDistanceMin = distance; 2441 downEvent = neighbor; 2442 } 2443 } 2444 } 2445 } 2446 2447 if (neighborLeft >= right) { 2448 // This neighbor is entirely to the right of me. 2449 // Take the closest neighbor in the y direction. 2450 int center = (top + bottom) / 2; 2451 int distance = 0; 2452 int neighborBottom = (int) neighbor.bottom; 2453 int neighborTop = (int) neighbor.top; 2454 if (neighborBottom <= center) { 2455 distance = center - neighborBottom; 2456 } else if (neighborTop >= center) { 2457 distance = neighborTop - center; 2458 } 2459 if (distance < rightDistanceMin) { 2460 rightDistanceMin = distance; 2461 rightEvent = neighbor; 2462 } else if (distance == rightDistanceMin) { 2463 // Pick the closest in the x direction 2464 int neighborDistance = neighborLeft - right; 2465 int currentDistance = (int) rightEvent.left - right; 2466 if (neighborDistance < currentDistance) { 2467 rightDistanceMin = distance; 2468 rightEvent = neighbor; 2469 } 2470 } 2471 } else if (neighborRight <= left) { 2472 // This neighbor is entirely to the left of me. 2473 // Take the closest neighbor in the y direction. 2474 int center = (top + bottom) / 2; 2475 int distance = 0; 2476 int neighborBottom = (int) neighbor.bottom; 2477 int neighborTop = (int) neighbor.top; 2478 if (neighborBottom <= center) { 2479 distance = center - neighborBottom; 2480 } else if (neighborTop >= center) { 2481 distance = neighborTop - center; 2482 } 2483 if (distance < leftDistanceMin) { 2484 leftDistanceMin = distance; 2485 leftEvent = neighbor; 2486 } else if (distance == leftDistanceMin) { 2487 // Pick the closest in the x direction 2488 int neighborDistance = left - neighborRight; 2489 int currentDistance = left - (int) leftEvent.right; 2490 if (neighborDistance < currentDistance) { 2491 leftDistanceMin = distance; 2492 leftEvent = neighbor; 2493 } 2494 } 2495 } 2496 } 2497 ev.nextUp = upEvent; 2498 ev.nextDown = downEvent; 2499 ev.nextLeft = leftEvent; 2500 ev.nextRight = rightEvent; 2501 } 2502 mSelectedEvent = startEvent; 2503 } 2504 2505 private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint) { 2506 // Draw the Event Rect 2507 Rect r = mRect; 2508 r.top = (int) event.top + EVENT_RECT_TOP_MARGIN; 2509 r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN; 2510 r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; 2511 r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN; 2512 2513 mEventBoxDrawable.setBounds(r); 2514 mEventBoxDrawable.draw(canvas); 2515// drawEmptyRect(canvas, r, 0xFF00FF00); // for debugging 2516 2517 int eventTextColor = mEventTextColor; 2518 p.setStyle(Style.FILL); 2519 2520 // If this event is selected, then use the selection color 2521 if (mSelectedEvent == event) { 2522 boolean paintIt = false; 2523 int color = 0; 2524 if (mSelectionMode == SELECTION_PRESSED) { 2525 // Also, remember the last selected event that we drew 2526 mPrevSelectedEvent = event; 2527 // box = mBoxPressed; 2528 color = mPressedColor; 2529 eventTextColor = mSelectedEventTextColor; 2530 paintIt = true; 2531 } else if (mSelectionMode == SELECTION_SELECTED) { 2532 // Also, remember the last selected event that we drew 2533 mPrevSelectedEvent = event; 2534 // box = mBoxSelected; 2535 color = mPressedColor; 2536 eventTextColor = mSelectedEventTextColor; 2537 paintIt = true; 2538 } else if (mSelectionMode == SELECTION_LONGPRESS) { 2539 // box = mBoxLongPressed; 2540 color = mPressedColor; 2541 eventTextColor = mSelectedEventTextColor; 2542 paintIt = true; 2543 } 2544 2545 if (paintIt) { 2546 p.setColor(color); 2547 canvas.drawRect(r, p); 2548 } 2549 } 2550 2551 eventTextPaint.setColor(eventTextColor); 2552 // Draw cal color square border 2553 r.top = (int) event.top + CALENDAR_COLOR_SQUARE_V_OFFSET; 2554 r.left = (int) event.left + CALENDAR_COLOR_SQUARE_H_OFFSET; 2555 r.bottom = r.top + CALENDAR_COLOR_SQUARE_SIZE + 1; 2556 r.right = r.left + CALENDAR_COLOR_SQUARE_SIZE + 1; 2557 p.setColor(0xFFFFFFFF); 2558 canvas.drawRect(r, p); 2559 2560 // Draw cal color 2561 r.top++; 2562 r.left++; 2563 r.bottom--; 2564 r.right--; 2565 p.setColor(event.color); 2566 canvas.drawRect(r, p); 2567 2568 boolean declined = (event.selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED); 2569 if (declined) { 2570 boolean aa = p.isAntiAlias(); 2571 if (!aa) { 2572 p.setAntiAlias(true); 2573 } 2574 // Temp behavior 2575 p.setColor(0x88FFFFFF); 2576 canvas.drawLine(r.right, r.top, r.left, r.bottom, p); 2577 if (!aa) { 2578 p.setAntiAlias(false); 2579 } 2580 } 2581 2582 // Setup rect for drawEventText which follows 2583 r.top = (int) event.top + EVENT_RECT_TOP_MARGIN; 2584 r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN; 2585 r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN; 2586 r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN; 2587 return r; 2588 } 2589 2590 private Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],"); 2591 2592 // Sanitize a string before passing it to drawText or else we get little 2593 // squares. For newlines and tabs before a comma, delete the character. 2594 // Otherwise, just replace them with a space. 2595 private String drawTextSanitizer(String string, int maxEventTextLen) { 2596 Matcher m = drawTextSanitizerFilter.matcher(string); 2597 string = m.replaceAll(","); 2598 2599 int len = string.length(); 2600 if (len > maxEventTextLen) { 2601 string = string.substring(0, maxEventTextLen); 2602 len = maxEventTextLen; 2603 } 2604 2605 return string.replace('\n', ' '); 2606 } 2607 2608 private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top, 2609 int bottom) { 2610 // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging 2611 2612 int width = rect.right - rect.left; 2613 int height = rect.bottom - rect.top; 2614 2615 // If the rectangle is too small for text, then return 2616 if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) { 2617 return; 2618 } 2619 2620 int totalLineHeight = 0; 2621 int lineCount = eventLayout.getLineCount(); 2622 for (int i = 0; i < lineCount; i++) { 2623 int lineBottom = eventLayout.getLineBottom(i); 2624 if (lineBottom <= height) { 2625 totalLineHeight = lineBottom; 2626 } else { 2627 break; 2628 } 2629 } 2630 2631 if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight < top) { 2632 return; 2633 } 2634 2635 // Use a StaticLayout to format the string. 2636 canvas.save(); 2637 canvas.translate(rect.left, rect.top); 2638 rect.left = 0; 2639 rect.right = width; 2640 rect.top = 0; 2641 rect.bottom = totalLineHeight; 2642 2643 // There's a bug somewhere. If this rect is outside of a previous 2644 // cliprect, this becomes a no-op. What happens is that the text draw 2645 // past the event rect. The current fix is to not draw the staticLayout 2646 // at all if it is completely out of bound. 2647 canvas.clipRect(rect); 2648 eventLayout.draw(canvas); 2649 canvas.restore(); 2650 } 2651 2652 // This is to replace p.setStyle(Style.STROKE); canvas.drawRect() since it 2653 // doesn't work well with hardware acceleration 2654 private void drawEmptyRect(Canvas canvas, Rect r, int color) { 2655 int linesIndex = 0; 2656 mLines[linesIndex++] = r.left; 2657 mLines[linesIndex++] = r.top; 2658 mLines[linesIndex++] = r.right; 2659 mLines[linesIndex++] = r.top; 2660 2661 mLines[linesIndex++] = r.left; 2662 mLines[linesIndex++] = r.bottom; 2663 mLines[linesIndex++] = r.right; 2664 mLines[linesIndex++] = r.bottom; 2665 2666 mLines[linesIndex++] = r.left; 2667 mLines[linesIndex++] = r.top; 2668 mLines[linesIndex++] = r.left; 2669 mLines[linesIndex++] = r.bottom; 2670 2671 mLines[linesIndex++] = r.right; 2672 mLines[linesIndex++] = r.top; 2673 mLines[linesIndex++] = r.right; 2674 mLines[linesIndex++] = r.bottom; 2675 mPaint.setColor(color); 2676 canvas.drawLines(mLines, 0, linesIndex, mPaint); 2677 } 2678 2679 private void updateEventDetails() { 2680 if (mSelectedEvent == null || mSelectionMode == SELECTION_HIDDEN 2681 || mSelectionMode == SELECTION_LONGPRESS) { 2682 mPopup.dismiss(); 2683 return; 2684 } 2685 if (mLastPopupEventID == mSelectedEvent.id) { 2686 return; 2687 } 2688 2689 mLastPopupEventID = mSelectedEvent.id; 2690 2691 // Remove any outstanding callbacks to dismiss the popup. 2692 getHandler().removeCallbacks(mDismissPopup); 2693 2694 Event event = mSelectedEvent; 2695 TextView titleView = (TextView) mPopupView.findViewById(R.id.event_title); 2696 titleView.setText(event.title); 2697 2698 ImageView imageView = (ImageView) mPopupView.findViewById(R.id.reminder_icon); 2699 imageView.setVisibility(event.hasAlarm ? View.VISIBLE : View.GONE); 2700 2701 imageView = (ImageView) mPopupView.findViewById(R.id.repeat_icon); 2702 imageView.setVisibility(event.isRepeating ? View.VISIBLE : View.GONE); 2703 2704 int flags; 2705 if (event.allDay) { 2706 flags = DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_DATE | 2707 DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL; 2708 } else { 2709 flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE 2710 | DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_ALL 2711 | DateUtils.FORMAT_CAP_NOON_MIDNIGHT; 2712 } 2713 if (DateFormat.is24HourFormat(mContext)) { 2714 flags |= DateUtils.FORMAT_24HOUR; 2715 } 2716 String timeRange = Utils.formatDateRange(mContext, 2717 event.startMillis, event.endMillis, flags); 2718 TextView timeView = (TextView) mPopupView.findViewById(R.id.time); 2719 timeView.setText(timeRange); 2720 2721 TextView whereView = (TextView) mPopupView.findViewById(R.id.where); 2722 final boolean empty = TextUtils.isEmpty(event.location); 2723 whereView.setVisibility(empty ? View.GONE : View.VISIBLE); 2724 if (!empty) whereView.setText(event.location); 2725 2726 mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.LEFT, mHoursWidth, 5); 2727 postDelayed(mDismissPopup, POPUP_DISMISS_DELAY); 2728 } 2729 2730 // The following routines are called from the parent activity when certain 2731 // touch events occur. 2732 private void doDown(MotionEvent ev) { 2733 mTouchMode = TOUCH_MODE_DOWN; 2734 mViewStartX = 0; 2735 mOnFlingCalled = false; 2736 getHandler().removeCallbacks(mContinueScroll); 2737 } 2738 2739 private void doSingleTapUp(MotionEvent ev) { 2740 if (!mHandleActionUp) { 2741 return; 2742 } 2743 2744 int x = (int) ev.getX(); 2745 int y = (int) ev.getY(); 2746 int selectedDay = mSelectionDay; 2747 int selectedHour = mSelectionHour; 2748 2749 boolean validPosition = setSelectionFromPosition(x, y); 2750 if (!validPosition) { 2751 // return if the touch wasn't on an area of concern 2752 return; 2753 } 2754 2755 mSelectionMode = SELECTION_SELECTED; 2756 invalidate(); 2757 2758 if (mSelectedEvent != null) { 2759 // If the tap is on an event, launch the "View event" view 2760 mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mSelectedEvent.id, 2761 mSelectedEvent.startMillis, mSelectedEvent.endMillis, (int) ev.getRawX(), 2762 (int) ev.getRawY()); 2763 } else if (selectedDay == mSelectionDay && selectedHour == mSelectionHour) { 2764 // If the tap is on an already selected hour slot, then create a new 2765 // event 2766 mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1, 2767 getSelectedTimeInMillis(), 0, (int) ev.getRawX(), (int) ev.getRawY()); 2768 } else { 2769 Time startTime = new Time(mBaseDate); 2770 startTime.setJulianDay(mSelectionDay); 2771 startTime.hour = mSelectionHour; 2772 startTime.normalize(true /* ignore isDst */); 2773 2774 Time endTime = new Time(startTime); 2775 endTime.hour++; 2776 2777 mController.sendEvent(this, EventType.GO_TO, startTime, endTime, -1, ViewType.CURRENT); 2778 } 2779 } 2780 2781 private void doLongPress(MotionEvent ev) { 2782 // Scale gesture in progress 2783 if (mStartingSpanY != 0) { 2784 return; 2785 } 2786 2787 int x = (int) ev.getX(); 2788 int y = (int) ev.getY(); 2789 2790 boolean validPosition = setSelectionFromPosition(x, y); 2791 if (!validPosition) { 2792 // return if the touch wasn't on an area of concern 2793 return; 2794 } 2795 2796 mSelectionMode = SELECTION_LONGPRESS; 2797 invalidate(); 2798 performLongClick(); 2799 } 2800 2801 private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) { 2802 // Use the distance from the current point to the initial touch instead 2803 // of deltaX and deltaY to avoid accumulating floating-point rounding 2804 // errors. Also, we don't need floats, we can use ints. 2805 int distanceX = (int) e1.getX() - (int) e2.getX(); 2806 int distanceY = (int) e1.getY() - (int) e2.getY(); 2807 2808 // If we haven't figured out the predominant scroll direction yet, 2809 // then do it now. 2810 if (mTouchMode == TOUCH_MODE_DOWN) { 2811 int absDistanceX = Math.abs(distanceX); 2812 int absDistanceY = Math.abs(distanceY); 2813 mScrollStartY = mViewStartY; 2814 mPreviousDirection = 0; 2815 2816 // If the x distance is at least twice the y distance, then lock 2817 // the scroll horizontally. Otherwise scroll vertically. 2818 if (absDistanceX >= 2 * absDistanceY) { 2819 mTouchMode = TOUCH_MODE_HSCROLL; 2820 mViewStartX = distanceX; 2821 initNextView(-mViewStartX); 2822 } else { 2823 mTouchMode = TOUCH_MODE_VSCROLL; 2824 } 2825 } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { 2826 // We are already scrolling horizontally, so check if we 2827 // changed the direction of scrolling so that the other week 2828 // is now visible. 2829 mViewStartX = distanceX; 2830 if (distanceX != 0) { 2831 int direction = (distanceX > 0) ? 1 : -1; 2832 if (direction != mPreviousDirection) { 2833 // The user has switched the direction of scrolling 2834 // so re-init the next view 2835 initNextView(-mViewStartX); 2836 mPreviousDirection = direction; 2837 } 2838 } 2839 } 2840 2841 if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) { 2842 mViewStartY = mScrollStartY + distanceY; 2843 if (mViewStartY < 0) { 2844 mViewStartY = 0; 2845 } else if (mViewStartY > mMaxViewStartY) { 2846 mViewStartY = mMaxViewStartY; 2847 } 2848 computeFirstHour(); 2849 } 2850 2851 mScrolling = true; 2852 2853 mSelectionMode = SELECTION_HIDDEN; 2854 invalidate(); 2855 } 2856 2857 private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 2858 mTouchMode = TOUCH_MODE_INITIAL_STATE; 2859 mSelectionMode = SELECTION_HIDDEN; 2860 mOnFlingCalled = true; 2861 int deltaX = (int) e2.getX() - (int) e1.getX(); 2862 int distanceX = Math.abs(deltaX); 2863 int deltaY = (int) e2.getY() - (int) e1.getY(); 2864 int distanceY = Math.abs(deltaY); 2865 if (DEBUG) Log.d(TAG, "doFling: distanceX " + distanceX 2866 + ", HORIZONTAL_FLING_THRESHOLD " + HORIZONTAL_FLING_THRESHOLD); 2867 2868 if ((distanceX >= HORIZONTAL_FLING_THRESHOLD) && (distanceX > distanceY)) { 2869 // Horizontal fling. 2870 // initNextView(deltaX); 2871 switchViews(deltaX < 0, mViewStartX, mViewWidth); 2872 mViewStartX = 0; 2873 return; 2874 } 2875 2876 // Vertical fling. 2877 mViewStartX = 0; 2878 2879 // Continue scrolling vertically 2880 mContinueScroll.init((int) velocityY / 20); 2881 post(mContinueScroll); 2882 } 2883 2884 private boolean initNextView(int deltaX) { 2885 // Change the view to the previous day or week 2886 DayView view = (DayView) mViewSwitcher.getNextView(); 2887 Time date = view.mBaseDate; 2888 date.set(mBaseDate); 2889 boolean switchForward; 2890 if (deltaX > 0) { 2891 date.monthDay -= mNumDays; 2892 view.mSelectionDay = mSelectionDay - mNumDays; 2893 switchForward = false; 2894 } else { 2895 date.monthDay += mNumDays; 2896 view.mSelectionDay = mSelectionDay + mNumDays; 2897 switchForward = true; 2898 } 2899 date.normalize(true /* ignore isDst */); 2900 initView(view); 2901 view.layout(getLeft(), getTop(), getRight(), getBottom()); 2902 view.reloadEvents(); 2903 return switchForward; 2904 } 2905 2906 // ScaleGestureDetector.OnScaleGestureListener 2907 public boolean onScaleBegin(ScaleGestureDetector detector) { 2908 mHandleActionUp = false; 2909 float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAllDayHeight; 2910 mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP); 2911 2912 mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY())); 2913 mCellHeightBeforeScaleGesture = mCellHeight; 2914 2915 if (DEBUG) { 2916 float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); 2917 Log.d(TAG, "mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " 2918 + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" 2919 + mCellHeight); 2920 } 2921 2922 return true; 2923 } 2924 2925 // ScaleGestureDetector.OnScaleGestureListener 2926 public boolean onScale(ScaleGestureDetector detector) { 2927 float spanY = Math.abs(detector.getCurrentSpanY()); 2928 2929 mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY); 2930 2931 if (mCellHeight < mMinCellHeight) { 2932 // If mStartingSpanY is too small, even a small increase in the 2933 // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT 2934 mStartingSpanY = Math.max(MIN_Y_SPAN, spanY); 2935 mCellHeight = mMinCellHeight; 2936 mCellHeightBeforeScaleGesture = mMinCellHeight; 2937 } else if (mCellHeight > MAX_CELL_HEIGHT) { 2938 mStartingSpanY = spanY; 2939 mCellHeight = MAX_CELL_HEIGHT; 2940 mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT; 2941 } 2942 2943 int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAllDayHeight; 2944 mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels; 2945 mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight; 2946 2947 if (DEBUG) { 2948 float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP); 2949 Log.d(TAG, " mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " 2950 + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" 2951 + mCellHeight + " SpanY:" + detector.getCurrentSpanY()); 2952 } 2953 2954 if (mViewStartY < 0) { 2955 mViewStartY = 0; 2956 mGestureCenterHour = (mViewStartY + gestureCenterInPixels) 2957 / (float) (mCellHeight + DAY_GAP); 2958 } else if (mViewStartY > mMaxViewStartY) { 2959 mViewStartY = mMaxViewStartY; 2960 mGestureCenterHour = (mViewStartY + gestureCenterInPixels) 2961 / (float) (mCellHeight + DAY_GAP); 2962 } 2963 computeFirstHour(); 2964 2965 mRemeasure = true; 2966 invalidate(); 2967 return true; 2968 } 2969 2970 // ScaleGestureDetector.OnScaleGestureListener 2971 public void onScaleEnd(ScaleGestureDetector detector) { 2972 mStartingSpanY = 0; 2973 } 2974 2975 @Override 2976 public boolean onTouchEvent(MotionEvent ev) { 2977 int action = ev.getAction(); 2978 2979 if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) { 2980 mScaleGestureDetector.onTouchEvent(ev); 2981 if (mScaleGestureDetector.isInProgress()) { 2982 return true; 2983 } 2984 } 2985 2986 switch (action) { 2987 case MotionEvent.ACTION_DOWN: 2988 if (DEBUG) Log.e(TAG, "ACTION_DOWN"); 2989 mHandleActionUp = true; 2990 mGestureDetector.onTouchEvent(ev); 2991 return true; 2992 2993 case MotionEvent.ACTION_MOVE: 2994 if (DEBUG) Log.e(TAG, "ACTION_MOVE"); 2995 mGestureDetector.onTouchEvent(ev); 2996 return true; 2997 2998 case MotionEvent.ACTION_UP: 2999 if (DEBUG) Log.e(TAG, "ACTION_UP " + mHandleActionUp); 3000 mGestureDetector.onTouchEvent(ev); 3001 if (!mHandleActionUp) { 3002 mHandleActionUp = true; 3003 return true; 3004 } 3005 if (mOnFlingCalled) { 3006 return true; 3007 } 3008 if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) { 3009 mTouchMode = TOUCH_MODE_INITIAL_STATE; 3010 if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) { 3011 // The user has gone beyond the threshold so switch views 3012 if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views"); 3013 switchViews(mViewStartX > 0, mViewStartX, mViewWidth); 3014 mViewStartX = 0; 3015 return true; 3016 } else { 3017 // Not beyond the threshold so invalidate which will cause 3018 // the view to snap back. Also call recalc() to ensure 3019 // that we have the correct starting date and title. 3020 if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back"); 3021 recalc(); 3022 invalidate(); 3023 mViewStartX = 0; 3024 } 3025 } 3026 3027 // If we were scrolling, then reset the selected hour so that it 3028 // is visible. 3029 if (mScrolling) { 3030 mScrolling = false; 3031 resetSelectedHour(); 3032 invalidate(); 3033 } 3034 return true; 3035 3036 // This case isn't expected to happen. 3037 case MotionEvent.ACTION_CANCEL: 3038 if (DEBUG) Log.e(TAG, "ACTION_CANCEL"); 3039 mGestureDetector.onTouchEvent(ev); 3040 mScrolling = false; 3041 resetSelectedHour(); 3042 return true; 3043 3044 default: 3045 if (DEBUG) Log.e(TAG, "Not MotionEvent"); 3046 if (mGestureDetector.onTouchEvent(ev)) { 3047 return true; 3048 } 3049 return super.onTouchEvent(ev); 3050 } 3051 } 3052 3053 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { 3054 MenuItem item; 3055 3056 // If the trackball is held down, then the context menu pops up and 3057 // we never get onKeyUp() for the long-press. So check for it here 3058 // and change the selection to the long-press state. 3059 if (mSelectionMode != SELECTION_LONGPRESS) { 3060 mSelectionMode = SELECTION_LONGPRESS; 3061 invalidate(); 3062 } 3063 3064 final long startMillis = getSelectedTimeInMillis(); 3065 int flags = DateUtils.FORMAT_SHOW_TIME 3066 | DateUtils.FORMAT_CAP_NOON_MIDNIGHT 3067 | DateUtils.FORMAT_SHOW_WEEKDAY; 3068 final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags); 3069 menu.setHeaderTitle(title); 3070 3071 int numSelectedEvents = mSelectedEvents.size(); 3072 if (mNumDays == 1) { 3073 // Day view. 3074 3075 // If there is a selected event, then allow it to be viewed and 3076 // edited. 3077 if (numSelectedEvents >= 1) { 3078 item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view); 3079 item.setOnMenuItemClickListener(mContextMenuHandler); 3080 item.setIcon(android.R.drawable.ic_menu_info_details); 3081 3082 int accessLevel = getEventAccessLevel(mContext, mSelectedEvent); 3083 if (accessLevel == ACCESS_LEVEL_EDIT) { 3084 item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit); 3085 item.setOnMenuItemClickListener(mContextMenuHandler); 3086 item.setIcon(android.R.drawable.ic_menu_edit); 3087 item.setAlphabeticShortcut('e'); 3088 } 3089 3090 if (accessLevel >= ACCESS_LEVEL_DELETE) { 3091 item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete); 3092 item.setOnMenuItemClickListener(mContextMenuHandler); 3093 item.setIcon(android.R.drawable.ic_menu_delete); 3094 } 3095 3096 item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create); 3097 item.setOnMenuItemClickListener(mContextMenuHandler); 3098 item.setIcon(android.R.drawable.ic_menu_add); 3099 item.setAlphabeticShortcut('n'); 3100 } else { 3101 // Otherwise, if the user long-pressed on a blank hour, allow 3102 // them to create an event. They can also do this by tapping. 3103 item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create); 3104 item.setOnMenuItemClickListener(mContextMenuHandler); 3105 item.setIcon(android.R.drawable.ic_menu_add); 3106 item.setAlphabeticShortcut('n'); 3107 } 3108 } else { 3109 // Week view. 3110 3111 // If there is a selected event, then allow it to be viewed and 3112 // edited. 3113 if (numSelectedEvents >= 1) { 3114 item = menu.add(0, MENU_EVENT_VIEW, 0, R.string.event_view); 3115 item.setOnMenuItemClickListener(mContextMenuHandler); 3116 item.setIcon(android.R.drawable.ic_menu_info_details); 3117 3118 int accessLevel = getEventAccessLevel(mContext, mSelectedEvent); 3119 if (accessLevel == ACCESS_LEVEL_EDIT) { 3120 item = menu.add(0, MENU_EVENT_EDIT, 0, R.string.event_edit); 3121 item.setOnMenuItemClickListener(mContextMenuHandler); 3122 item.setIcon(android.R.drawable.ic_menu_edit); 3123 item.setAlphabeticShortcut('e'); 3124 } 3125 3126 if (accessLevel >= ACCESS_LEVEL_DELETE) { 3127 item = menu.add(0, MENU_EVENT_DELETE, 0, R.string.event_delete); 3128 item.setOnMenuItemClickListener(mContextMenuHandler); 3129 item.setIcon(android.R.drawable.ic_menu_delete); 3130 } 3131 } 3132 3133 item = menu.add(0, MENU_EVENT_CREATE, 0, R.string.event_create); 3134 item.setOnMenuItemClickListener(mContextMenuHandler); 3135 item.setIcon(android.R.drawable.ic_menu_add); 3136 item.setAlphabeticShortcut('n'); 3137 3138 item = menu.add(0, MENU_DAY, 0, R.string.show_day_view); 3139 item.setOnMenuItemClickListener(mContextMenuHandler); 3140 item.setIcon(android.R.drawable.ic_menu_day); 3141 item.setAlphabeticShortcut('d'); 3142 } 3143 3144 mPopup.dismiss(); 3145 } 3146 3147 private class ContextMenuHandler implements MenuItem.OnMenuItemClickListener { 3148 public boolean onMenuItemClick(MenuItem item) { 3149 switch (item.getItemId()) { 3150 case MENU_EVENT_VIEW: { 3151 if (mSelectedEvent != null) { 3152 mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT_DETAILS, 3153 mSelectedEvent.id, mSelectedEvent.startMillis, 3154 mSelectedEvent.endMillis, 0, 0); 3155 } 3156 break; 3157 } 3158 case MENU_EVENT_EDIT: { 3159 if (mSelectedEvent != null) { 3160 mController.sendEventRelatedEvent(this, EventType.EDIT_EVENT, 3161 mSelectedEvent.id, mSelectedEvent.startMillis, 3162 mSelectedEvent.endMillis, 0, 0); 3163 } 3164 break; 3165 } 3166 case MENU_DAY: { 3167 mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1, 3168 ViewType.DAY); 3169 break; 3170 } 3171 case MENU_AGENDA: { 3172 mController.sendEvent(this, EventType.GO_TO, getSelectedTime(), null, -1, 3173 ViewType.AGENDA); 3174 break; 3175 } 3176 case MENU_EVENT_CREATE: { 3177 long startMillis = getSelectedTimeInMillis(); 3178 long endMillis = startMillis + DateUtils.HOUR_IN_MILLIS; 3179 mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1, 3180 startMillis, endMillis, 0, 0); 3181 break; 3182 } 3183 case MENU_EVENT_DELETE: { 3184 if (mSelectedEvent != null) { 3185 Event selectedEvent = mSelectedEvent; 3186 long begin = selectedEvent.startMillis; 3187 long end = selectedEvent.endMillis; 3188 long id = selectedEvent.id; 3189 mController.sendEventRelatedEvent(this, EventType.DELETE_EVENT, id, begin, 3190 end, 0, 0); 3191 } 3192 break; 3193 } 3194 default: { 3195 return false; 3196 } 3197 } 3198 return true; 3199 } 3200 } 3201 3202 private static int getEventAccessLevel(Context context, Event e) { 3203 ContentResolver cr = context.getContentResolver(); 3204 3205 int visibility = Calendars.NO_ACCESS; 3206 3207 // Get the calendar id for this event 3208 Cursor cursor = cr.query(ContentUris.withAppendedId(Events.CONTENT_URI, e.id), 3209 new String[] { Events.CALENDAR_ID }, 3210 null /* selection */, 3211 null /* selectionArgs */, 3212 null /* sort */); 3213 3214 if (cursor == null) { 3215 return ACCESS_LEVEL_NONE; 3216 } 3217 3218 if (cursor.getCount() == 0) { 3219 cursor.close(); 3220 return ACCESS_LEVEL_NONE; 3221 } 3222 3223 cursor.moveToFirst(); 3224 long calId = cursor.getLong(0); 3225 cursor.close(); 3226 3227 Uri uri = Calendars.CONTENT_URI; 3228 String where = String.format(CALENDARS_WHERE, calId); 3229 cursor = cr.query(uri, CALENDARS_PROJECTION, where, null, null); 3230 3231 String calendarOwnerAccount = null; 3232 if (cursor != null) { 3233 cursor.moveToFirst(); 3234 visibility = cursor.getInt(CALENDARS_INDEX_ACCESS_LEVEL); 3235 calendarOwnerAccount = cursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT); 3236 cursor.close(); 3237 } 3238 3239 if (visibility < Calendars.CONTRIBUTOR_ACCESS) { 3240 return ACCESS_LEVEL_NONE; 3241 } 3242 3243 if (e.guestsCanModify) { 3244 return ACCESS_LEVEL_EDIT; 3245 } 3246 3247 if (!TextUtils.isEmpty(calendarOwnerAccount) && 3248 calendarOwnerAccount.equalsIgnoreCase(e.organizer)) { 3249 return ACCESS_LEVEL_EDIT; 3250 } 3251 3252 return ACCESS_LEVEL_DELETE; 3253 } 3254 3255 /** 3256 * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position. 3257 * If the touch position is not within the displayed grid, then this 3258 * method returns false. 3259 * 3260 * @param x the x position of the touch 3261 * @param y the y position of the touch 3262 * @return true if the touch position is valid 3263 */ 3264 private boolean setSelectionFromPosition(int x, int y) { 3265 if (x < mHoursWidth) { 3266 return false; 3267 } 3268 3269 int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP); 3270 if (day >= mNumDays) { 3271 day = mNumDays - 1; 3272 } 3273 day += mFirstJulianDay; 3274 int hour; 3275 if (y < mFirstCell + mFirstHourOffset) { 3276 mSelectionAllDay = true; 3277 } else { 3278 hour = (y - mFirstCell - mFirstHourOffset) / (mCellHeight + HOUR_GAP); 3279 hour += mFirstHour; 3280 mSelectionHour = hour; 3281 mSelectionAllDay = false; 3282 } 3283 mSelectionDay = day; 3284 findSelectedEvent(x, y); 3285// Log.i("Cal", "setSelectionFromPosition( " + x + ", " + y + " ) day: " + day 3286// + " hour: " + hour 3287// + " mFirstCell: " + mFirstCell + " mFirstHourOffset: " + mFirstHourOffset); 3288// if (mSelectedEvent != null) { 3289// Log.i("Cal", " num events: " + mSelectedEvents.size() + " event: " + mSelectedEvent.title); 3290// for (Event ev : mSelectedEvents) { 3291// int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL 3292// | DateUtils.FORMAT_CAP_NOON_MIDNIGHT; 3293// String timeRange = formatDateRange(mContext, 3294// ev.startMillis, ev.endMillis, flags); 3295// 3296// Log.i("Cal", " " + timeRange + " " + ev.title); 3297// } 3298// } 3299 return true; 3300 } 3301 3302 private void findSelectedEvent(int x, int y) { 3303 int date = mSelectionDay; 3304 int cellWidth = mCellWidth; 3305 final ArrayList<Event> events = mEvents; 3306 int numEvents = events.size(); 3307 int left = mHoursWidth + (mSelectionDay - mFirstJulianDay) * (cellWidth + DAY_GAP); 3308 int top = 0; 3309 mSelectedEvent = null; 3310 3311 mSelectedEvents.clear(); 3312 if (mSelectionAllDay) { 3313 float yDistance; 3314 float minYdistance = 10000.0f; // any large number 3315 Event closestEvent = null; 3316 float drawHeight = mAllDayHeight; 3317 int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN; 3318 for (int i = 0; i < numEvents; i++) { 3319 Event event = events.get(i); 3320 if (!event.allDay) { 3321 continue; 3322 } 3323 3324 if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) { 3325 float numRectangles = event.getMaxColumns(); 3326 float height = drawHeight / numRectangles; 3327 if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) { 3328 height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT; 3329 } 3330 float eventTop = yOffset + height * event.getColumn(); 3331 float eventBottom = eventTop + height; 3332 if (eventTop < y && eventBottom > y) { 3333 // If the touch is inside the event rectangle, then 3334 // add the event. 3335 mSelectedEvents.add(event); 3336 closestEvent = event; 3337 break; 3338 } else { 3339 // Find the closest event 3340 if (eventTop >= y) { 3341 yDistance = eventTop - y; 3342 } else { 3343 yDistance = y - eventBottom; 3344 } 3345 if (yDistance < minYdistance) { 3346 minYdistance = yDistance; 3347 closestEvent = event; 3348 } 3349 } 3350 } 3351 } 3352 mSelectedEvent = closestEvent; 3353 return; 3354 } 3355 3356 // Adjust y for the scrollable bitmap 3357 y += mViewStartY - mFirstCell; 3358 3359 // Use a region around (x,y) for the selection region 3360 Rect region = mRect; 3361 region.left = x - 10; 3362 region.right = x + 10; 3363 region.top = y - 10; 3364 region.bottom = y + 10; 3365 3366 EventGeometry geometry = mEventGeometry; 3367 3368 for (int i = 0; i < numEvents; i++) { 3369 Event event = events.get(i); 3370 // Compute the event rectangle. 3371 if (!geometry.computeEventRect(date, left, top, cellWidth, event)) { 3372 continue; 3373 } 3374 3375 // If the event intersects the selection region, then add it to 3376 // mSelectedEvents. 3377 if (geometry.eventIntersectsSelection(event, region)) { 3378 mSelectedEvents.add(event); 3379 } 3380 } 3381 3382 // If there are any events in the selected region, then assign the 3383 // closest one to mSelectedEvent. 3384 if (mSelectedEvents.size() > 0) { 3385 int len = mSelectedEvents.size(); 3386 Event closestEvent = null; 3387 float minDist = mViewWidth + mViewHeight; // some large distance 3388 for (int index = 0; index < len; index++) { 3389 Event ev = mSelectedEvents.get(index); 3390 float dist = geometry.pointToEvent(x, y, ev); 3391 if (dist < minDist) { 3392 minDist = dist; 3393 closestEvent = ev; 3394 } 3395 } 3396 mSelectedEvent = closestEvent; 3397 3398 // Keep the selected hour and day consistent with the selected 3399 // event. They could be different if we touched on an empty hour 3400 // slot very close to an event in the previous hour slot. In 3401 // that case we will select the nearby event. 3402 int startDay = mSelectedEvent.startDay; 3403 int endDay = mSelectedEvent.endDay; 3404 if (mSelectionDay < startDay) { 3405 mSelectionDay = startDay; 3406 } else if (mSelectionDay > endDay) { 3407 mSelectionDay = endDay; 3408 } 3409 3410 int startHour = mSelectedEvent.startTime / 60; 3411 int endHour; 3412 if (mSelectedEvent.startTime < mSelectedEvent.endTime) { 3413 endHour = (mSelectedEvent.endTime - 1) / 60; 3414 } else { 3415 endHour = mSelectedEvent.endTime / 60; 3416 } 3417 3418 if (mSelectionHour < startHour) { 3419 mSelectionHour = startHour; 3420 } else if (mSelectionHour > endHour) { 3421 mSelectionHour = endHour; 3422 } 3423 } 3424 } 3425 3426 // Encapsulates the code to continue the scrolling after the 3427 // finger is lifted. Instead of stopping the scroll immediately, 3428 // the scroll continues to "free spin" and gradually slows down. 3429 private class ContinueScroll implements Runnable { 3430 int mSignDeltaY; 3431 int mAbsDeltaY; 3432 float mFloatDeltaY; 3433 long mFreeSpinTime; 3434 private static final float FRICTION_COEF = 0.7F; 3435 private static final long FREE_SPIN_MILLIS = 180; 3436 private static final int MAX_DELTA = 60; 3437 private static final int SCROLL_REPEAT_INTERVAL = 30; 3438 3439 public void init(int deltaY) { 3440 mSignDeltaY = 0; 3441 if (deltaY > 0) { 3442 mSignDeltaY = 1; 3443 } else if (deltaY < 0) { 3444 mSignDeltaY = -1; 3445 } 3446 mAbsDeltaY = Math.abs(deltaY); 3447 3448 // Limit the maximum speed 3449 if (mAbsDeltaY > MAX_DELTA) { 3450 mAbsDeltaY = MAX_DELTA; 3451 } 3452 mFloatDeltaY = mAbsDeltaY; 3453 mFreeSpinTime = System.currentTimeMillis() + FREE_SPIN_MILLIS; 3454// Log.i("Cal", "init scroll: mAbsDeltaY: " + mAbsDeltaY 3455// + " mViewStartY: " + mViewStartY); 3456 } 3457 3458 public void run() { 3459 long time = System.currentTimeMillis(); 3460 3461 // Start out with a frictionless "free spin" 3462 if (time > mFreeSpinTime) { 3463 // If the delta is small, then apply a fixed deceleration. 3464 // Otherwise 3465 if (mAbsDeltaY <= 10) { 3466 mAbsDeltaY -= 2; 3467 } else { 3468 mFloatDeltaY *= FRICTION_COEF; 3469 mAbsDeltaY = (int) mFloatDeltaY; 3470 } 3471 3472 if (mAbsDeltaY < 0) { 3473 mAbsDeltaY = 0; 3474 } 3475 } 3476 3477 if (mSignDeltaY == 1) { 3478 mViewStartY -= mAbsDeltaY; 3479 } else { 3480 mViewStartY += mAbsDeltaY; 3481 } 3482// Log.i("Cal", " scroll: mAbsDeltaY: " + mAbsDeltaY 3483// + " mViewStartY: " + mViewStartY); 3484 3485 if (mViewStartY < 0) { 3486 mViewStartY = 0; 3487 mAbsDeltaY = 0; 3488 } else if (mViewStartY > mMaxViewStartY) { 3489 mViewStartY = mMaxViewStartY; 3490 mAbsDeltaY = 0; 3491 } 3492 3493 computeFirstHour(); 3494 3495 if (mAbsDeltaY > 0) { 3496 postDelayed(this, SCROLL_REPEAT_INTERVAL); 3497 } else { 3498 // Done scrolling. 3499 mScrolling = false; 3500 resetSelectedHour(); 3501 } 3502 3503 invalidate(); 3504 } 3505 } 3506 3507 /** 3508 * Cleanup the pop-up and timers. 3509 */ 3510 public void cleanup() { 3511 // Protect against null-pointer exceptions 3512 if (mPopup != null) { 3513 mPopup.dismiss(); 3514 } 3515 mLastPopupEventID = INVALID_EVENT_ID; 3516 Handler handler = getHandler(); 3517 if (handler != null) { 3518 handler.removeCallbacks(mDismissPopup); 3519 handler.removeCallbacks(mUpdateCurrentTime); 3520 } 3521 3522 Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, 3523 mCellHeight); 3524 3525 // Turn off redraw 3526 mRemeasure = false; 3527 } 3528 3529 /** 3530 * Restart the update timer 3531 */ 3532 public void restartCurrentTimeUpdates() { 3533 post(mUpdateCurrentTime); 3534 } 3535 3536 @Override protected void onDetachedFromWindow() { 3537 cleanup(); 3538 super.onDetachedFromWindow(); 3539 } 3540 3541 class DismissPopup implements Runnable { 3542 public void run() { 3543 // Protect against null-pointer exceptions 3544 if (mPopup != null) { 3545 mPopup.dismiss(); 3546 } 3547 } 3548 } 3549 3550 class UpdateCurrentTime implements Runnable { 3551 public void run() { 3552 long currentTime = System.currentTimeMillis(); 3553 mCurrentTime.set(currentTime); 3554 //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.) 3555 postDelayed(mUpdateCurrentTime, 3556 UPDATE_CURRENT_TIME_DELAY - (currentTime % UPDATE_CURRENT_TIME_DELAY)); 3557 mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff); 3558 invalidate(); 3559 } 3560 } 3561 3562 class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { 3563 @Override 3564 public boolean onSingleTapUp(MotionEvent ev) { 3565 DayView.this.doSingleTapUp(ev); 3566 return true; 3567 } 3568 3569 @Override 3570 public void onLongPress(MotionEvent ev) { 3571 DayView.this.doLongPress(ev); 3572 } 3573 3574 @Override 3575 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 3576 DayView.this.doScroll(e1, e2, distanceX, distanceY); 3577 return true; 3578 } 3579 3580 @Override 3581 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 3582 DayView.this.doFling(e1, e2, velocityX, velocityY); 3583 return true; 3584 } 3585 3586 @Override 3587 public boolean onDown(MotionEvent ev) { 3588 DayView.this.doDown(ev); 3589 return true; 3590 } 3591 } 3592} 3593