[automerger skipped] [DO NOT MERGE] am: ae487ada19 -s ours

am skip reason: skipped by user rtenneti

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Calendar/+/15831943

Change-Id: Ib053fcc18961da9e00709d2bb26c99518abb3f2c
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..d732f94
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,55 @@
+package {
+
+    default_applicable_licenses: ["packages_apps_Calendar_license"],
+}
+
+license {
+
+    name: "packages_apps_Calendar_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
+// Include res dir from chips
+
+android_app {
+    name: "Calendar",
+
+    jacoco: {
+        include_filter: ["com.android.calendar.**"],
+    },
+
+    srcs: [
+        "src/**/*.kt",
+    ],
+
+    // bundled
+    //LOCAL_STATIC_JAVA_LIBRARIES +=
+    //#        android-common
+    //#        libchips
+    //#        calendar-common
+
+    // unbundled
+    static_libs: [
+        "android-common",
+        "libchips",
+        "colorpicker",
+        "android-opt-timezonepicker",
+        "androidx.legacy_legacy-support-v4",
+        "calendar-common",
+    ],
+
+    sdk_version: "current",
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
+
+    product_specific: true,
+
+    aaptflags: ["--auto-add-overlay"],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index dce26a4..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# Include res dir from chips
-chips_dir := ../../../frameworks/opt/chips/res
-color_picker_dir := ../../../frameworks/opt/colorpicker/res
-timezonepicker_dir := ../../../frameworks/opt/timezonepicker/res
-res_dirs := $(chips_dir) $(color_picker_dir) $(timezonepicker_dir) res
-src_dirs := src
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.calendar.*
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-java-files-under,$(src_dirs))
-
-# bundled
-#LOCAL_STATIC_JAVA_LIBRARIES += \
-#        android-common \
-#        libchips \
-#        calendar-common
-
-# unbundled
-LOCAL_STATIC_JAVA_LIBRARIES := \
-        android-common \
-        libchips \
-        colorpicker \
-        android-opt-timezonepicker \
-        androidx.legacy_legacy-support-v4 \
-        calendar-common
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-
-LOCAL_PACKAGE_NAME := Calendar
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/NOTICE
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-LOCAL_PRODUCT_MODULE := true
-
-LOCAL_AAPT_FLAGS := --auto-add-overlay
-LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips
-LOCAL_AAPT_FLAGS += --extra-packages com.android.colorpicker
-LOCAL_AAPT_FLAGS += --extra-packages com.android.timezonepicker
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c9c5a04..686be7a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -38,7 +38,7 @@
     <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.mail" />
-    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29"></uses-sdk>
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="30"></uses-sdk>
 
 
     <application android:name="CalendarApplication"
diff --git a/src/com/android/calendar/AllInOneActivity.java b/src/com/android/calendar/AllInOneActivity.java
deleted file mode 100644
index cec6a40..0000000
--- a/src/com/android/calendar/AllInOneActivity.java
+++ /dev/null
@@ -1,1062 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.ObjectAnimator;
-import android.app.ActionBar;
-import android.app.ActionBar.Tab;
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.content.AsyncQueryHandler;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.graphics.drawable.LayerDrawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.provider.CalendarContract;
-import android.provider.CalendarContract.Attendees;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.RelativeLayout.LayoutParams;
-import android.widget.TextView;
-
-import com.android.calendar.CalendarController.EventHandler;
-import com.android.calendar.CalendarController.EventInfo;
-import com.android.calendar.CalendarController.EventType;
-import com.android.calendar.CalendarController.ViewType;
-import com.android.calendar.month.MonthByWeekFragment;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
-import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
-import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
-import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
-
-public class AllInOneActivity extends Activity implements EventHandler,
-        OnSharedPreferenceChangeListener, ActionBar.TabListener,
-        ActionBar.OnNavigationListener {
-    private static final String TAG = "AllInOneActivity";
-    private static final boolean DEBUG = false;
-    private static final String EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment";
-    private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
-    private static final String BUNDLE_KEY_EVENT_ID = "key_event_id";
-    private static final String BUNDLE_KEY_RESTORE_VIEW = "key_restore_view";
-    private static final String BUNDLE_KEY_CHECK_ACCOUNTS = "key_check_for_accounts";
-    private static final int HANDLER_KEY = 0;
-
-    // Indices of buttons for the drop down menu (tabs replacement)
-    // Must match the strings in the array buttons_list in arrays.xml and the
-    // OnNavigationListener
-    private static final int BUTTON_DAY_INDEX = 0;
-    private static final int BUTTON_WEEK_INDEX = 1;
-    private static final int BUTTON_MONTH_INDEX = 2;
-    private static final int BUTTON_AGENDA_INDEX = 3;
-
-    private CalendarController mController;
-    private static boolean mIsMultipane;
-    private static boolean mIsTabletConfig;
-    private boolean mOnSaveInstanceStateCalled = false;
-    private boolean mBackToPreviousView = false;
-    private ContentResolver mContentResolver;
-    private int mPreviousView;
-    private int mCurrentView;
-    private boolean mPaused = true;
-    private boolean mUpdateOnResume = false;
-    private boolean mHideControls = false;
-    private boolean mShowSideViews = true;
-    private boolean mShowWeekNum = false;
-    private TextView mHomeTime;
-    private TextView mDateRange;
-    private TextView mWeekTextView;
-    private View mMiniMonth;
-    private View mCalendarsList;
-    private View mMiniMonthContainer;
-    private View mSecondaryPane;
-    private String mTimeZone;
-    private boolean mShowCalendarControls;
-    private boolean mShowEventInfoFullScreen;
-    private int mWeekNum;
-    private int mCalendarControlsAnimationTime;
-    private int mControlsAnimateWidth;
-    private int mControlsAnimateHeight;
-
-    private long mViewEventId = -1;
-    private long mIntentEventStartMillis = -1;
-    private long mIntentEventEndMillis = -1;
-    private int mIntentAttendeeResponse = Attendees.ATTENDEE_STATUS_NONE;
-    private boolean mIntentAllDay = false;
-
-    // Action bar and Navigation bar (left side of Action bar)
-    private ActionBar mActionBar;
-    private ActionBar.Tab mDayTab;
-    private ActionBar.Tab mWeekTab;
-    private ActionBar.Tab mMonthTab;
-    private MenuItem mControlsMenu;
-    private Menu mOptionsMenu;
-    private CalendarViewAdapter mActionBarMenuSpinnerAdapter;
-    private QueryHandler mHandler;
-    private boolean mCheckForAccounts = true;
-
-    private String mHideString;
-    private String mShowString;
-
-    DayOfMonthDrawable mDayOfMonthIcon;
-
-    int mOrientation;
-
-    // Params for animating the controls on the right
-    private LayoutParams mControlsParams;
-    private LinearLayout.LayoutParams mVerticalControlsParams;
-
-    private final AnimatorListener mSlideAnimationDoneListener = new AnimatorListener() {
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-        }
-
-        @Override
-        public void onAnimationEnd(android.animation.Animator animation) {
-            int visibility = mShowSideViews ? View.VISIBLE : View.GONE;
-            mMiniMonth.setVisibility(visibility);
-            mCalendarsList.setVisibility(visibility);
-            mMiniMonthContainer.setVisibility(visibility);
-        }
-
-        @Override
-        public void onAnimationRepeat(android.animation.Animator animation) {
-        }
-
-        @Override
-        public void onAnimationStart(android.animation.Animator animation) {
-        }
-    };
-
-    private class QueryHandler extends AsyncQueryHandler {
-        public QueryHandler(ContentResolver cr) {
-            super(cr);
-        }
-
-        @Override
-        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-            mCheckForAccounts = false;
-            try {
-                // If the query didn't return a cursor for some reason return
-                if (cursor == null || cursor.getCount() > 0 || isFinishing()) {
-                    return;
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-
-            Bundle options = new Bundle();
-            options.putCharSequence("introMessage",
-                    getResources().getString(R.string.create_an_account_desc));
-            options.putBoolean("allowSkip", true);
-
-            AccountManager am = AccountManager.get(AllInOneActivity.this);
-            am.addAccount("com.google", CalendarContract.AUTHORITY, null, options,
-                    AllInOneActivity.this,
-                    new AccountManagerCallback<Bundle>() {
-                        @Override
-                        public void run(AccountManagerFuture<Bundle> future) {
-                        }
-                    }, null);
-        }
-    }
-
-    private final Runnable mHomeTimeUpdater = new Runnable() {
-        @Override
-        public void run() {
-            mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater);
-            updateSecondaryTitleFields(-1);
-            AllInOneActivity.this.invalidateOptionsMenu();
-            Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone);
-        }
-    };
-
-    // runs every midnight/time changes and refreshes the today icon
-    private final Runnable mTimeChangesUpdater = new Runnable() {
-        @Override
-        public void run() {
-            mTimeZone = Utils.getTimeZone(AllInOneActivity.this, mHomeTimeUpdater);
-            AllInOneActivity.this.invalidateOptionsMenu();
-            Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone);
-        }
-    };
-
-
-    // Create an observer so that we can update the views whenever a
-    // Calendar event changes.
-    private final ContentObserver mObserver = new ContentObserver(new Handler()) {
-        @Override
-        public boolean deliverSelfNotifications() {
-            return true;
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            eventsChanged();
-        }
-    };
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        String action = intent.getAction();
-        if (DEBUG)
-            Log.d(TAG, "New intent received " + intent.toString());
-        // Don't change the date if we're just returning to the app's home
-        if (Intent.ACTION_VIEW.equals(action)
-                && !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false)) {
-            long millis = parseViewAction(intent);
-            if (millis == -1) {
-                millis = Utils.timeFromIntentInMillis(intent);
-            }
-            if (millis != -1 && mViewEventId == -1 && mController != null) {
-                Time time = new Time(mTimeZone);
-                time.set(millis);
-                time.normalize(true);
-                mController.sendEvent(this, EventType.GO_TO, time, time, -1, ViewType.CURRENT);
-            }
-        }
-    }
-
-    @Override
-    protected void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        if (icicle != null && icicle.containsKey(BUNDLE_KEY_CHECK_ACCOUNTS)) {
-            mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS);
-        }
-        // Launch add google account if this is first time and there are no
-        // accounts yet
-        if (mCheckForAccounts) {
-
-            mHandler = new QueryHandler(this.getContentResolver());
-            mHandler.startQuery(0, null, Calendars.CONTENT_URI, new String[] {
-                Calendars._ID
-            }, null, null /* selection args */, null /* sort order */);
-        }
-
-        // This needs to be created before setContentView
-        mController = CalendarController.getInstance(this);
-
-
-        // Get time from intent or icicle
-        long timeMillis = -1;
-        int viewType = -1;
-        final Intent intent = getIntent();
-        if (icicle != null) {
-            timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME);
-            viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1);
-        } else {
-            String action = intent.getAction();
-            if (Intent.ACTION_VIEW.equals(action)) {
-                // Open EventInfo later
-                timeMillis = parseViewAction(intent);
-            }
-
-            if (timeMillis == -1) {
-                timeMillis = Utils.timeFromIntentInMillis(intent);
-            }
-        }
-
-        if (viewType == -1 || viewType > ViewType.MAX_VALUE) {
-            viewType = Utils.getViewTypeFromIntentAndSharedPref(this);
-        }
-        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater);
-        Time t = new Time(mTimeZone);
-        t.set(timeMillis);
-
-        if (DEBUG) {
-            if (icicle != null && intent != null) {
-                Log.d(TAG, "both, icicle:" + icicle.toString() + "  intent:" + intent.toString());
-            } else {
-                Log.d(TAG, "not both, icicle:" + icicle + " intent:" + intent);
-            }
-        }
-
-        Resources res = getResources();
-        mHideString = res.getString(R.string.hide_controls);
-        mShowString = res.getString(R.string.show_controls);
-        mOrientation = res.getConfiguration().orientation;
-        if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            mControlsAnimateWidth = (int)res.getDimension(R.dimen.calendar_controls_width);
-            if (mControlsParams == null) {
-                mControlsParams = new LayoutParams(mControlsAnimateWidth, 0);
-            }
-            mControlsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
-        } else {
-            // Make sure width is in between allowed min and max width values
-            mControlsAnimateWidth = Math.max(res.getDisplayMetrics().widthPixels * 45 / 100,
-                    (int)res.getDimension(R.dimen.min_portrait_calendar_controls_width));
-            mControlsAnimateWidth = Math.min(mControlsAnimateWidth,
-                    (int)res.getDimension(R.dimen.max_portrait_calendar_controls_width));
-        }
-
-        mControlsAnimateHeight = (int)res.getDimension(R.dimen.calendar_controls_height);
-
-        mHideControls = true;
-        mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config);
-        mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config);
-        mShowCalendarControls =
-                Utils.getConfigBool(this, R.bool.show_calendar_controls);
-        mShowEventInfoFullScreen =
-            Utils.getConfigBool(this, R.bool.show_event_info_full_screen);
-        mCalendarControlsAnimationTime = res.getInteger(R.integer.calendar_controls_animation_time);
-        Utils.setAllowWeekForDetailView(mIsMultipane);
-
-        // setContentView must be called before configureActionBar
-        setContentView(R.layout.all_in_one);
-
-        if (mIsTabletConfig) {
-            mDateRange = (TextView) findViewById(R.id.date_bar);
-            mWeekTextView = (TextView) findViewById(R.id.week_num);
-        } else {
-            mDateRange = (TextView) getLayoutInflater().inflate(R.layout.date_range_title, null);
-        }
-
-        // configureActionBar auto-selects the first tab you add, so we need to
-        // call it before we set up our own fragments to make sure it doesn't
-        // overwrite us
-        configureActionBar(viewType);
-
-        mHomeTime = (TextView) findViewById(R.id.home_time);
-        mMiniMonth = findViewById(R.id.mini_month);
-        if (mIsTabletConfig && mOrientation == Configuration.ORIENTATION_PORTRAIT) {
-            mMiniMonth.setLayoutParams(new RelativeLayout.LayoutParams(mControlsAnimateWidth,
-                    mControlsAnimateHeight));
-        }
-        mCalendarsList = findViewById(R.id.calendar_list);
-        mMiniMonthContainer = findViewById(R.id.mini_month_container);
-        mSecondaryPane = findViewById(R.id.secondary_pane);
-
-        // Must register as the first activity because this activity can modify
-        // the list of event handlers in it's handle method. This affects who
-        // the rest of the handlers the controller dispatches to are.
-        mController.registerFirstEventHandler(HANDLER_KEY, this);
-
-        initFragments(timeMillis, viewType, icicle);
-
-        // Listen for changes that would require this to be refreshed
-        SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
-        prefs.registerOnSharedPreferenceChangeListener(this);
-
-        mContentResolver = getContentResolver();
-    }
-
-    private long parseViewAction(final Intent intent) {
-        long timeMillis = -1;
-        Uri data = intent.getData();
-        if (data != null && data.isHierarchical()) {
-            List<String> path = data.getPathSegments();
-            if (path.size() == 2 && path.get(0).equals("events")) {
-                try {
-                    mViewEventId = Long.valueOf(data.getLastPathSegment());
-                    if (mViewEventId != -1) {
-                        mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0);
-                        mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0);
-                        mIntentAttendeeResponse = intent.getIntExtra(
-                            ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE);
-                        mIntentAllDay = intent.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false);
-                        timeMillis = mIntentEventStartMillis;
-                    }
-                } catch (NumberFormatException e) {
-                    // Ignore if mViewEventId can't be parsed
-                }
-            }
-        }
-        return timeMillis;
-    }
-
-    private void configureActionBar(int viewType) {
-        createButtonsSpinner(viewType, mIsTabletConfig);
-        if (mIsMultipane) {
-            mActionBar.setDisplayOptions(
-                    ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
-        } else {
-            mActionBar.setDisplayOptions(0);
-        }
-    }
-
-    private void createButtonsSpinner(int viewType, boolean tabletConfig) {
-        // If tablet configuration , show spinner with no dates
-        mActionBarMenuSpinnerAdapter = new CalendarViewAdapter (this, viewType, !tabletConfig);
-        mActionBar = getActionBar();
-        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
-        mActionBar.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this);
-        switch (viewType) {
-            case ViewType.AGENDA:
-                break;
-            case ViewType.DAY:
-                mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX);
-                break;
-            case ViewType.WEEK:
-                mActionBar.setSelectedNavigationItem(BUTTON_WEEK_INDEX);
-                break;
-            case ViewType.MONTH:
-                mActionBar.setSelectedNavigationItem(BUTTON_MONTH_INDEX);
-                break;
-            default:
-                mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX);
-                break;
-       }
-    }
-    // Clear buttons used in the agenda view
-    private void clearOptionsMenu() {
-        if (mOptionsMenu == null) {
-            return;
-        }
-        MenuItem cancelItem = mOptionsMenu.findItem(R.id.action_cancel);
-        if (cancelItem != null) {
-            cancelItem.setVisible(false);
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-
-        // Check if the upgrade code has ever been run. If not, force a sync just this one time.
-        Utils.trySyncAndDisableUpgradeReceiver(this);
-
-        // Must register as the first activity because this activity can modify
-        // the list of event handlers in it's handle method. This affects who
-        // the rest of the handlers the controller dispatches to are.
-        mController.registerFirstEventHandler(HANDLER_KEY, this);
-
-        mOnSaveInstanceStateCalled = false;
-        mContentResolver.registerContentObserver(CalendarContract.Events.CONTENT_URI,
-                true, mObserver);
-        if (mUpdateOnResume) {
-            initFragments(mController.getTime(), mController.getViewType(), null);
-            mUpdateOnResume = false;
-        }
-        Time t = new Time(mTimeZone);
-        t.set(mController.getTime());
-        mController.sendEvent(this, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT,
-                mController.getDateFlags(), null, null);
-        // Make sure the drop-down menu will get its date updated at midnight
-        if (mActionBarMenuSpinnerAdapter != null) {
-            mActionBarMenuSpinnerAdapter.refresh(this);
-        }
-
-        if (mControlsMenu != null) {
-            mControlsMenu.setTitle(mHideControls ? mShowString : mHideString);
-        }
-        mPaused = false;
-
-        if (mViewEventId != -1 && mIntentEventStartMillis != -1 && mIntentEventEndMillis != -1) {
-            long currentMillis = System.currentTimeMillis();
-            long selectedTime = -1;
-            if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) {
-                selectedTime = currentMillis;
-            }
-            mController.sendEventRelatedEventWithExtra(this, EventType.VIEW_EVENT, mViewEventId,
-                    mIntentEventStartMillis, mIntentEventEndMillis, -1, -1,
-                    EventInfo.buildViewExtraLong(mIntentAttendeeResponse,mIntentAllDay),
-                    selectedTime);
-            mViewEventId = -1;
-            mIntentEventStartMillis = -1;
-            mIntentEventEndMillis = -1;
-            mIntentAllDay = false;
-        }
-        Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone);
-        // Make sure the today icon is up to date
-        invalidateOptionsMenu();
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-
-        mController.deregisterEventHandler(HANDLER_KEY);
-        mPaused = true;
-        mHomeTime.removeCallbacks(mHomeTimeUpdater);
-        if (mActionBarMenuSpinnerAdapter != null) {
-            mActionBarMenuSpinnerAdapter.onPause();
-        }
-        mContentResolver.unregisterContentObserver(mObserver);
-        if (isFinishing()) {
-            // Stop listening for changes that would require this to be refreshed
-            SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
-            prefs.unregisterOnSharedPreferenceChangeListener(this);
-        }
-        // FRAG_TODO save highlighted days of the week;
-        if (mController.getViewType() != ViewType.EDIT) {
-            Utils.setDefaultView(this, mController.getViewType());
-        }
-        Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater);
-    }
-
-    @Override
-    protected void onUserLeaveHint() {
-        mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT);
-        super.onUserLeaveHint();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        mOnSaveInstanceStateCalled = true;
-        super.onSaveInstanceState(outState);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
-        prefs.unregisterOnSharedPreferenceChangeListener(this);
-
-        mController.deregisterAllEventHandlers();
-
-        CalendarController.removeInstance(this);
-    }
-
-    private void initFragments(long timeMillis, int viewType, Bundle icicle) {
-        if (DEBUG) {
-            Log.d(TAG, "Initializing to " + timeMillis + " for view " + viewType);
-        }
-        FragmentTransaction ft = getFragmentManager().beginTransaction();
-
-        if (mShowCalendarControls) {
-            Fragment miniMonthFrag = new MonthByWeekFragment(timeMillis, true);
-            ft.replace(R.id.mini_month, miniMonthFrag);
-            mController.registerEventHandler(R.id.mini_month, (EventHandler) miniMonthFrag);
-        }
-        if (!mShowCalendarControls || viewType == ViewType.EDIT) {
-            mMiniMonth.setVisibility(View.GONE);
-            mCalendarsList.setVisibility(View.GONE);
-        }
-
-        EventInfo info = null;
-        if (viewType == ViewType.EDIT) {
-            mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt(
-                    GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW);
-
-            long eventId = -1;
-            Intent intent = getIntent();
-            Uri data = intent.getData();
-            if (data != null) {
-                try {
-                    eventId = Long.parseLong(data.getLastPathSegment());
-                } catch (NumberFormatException e) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Create new event");
-                    }
-                }
-            } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) {
-                eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID);
-            }
-
-            long begin = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1);
-            long end = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1);
-            info = new EventInfo();
-            if (end != -1) {
-                info.endTime = new Time();
-                info.endTime.set(end);
-            }
-            if (begin != -1) {
-                info.startTime = new Time();
-                info.startTime.set(begin);
-            }
-            info.id = eventId;
-            // We set the viewtype so if the user presses back when they are
-            // done editing the controller knows we were in the Edit Event
-            // screen. Likewise for eventId
-            mController.setViewType(viewType);
-            mController.setEventId(eventId);
-        } else {
-            mPreviousView = viewType;
-        }
-
-        setMainPane(ft, R.id.main_pane, viewType, timeMillis, true);
-        ft.commit(); // this needs to be after setMainPane()
-
-        Time t = new Time(mTimeZone);
-        t.set(timeMillis);
-        if (viewType != ViewType.EDIT) {
-            mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType);
-        }
-    }
-
-    @Override
-    public void onBackPressed() {
-        if (mCurrentView == ViewType.EDIT || mBackToPreviousView) {
-            mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView);
-        } else {
-            super.onBackPressed();
-        }
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        super.onCreateOptionsMenu(menu);
-        mOptionsMenu = menu;
-        getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu);
-
-        // Hide the "show/hide controls" button if this is a phone
-        // or the view type is "Month".
-
-        mControlsMenu = menu.findItem(R.id.action_hide_controls);
-        if (!mShowCalendarControls) {
-            if (mControlsMenu != null) {
-                mControlsMenu.setVisible(false);
-                mControlsMenu.setEnabled(false);
-            }
-        } else if (mControlsMenu != null && mController != null
-                    && (mController.getViewType() == ViewType.MONTH)) {
-            mControlsMenu.setVisible(false);
-            mControlsMenu.setEnabled(false);
-        } else if (mControlsMenu != null){
-            mControlsMenu.setTitle(mHideControls ? mShowString : mHideString);
-        }
-
-        MenuItem menuItem = menu.findItem(R.id.action_today);
-        if (Utils.isJellybeanOrLater()) {
-            // replace the default top layer drawable of the today icon with a
-            // custom drawable that shows the day of the month of today
-            LayerDrawable icon = (LayerDrawable) menuItem.getIcon();
-            Utils.setTodayIcon(icon, this, mTimeZone);
-        } else {
-            menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light);
-        }
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        Time t = null;
-        int viewType = ViewType.CURRENT;
-        long extras = CalendarController.EXTRA_GOTO_TIME;
-        final int itemId = item.getItemId();
-        if (itemId == R.id.action_today) {
-            viewType = ViewType.CURRENT;
-            t = new Time(mTimeZone);
-            t.setToNow();
-            extras |= CalendarController.EXTRA_GOTO_TODAY;
-        } else if (itemId == R.id.action_hide_controls) {
-            mHideControls = !mHideControls;
-            item.setTitle(mHideControls ? mShowString : mHideString);
-            if (!mHideControls) {
-                mMiniMonth.setVisibility(View.VISIBLE);
-                mCalendarsList.setVisibility(View.VISIBLE);
-                mMiniMonthContainer.setVisibility(View.VISIBLE);
-            }
-            final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, "controlsOffset",
-                    mHideControls ? 0 : mControlsAnimateWidth,
-                    mHideControls ? mControlsAnimateWidth : 0);
-            slideAnimation.setDuration(mCalendarControlsAnimationTime);
-            ObjectAnimator.setFrameDelay(0);
-            slideAnimation.start();
-            return true;
-        } else {
-            Log.d(TAG, "Unsupported itemId: " + itemId);
-            return true;
-        }
-        mController.sendEvent(this, EventType.GO_TO, t, null, t, -1, viewType, extras, null, null);
-        return true;
-    }
-
-    /**
-     * Sets the offset of the controls on the right for animating them off/on
-     * screen. ProGuard strips this if it's not in proguard.flags
-     *
-     * @param controlsOffset The current offset in pixels
-     */
-    public void setControlsOffset(int controlsOffset) {
-        if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            mMiniMonth.setTranslationX(controlsOffset);
-            mCalendarsList.setTranslationX(controlsOffset);
-            mControlsParams.width = Math.max(0, mControlsAnimateWidth - controlsOffset);
-            mMiniMonthContainer.setLayoutParams(mControlsParams);
-        } else {
-            mMiniMonth.setTranslationY(controlsOffset);
-            mCalendarsList.setTranslationY(controlsOffset);
-            if (mVerticalControlsParams == null) {
-                mVerticalControlsParams = new LinearLayout.LayoutParams(
-                        LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight);
-            }
-            mVerticalControlsParams.height = Math.max(0, mControlsAnimateHeight - controlsOffset);
-            mMiniMonthContainer.setLayoutParams(mVerticalControlsParams);
-        }
-    }
-
-    @Override
-    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
-        if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) {
-            if (mPaused) {
-                mUpdateOnResume = true;
-            } else {
-                initFragments(mController.getTime(), mController.getViewType(), null);
-            }
-        }
-    }
-
-    private void setMainPane(
-            FragmentTransaction ft, int viewId, int viewType, long timeMillis, boolean force) {
-        if (mOnSaveInstanceStateCalled) {
-            return;
-        }
-        if (!force && mCurrentView == viewType) {
-            return;
-        }
-
-        // Remove this when transition to and from month view looks fine.
-        boolean doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH;
-        FragmentManager fragmentManager = getFragmentManager();
-
-        if (viewType != mCurrentView) {
-            // The rules for this previous view are different than the
-            // controller's and are used for intercepting the back button.
-            if (mCurrentView != ViewType.EDIT && mCurrentView > 0) {
-                mPreviousView = mCurrentView;
-            }
-            mCurrentView = viewType;
-        }
-        // Create new fragment
-        Fragment frag = null;
-        Fragment secFrag = null;
-        switch (viewType) {
-            case ViewType.AGENDA:
-                break;
-            case ViewType.DAY:
-                if (mActionBar != null && (mActionBar.getSelectedTab() != mDayTab)) {
-                    mActionBar.selectTab(mDayTab);
-                }
-                if (mActionBarMenuSpinnerAdapter != null) {
-                    mActionBar.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX);
-                }
-                frag = new DayFragment(timeMillis, 1);
-                break;
-            case ViewType.MONTH:
-                if (mActionBar != null && (mActionBar.getSelectedTab() != mMonthTab)) {
-                    mActionBar.selectTab(mMonthTab);
-                }
-                if (mActionBarMenuSpinnerAdapter != null) {
-                    mActionBar.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX);
-                }
-                frag = new MonthByWeekFragment(timeMillis, false);
-                break;
-            case ViewType.WEEK:
-            default:
-                if (mActionBar != null && (mActionBar.getSelectedTab() != mWeekTab)) {
-                    mActionBar.selectTab(mWeekTab);
-                }
-                if (mActionBarMenuSpinnerAdapter != null) {
-                    mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX);
-                }
-                frag = new DayFragment(timeMillis, 7);
-                break;
-        }
-
-        // Update the current view so that the menu can update its look according to the
-        // current view.
-        if (mActionBarMenuSpinnerAdapter != null) {
-            mActionBarMenuSpinnerAdapter.setMainView(viewType);
-            if (!mIsTabletConfig) {
-                mActionBarMenuSpinnerAdapter.setTime(timeMillis);
-            }
-        }
-
-
-        // Show date only on tablet configurations in views different than Agenda
-        if (!mIsTabletConfig) {
-            mDateRange.setVisibility(View.GONE);
-        } else {
-            mDateRange.setVisibility(View.GONE);
-        }
-
-        // Clear unnecessary buttons from the option menu when switching from the agenda view
-        if (viewType != ViewType.AGENDA) {
-            clearOptionsMenu();
-        }
-
-        boolean doCommit = false;
-        if (ft == null) {
-            doCommit = true;
-            ft = fragmentManager.beginTransaction();
-        }
-
-        if (doTransition) {
-            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
-        }
-
-        ft.replace(viewId, frag);
-        if (DEBUG) {
-            Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType);
-        }
-        // If the key is already registered this will replace it
-        mController.registerEventHandler(viewId, (EventHandler) frag);
-
-        if (doCommit) {
-            if (DEBUG) {
-                Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing());
-            }
-            ft.commit();
-        }
-    }
-
-    private void setTitleInActionBar(EventInfo event) {
-        if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) {
-            return;
-        }
-
-        final long start = event.startTime.toMillis(false /* use isDst */);
-        final long end;
-        if (event.endTime != null) {
-            end = event.endTime.toMillis(false /* use isDst */);
-        } else {
-            end = start;
-        }
-
-        final String msg = Utils.formatDateRange(this, start, end, (int) event.extraLong);
-        CharSequence oldDate = mDateRange.getText();
-        mDateRange.setText(msg);
-        updateSecondaryTitleFields(event.selectedTime != null ? event.selectedTime.toMillis(true)
-                : start);
-        if (!TextUtils.equals(oldDate, msg)) {
-            mDateRange.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-            if (mShowWeekNum && mWeekTextView != null) {
-                mWeekTextView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-            }
-        }
-    }
-
-    private void updateSecondaryTitleFields(long visibleMillisSinceEpoch) {
-        mShowWeekNum = Utils.getShowWeekNumber(this);
-        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater);
-        if (visibleMillisSinceEpoch != -1) {
-            int weekNum = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this);
-            mWeekNum = weekNum;
-        }
-
-        if (mShowWeekNum && (mCurrentView == ViewType.WEEK) && mIsTabletConfig
-                && mWeekTextView != null) {
-            String weekString = getResources().getQuantityString(R.plurals.weekN, mWeekNum,
-                    mWeekNum);
-            mWeekTextView.setText(weekString);
-            mWeekTextView.setVisibility(View.VISIBLE);
-        } else if (visibleMillisSinceEpoch != -1 && mWeekTextView != null
-                && mCurrentView == ViewType.DAY && mIsTabletConfig) {
-            Time time = new Time(mTimeZone);
-            time.set(visibleMillisSinceEpoch);
-            int julianDay = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff);
-            time.setToNow();
-            int todayJulianDay = Time.getJulianDay(time.toMillis(false), time.gmtoff);
-            String dayString = Utils.getDayOfWeekString(julianDay, todayJulianDay,
-                    visibleMillisSinceEpoch, this);
-            mWeekTextView.setText(dayString);
-            mWeekTextView.setVisibility(View.VISIBLE);
-        } else if (mWeekTextView != null && (!mIsTabletConfig || mCurrentView != ViewType.DAY)) {
-            mWeekTextView.setVisibility(View.GONE);
-        }
-
-        if (mHomeTime != null
-                && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK)
-                && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())) {
-            Time time = new Time(mTimeZone);
-            time.setToNow();
-            long millis = time.toMillis(true);
-            boolean isDST = time.isDst != 0;
-            int flags = DateUtils.FORMAT_SHOW_TIME;
-            if (DateFormat.is24HourFormat(this)) {
-                flags |= DateUtils.FORMAT_24HOUR;
-            }
-            // Formats the time as
-            String timeString = (new StringBuilder(
-                    Utils.formatDateRange(this, millis, millis, flags))).append(" ").append(
-                    TimeZone.getTimeZone(mTimeZone).getDisplayName(
-                            isDST, TimeZone.SHORT, Locale.getDefault())).toString();
-            mHomeTime.setText(timeString);
-            mHomeTime.setVisibility(View.VISIBLE);
-            // Update when the minute changes
-            mHomeTime.removeCallbacks(mHomeTimeUpdater);
-            mHomeTime.postDelayed(
-                    mHomeTimeUpdater,
-                    DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS));
-        } else if (mHomeTime != null) {
-            mHomeTime.setVisibility(View.GONE);
-        }
-    }
-
-    @Override
-    public long getSupportedEventTypes() {
-        return EventType.GO_TO | EventType.UPDATE_TITLE;
-    }
-
-    @Override
-    public void handleEvent(EventInfo event) {
-        long displayTime = -1;
-        if (event.eventType == EventType.GO_TO) {
-            if ((event.extraLong & CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS) != 0) {
-                mBackToPreviousView = true;
-            } else if (event.viewType != mController.getPreviousViewType()
-                    && event.viewType != ViewType.EDIT) {
-                // Clear the flag is change to a different view type
-                mBackToPreviousView = false;
-            }
-
-            setMainPane(
-                    null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), false);
-            if (mShowCalendarControls) {
-                int animationSize = (mOrientation == Configuration.ORIENTATION_LANDSCAPE) ?
-                        mControlsAnimateWidth : mControlsAnimateHeight;
-                boolean noControlsView = event.viewType == ViewType.MONTH;
-                if (mControlsMenu != null) {
-                    mControlsMenu.setVisible(!noControlsView);
-                    mControlsMenu.setEnabled(!noControlsView);
-                }
-                if (noControlsView || mHideControls) {
-                    // hide minimonth and calendar frag
-                    mShowSideViews = false;
-                    if (!mHideControls) {
-                            final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this,
-                                    "controlsOffset", 0, animationSize);
-                            slideAnimation.addListener(mSlideAnimationDoneListener);
-                            slideAnimation.setDuration(mCalendarControlsAnimationTime);
-                            ObjectAnimator.setFrameDelay(0);
-                            slideAnimation.start();
-                    } else {
-                        mMiniMonth.setVisibility(View.GONE);
-                        mCalendarsList.setVisibility(View.GONE);
-                        mMiniMonthContainer.setVisibility(View.GONE);
-                    }
-                } else {
-                    // show minimonth and calendar frag
-                    mShowSideViews = true;
-                    mMiniMonth.setVisibility(View.VISIBLE);
-                    mCalendarsList.setVisibility(View.VISIBLE);
-                    mMiniMonthContainer.setVisibility(View.VISIBLE);
-                    if (!mHideControls &&
-                            (mController.getPreviousViewType() == ViewType.MONTH)) {
-                        final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this,
-                                "controlsOffset", animationSize, 0);
-                        slideAnimation.setDuration(mCalendarControlsAnimationTime);
-                        ObjectAnimator.setFrameDelay(0);
-                        slideAnimation.start();
-                    }
-                }
-            }
-            displayTime = event.selectedTime != null ? event.selectedTime.toMillis(true)
-                    : event.startTime.toMillis(true);
-            if (!mIsTabletConfig) {
-                mActionBarMenuSpinnerAdapter.setTime(displayTime);
-            }
-        } else if (event.eventType == EventType.UPDATE_TITLE) {
-            setTitleInActionBar(event);
-            if (!mIsTabletConfig) {
-                mActionBarMenuSpinnerAdapter.setTime(mController.getTime());
-            }
-        }
-        updateSecondaryTitleFields(displayTime);
-    }
-
-    @Override
-    public void eventsChanged() {
-        mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT);
-    }
-
-    @Override
-    public void onTabSelected(Tab tab, FragmentTransaction ft) {
-        Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing());
-        if (tab == mDayTab && mCurrentView != ViewType.DAY) {
-            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY);
-        } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) {
-            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK);
-        } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) {
-            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH);
-        } else {
-            Log.w(TAG, "TabSelected event from unknown tab: "
-                    + (tab == null ? "null" : tab.getText()));
-            Log.w(TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab
-                    + " Week:" + mWeekTab + " Month:" + mMonthTab);
-        }
-    }
-
-    @Override
-    public void onTabReselected(Tab tab, FragmentTransaction ft) {
-    }
-
-    @Override
-    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
-    }
-
-
-    @Override
-    public boolean onNavigationItemSelected(int itemPosition, long itemId) {
-        switch (itemPosition) {
-            case CalendarViewAdapter.DAY_BUTTON_INDEX:
-                if (mCurrentView != ViewType.DAY) {
-                    mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY);
-                }
-                break;
-            case CalendarViewAdapter.WEEK_BUTTON_INDEX:
-                if (mCurrentView != ViewType.WEEK) {
-                    mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK);
-                }
-                break;
-            case CalendarViewAdapter.MONTH_BUTTON_INDEX:
-                if (mCurrentView != ViewType.MONTH) {
-                    mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH);
-                }
-                break;
-            case CalendarViewAdapter.AGENDA_BUTTON_INDEX:
-                break;
-            default:
-                Log.w(TAG, "ItemSelected event from unknown button: " + itemPosition);
-                Log.w(TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition +
-                        " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab);
-                break;
-        }
-        return false;
-    }
-}
diff --git a/src/com/android/calendar/AllInOneActivity.kt b/src/com/android/calendar/AllInOneActivity.kt
new file mode 100644
index 0000000..1747bf5
--- /dev/null
+++ b/src/com/android/calendar/AllInOneActivity.kt
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import android.accounts.AccountManager
+import android.accounts.AccountManagerCallback
+import android.accounts.AccountManagerFuture
+import android.animation.Animator
+import android.animation.Animator.AnimatorListener
+import android.animation.ObjectAnimator
+import android.app.ActionBar
+import android.app.ActionBar.Tab
+import android.app.Activity
+import android.app.Fragment
+import android.app.FragmentManager
+import android.app.FragmentTransaction
+import android.content.AsyncQueryHandler
+import android.content.ContentResolver
+import android.content.Intent
+import android.content.SharedPreferences
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.database.ContentObserver
+import android.database.Cursor
+import android.graphics.drawable.LayerDrawable
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.provider.CalendarContract
+import android.provider.CalendarContract.Attendees
+import android.provider.CalendarContract.Calendars
+import android.provider.CalendarContract.Events
+import android.text.TextUtils
+import android.text.format.DateFormat
+import android.text.format.DateUtils
+import android.text.format.Time
+import android.util.Log
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import android.view.accessibility.AccessibilityEvent
+import android.widget.LinearLayout
+import android.widget.RelativeLayout
+import android.widget.RelativeLayout.LayoutParams
+import android.widget.TextView
+import com.android.calendar.CalendarController.EventHandler
+import com.android.calendar.CalendarController.EventInfo
+import com.android.calendar.CalendarController.EventType
+import com.android.calendar.CalendarController.ViewType
+import com.android.calendar.month.MonthByWeekFragment
+import java.util.Locale
+import java.util.TimeZone
+import android.provider.CalendarContract.Attendees.ATTENDEE_STATUS
+import android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY
+import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME
+import android.provider.CalendarContract.EXTRA_EVENT_END_TIME
+
+class AllInOneActivity : Activity(), EventHandler, OnSharedPreferenceChangeListener,
+    ActionBar.TabListener, ActionBar.OnNavigationListener {
+    private var mController: CalendarController? = null
+    private var mOnSaveInstanceStateCalled = false
+    private var mBackToPreviousView = false
+    private var mContentResolver: ContentResolver? = null
+    private var mPreviousView = 0
+    private var mCurrentView = 0
+    private var mPaused = true
+    private var mUpdateOnResume = false
+    private var mHideControls = false
+    private var mShowSideViews = true
+    private var mShowWeekNum = false
+    private var mHomeTime: TextView? = null
+    private var mDateRange: TextView? = null
+    private var mWeekTextView: TextView? = null
+    private var mMiniMonth: View? = null
+    private var mCalendarsList: View? = null
+    private var mMiniMonthContainer: View? = null
+    private var mSecondaryPane: View? = null
+    private var mTimeZone: String? = null
+    private var mShowCalendarControls = false
+    private var mShowEventInfoFullScreen = false
+    private var mWeekNum = 0
+    private var mCalendarControlsAnimationTime = 0
+    private var mControlsAnimateWidth = 0
+    private var mControlsAnimateHeight = 0
+    private var mViewEventId: Long = -1
+    private var mIntentEventStartMillis: Long = -1
+    private var mIntentEventEndMillis: Long = -1
+    private var mIntentAttendeeResponse: Int = Attendees.ATTENDEE_STATUS_NONE
+    private var mIntentAllDay = false
+
+    // Action bar and Navigation bar (left side of Action bar)
+    private var mActionBar: ActionBar? = null
+    private val mDayTab: Tab? = null
+    private val mWeekTab: Tab? = null
+    private val mMonthTab: Tab? = null
+    private var mControlsMenu: MenuItem? = null
+    private var mOptionsMenu: Menu? = null
+    private var mActionBarMenuSpinnerAdapter: CalendarViewAdapter? = null
+    private var mHandler: QueryHandler? = null
+    private var mCheckForAccounts = true
+    private var mHideString: String? = null
+    private var mShowString: String? = null
+    var mDayOfMonthIcon: DayOfMonthDrawable? = null
+    var mOrientation = 0
+
+    // Params for animating the controls on the right
+    private var mControlsParams: LayoutParams? = null
+    private var mVerticalControlsParams: LinearLayout.LayoutParams? = null
+    private val mSlideAnimationDoneListener: AnimatorListener = object : AnimatorListener {
+        @Override
+        override fun onAnimationCancel(animation: Animator?) {
+        }
+
+        @Override
+        override fun onAnimationEnd(animation: Animator?) {
+            val visibility: Int = if (mShowSideViews) View.VISIBLE else View.GONE
+            mMiniMonth?.setVisibility(visibility)
+            mCalendarsList?.setVisibility(visibility)
+            mMiniMonthContainer?.setVisibility(visibility)
+        }
+
+        @Override
+        override fun onAnimationRepeat(animation: Animator?) {
+        }
+
+        @Override
+        override fun onAnimationStart(animation: Animator?) {
+        }
+    }
+
+    private inner class QueryHandler(cr: ContentResolver?) : AsyncQueryHandler(cr) {
+        @Override
+        protected override fun onQueryComplete(token: Int, cookie: Any?, cursor: Cursor?) {
+            mCheckForAccounts = false
+            try {
+                // If the query didn't return a cursor for some reason return
+                if (cursor == null || cursor.getCount() > 0 || isFinishing()) {
+                    return
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close()
+                }
+            }
+            val options = Bundle()
+            options.putCharSequence(
+                "introMessage",
+                getResources().getString(R.string.create_an_account_desc)
+            )
+            options.putBoolean("allowSkip", true)
+            val am: AccountManager = AccountManager.get(this@AllInOneActivity)
+            am.addAccount("com.google", CalendarContract.AUTHORITY, null, options,
+                this@AllInOneActivity,
+                    object : AccountManagerCallback<Bundle?> {
+                        @Override
+                        override fun run(future: AccountManagerFuture<Bundle?>?) {
+                        }
+                    }, null
+            )
+        }
+    }
+
+    private val mHomeTimeUpdater: Runnable = object : Runnable {
+        @Override
+        override fun run() {
+            mTimeZone = Utils.getTimeZone(this@AllInOneActivity, this)
+            updateSecondaryTitleFields(-1)
+            this@AllInOneActivity.invalidateOptionsMenu()
+            Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone)
+        }
+    }
+
+    // runs every midnight/time changes and refreshes the today icon
+    private val mTimeChangesUpdater: Runnable = object : Runnable {
+        @Override
+        override fun run() {
+            mTimeZone = Utils.getTimeZone(this@AllInOneActivity, mHomeTimeUpdater)
+            this@AllInOneActivity.invalidateOptionsMenu()
+            Utils.setMidnightUpdater(mHandler, this, mTimeZone)
+        }
+    }
+
+    // Create an observer so that we can update the views whenever a
+    // Calendar event changes.
+    private val mObserver: ContentObserver = object : ContentObserver(Handler()) {
+        @Override
+        override fun deliverSelfNotifications(): Boolean {
+            return true
+        }
+
+        @Override
+        override fun onChange(selfChange: Boolean) {
+            eventsChanged()
+        }
+    }
+
+    @Override
+    protected override fun onNewIntent(intent: Intent) {
+        val action: String? = intent.getAction()
+        if (DEBUG) Log.d(TAG, "New intent received " + intent.toString())
+        // Don't change the date if we're just returning to the app's home
+        if (Intent.ACTION_VIEW.equals(action) &&
+            !intent.getBooleanExtra(Utils.INTENT_KEY_HOME, false)
+        ) {
+            var millis = parseViewAction(intent)
+            if (millis == -1L) {
+                millis = Utils.timeFromIntentInMillis(intent) as Long
+            }
+            if (millis != -1L && mViewEventId == -1L && mController != null) {
+                val time = Time(mTimeZone)
+                time.set(millis)
+                time.normalize(true)
+                mController?.sendEvent(this as Object?, EventType.GO_TO, time, time, -1,
+                    ViewType.CURRENT)
+            }
+        }
+    }
+
+    @Override
+    protected override fun onCreate(icicle: Bundle?) {
+        super.onCreate(icicle)
+        if (icicle != null && icicle.containsKey(BUNDLE_KEY_CHECK_ACCOUNTS)) {
+            mCheckForAccounts = icicle.getBoolean(BUNDLE_KEY_CHECK_ACCOUNTS)
+        }
+        // Launch add google account if this is first time and there are no
+        // accounts yet
+        if (mCheckForAccounts) {
+            mHandler = QueryHandler(this.getContentResolver())
+            mHandler?.startQuery(
+                0, null, Calendars.CONTENT_URI, arrayOf<String>(
+                    Calendars._ID
+                ), null, null /* selection args */, null /* sort order */
+            )
+        }
+
+        // This needs to be created before setContentView
+        mController = CalendarController.getInstance(this)
+
+        // Get time from intent or icicle
+        var timeMillis: Long = -1
+        var viewType = -1
+        val intent: Intent = getIntent()
+        if (icicle != null) {
+            timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME)
+            viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1)
+        } else {
+            val action: String? = intent.getAction()
+            if (Intent.ACTION_VIEW.equals(action)) {
+                // Open EventInfo later
+                timeMillis = parseViewAction(intent)
+            }
+            if (timeMillis == -1L) {
+                timeMillis = Utils.timeFromIntentInMillis(intent) as Long
+            }
+        }
+        if (viewType == -1 || viewType > ViewType.MAX_VALUE) {
+            viewType = Utils.getViewTypeFromIntentAndSharedPref(this)
+        }
+        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater)
+        val t = Time(mTimeZone)
+        t.set(timeMillis)
+        if (DEBUG) {
+            if (icicle != null && intent != null) {
+                Log.d(
+                    TAG,
+                    "both, icicle:" + icicle.toString().toString() + "  intent:" + intent.toString()
+                )
+            } else {
+                Log.d(TAG, "not both, icicle:$icicle intent:$intent")
+            }
+        }
+        val res: Resources = getResources()
+        mHideString = res.getString(R.string.hide_controls)
+        mShowString = res.getString(R.string.show_controls)
+        mOrientation = res.getConfiguration().orientation
+        if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+            mControlsAnimateWidth = res.getDimension(R.dimen.calendar_controls_width).toInt()
+            if (mControlsParams == null) {
+                mControlsParams = LayoutParams(mControlsAnimateWidth, 0)
+            }
+            mControlsParams?.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
+                as RelativeLayout.LayoutParams
+        } else {
+            // Make sure width is in between allowed min and max width values
+            mControlsAnimateWidth = Math.max(
+                res.getDisplayMetrics().widthPixels * 45 / 100,
+                res.getDimension(R.dimen.min_portrait_calendar_controls_width).toInt()
+            )
+            mControlsAnimateWidth = Math.min(
+                mControlsAnimateWidth,
+                res.getDimension(R.dimen.max_portrait_calendar_controls_width).toInt()
+            )
+        }
+        mControlsAnimateHeight = res?.getDimension(R.dimen.calendar_controls_height).toInt()
+        mHideControls = true
+        mIsMultipane = Utils.getConfigBool(this, R.bool.multiple_pane_config)
+        mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config)
+        mShowCalendarControls = Utils.getConfigBool(this, R.bool.show_calendar_controls)
+        mShowEventInfoFullScreen = Utils.getConfigBool(this, R.bool.show_event_info_full_screen)
+        mCalendarControlsAnimationTime = res.getInteger(R.integer.calendar_controls_animation_time)
+        Utils.setAllowWeekForDetailView(mIsMultipane)
+
+        // setContentView must be called before configureActionBar
+        setContentView(R.layout.all_in_one)
+        if (mIsTabletConfig) {
+            mDateRange = findViewById(R.id.date_bar) as TextView?
+            mWeekTextView = findViewById(R.id.week_num) as TextView?
+        } else {
+            mDateRange = getLayoutInflater().inflate(R.layout.date_range_title, null) as TextView
+        }
+
+        // configureActionBar auto-selects the first tab you add, so we need to
+        // call it before we set up our own fragments to make sure it doesn't
+        // overwrite us
+        configureActionBar(viewType)
+        mHomeTime = findViewById(R.id.home_time) as TextView?
+        mMiniMonth = findViewById(R.id.mini_month)
+        if (mIsTabletConfig && mOrientation == Configuration.ORIENTATION_PORTRAIT) {
+            mMiniMonth?.setLayoutParams(
+                LayoutParams(
+                    mControlsAnimateWidth,
+                    mControlsAnimateHeight
+                )
+            )
+        }
+        mCalendarsList = findViewById(R.id.calendar_list)
+        mMiniMonthContainer = findViewById(R.id.mini_month_container)
+        mSecondaryPane = findViewById(R.id.secondary_pane)
+
+        // Must register as the first activity because this activity can modify
+        // the list of event handlers in it's handle method. This affects who
+        // the rest of the handlers the controller dispatches to are.
+        mController?.registerFirstEventHandler(HANDLER_KEY, this)
+        initFragments(timeMillis, viewType, icicle)
+
+        // Listen for changes that would require this to be refreshed
+        val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this)
+        prefs?.registerOnSharedPreferenceChangeListener(this)
+        mContentResolver = getContentResolver()
+    }
+
+    private fun parseViewAction(intent: Intent?): Long {
+        var timeMillis: Long = -1
+        val data: Uri? = intent?.getData()
+        if (data != null && data?.isHierarchical()) {
+            val path = data.getPathSegments()
+            if (path?.size == 2 && path!![0].equals("events")) {
+                try {
+                    mViewEventId = data.getLastPathSegment()?.toLong() as Long
+                    if (mViewEventId != -1L) {
+                        mIntentEventStartMillis = intent?.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0)
+                        mIntentEventEndMillis = intent?.getLongExtra(EXTRA_EVENT_END_TIME, 0)
+                        mIntentAttendeeResponse = intent?.getIntExtra(
+                            ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE
+                        )
+                        mIntentAllDay = intent?.getBooleanExtra(EXTRA_EVENT_ALL_DAY, false)
+                            as Boolean
+                        timeMillis = mIntentEventStartMillis
+                    }
+                } catch (e: NumberFormatException) {
+                    // Ignore if mViewEventId can't be parsed
+                }
+            }
+        }
+        return timeMillis
+    }
+
+    private fun configureActionBar(viewType: Int) {
+        createButtonsSpinner(viewType, mIsTabletConfig)
+        if (mIsMultipane) {
+            mActionBar?.setDisplayOptions(
+                ActionBar.DISPLAY_SHOW_CUSTOM or ActionBar.DISPLAY_SHOW_HOME
+            )
+        } else {
+            mActionBar?.setDisplayOptions(0)
+        }
+    }
+
+    private fun createButtonsSpinner(viewType: Int, tabletConfig: Boolean) {
+        // If tablet configuration , show spinner with no dates
+        mActionBarMenuSpinnerAdapter = CalendarViewAdapter(this, viewType, !tabletConfig)
+        mActionBar = getActionBar()
+        mActionBar?.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST)
+        mActionBar?.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this)
+        when (viewType) {
+            ViewType.AGENDA -> {
+            }
+            ViewType.DAY -> mActionBar?.setSelectedNavigationItem(BUTTON_DAY_INDEX)
+            ViewType.WEEK -> mActionBar?.setSelectedNavigationItem(BUTTON_WEEK_INDEX)
+            ViewType.MONTH -> mActionBar?.setSelectedNavigationItem(BUTTON_MONTH_INDEX)
+            else -> mActionBar?.setSelectedNavigationItem(BUTTON_DAY_INDEX)
+        }
+    }
+
+    // Clear buttons used in the agenda view
+    private fun clearOptionsMenu() {
+        if (mOptionsMenu == null) {
+            return
+        }
+        val cancelItem: MenuItem? = mOptionsMenu?.findItem(R.id.action_cancel)
+        if (cancelItem != null) {
+            cancelItem?.setVisible(false)
+        }
+    }
+
+    @Override
+    protected override fun onResume() {
+        super.onResume()
+
+        // Check if the upgrade code has ever been run. If not, force a sync just this one time.
+        Utils.trySyncAndDisableUpgradeReceiver(this)
+
+        // Must register as the first activity because this activity can modify
+        // the list of event handlers in it's handle method. This affects who
+        // the rest of the handlers the controller dispatches to are.
+        mController?.registerFirstEventHandler(HANDLER_KEY, this)
+        mOnSaveInstanceStateCalled = false
+        mContentResolver?.registerContentObserver(
+            CalendarContract.Events.CONTENT_URI,
+            true, mObserver
+        )
+        if (mUpdateOnResume) {
+            initFragments(mController?.time as Long, mController?.viewType as Int, null)
+            mUpdateOnResume = false
+        }
+        val t = Time(mTimeZone)
+        t.set(mController?.time as Long)
+        mController?.sendEvent(
+            this as Object?, EventType.UPDATE_TITLE, t, t, -1, ViewType.CURRENT,
+            mController?.dateFlags as Long, null, null
+        )
+        // Make sure the drop-down menu will get its date updated at midnight
+        if (mActionBarMenuSpinnerAdapter != null) {
+            mActionBarMenuSpinnerAdapter?.refresh(this)
+        }
+        if (mControlsMenu != null) {
+            mControlsMenu?.setTitle(if (mHideControls) mShowString else mHideString)
+        }
+        mPaused = false
+        if (mViewEventId != -1L && mIntentEventStartMillis != -1L && mIntentEventEndMillis != -1L) {
+            val currentMillis: Long = System.currentTimeMillis()
+            var selectedTime: Long = -1
+            if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) {
+                selectedTime = currentMillis
+            }
+            mController?.sendEventRelatedEventWithExtra(
+                this as Object?, EventType.VIEW_EVENT, mViewEventId,
+                mIntentEventStartMillis, mIntentEventEndMillis, -1, -1,
+                EventInfo.buildViewExtraLong(mIntentAttendeeResponse, mIntentAllDay),
+                selectedTime
+            )
+            mViewEventId = -1
+            mIntentEventStartMillis = -1
+            mIntentEventEndMillis = -1
+            mIntentAllDay = false
+        }
+        Utils.setMidnightUpdater(mHandler, mTimeChangesUpdater, mTimeZone)
+        // Make sure the today icon is up to date
+        invalidateOptionsMenu()
+    }
+
+    @Override
+    protected override fun onPause() {
+        super.onPause()
+        mController?.deregisterEventHandler(HANDLER_KEY)
+        mPaused = true
+        mHomeTime?.removeCallbacks(mHomeTimeUpdater)
+        if (mActionBarMenuSpinnerAdapter != null) {
+            mActionBarMenuSpinnerAdapter?.onPause()
+        }
+        mContentResolver?.unregisterContentObserver(mObserver)
+        if (isFinishing()) {
+            // Stop listening for changes that would require this to be refreshed
+            val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this)
+            prefs?.unregisterOnSharedPreferenceChangeListener(this)
+        }
+        // FRAG_TODO save highlighted days of the week;
+        if (mController?.viewType != ViewType.EDIT) {
+            Utils.setDefaultView(this, mController?.viewType as Int)
+        }
+        Utils.resetMidnightUpdater(mHandler, mTimeChangesUpdater)
+    }
+
+    @Override
+    protected override fun onUserLeaveHint() {
+        mController?.sendEvent(this as Object?, EventType.USER_HOME, null, null, -1,
+            ViewType.CURRENT)
+        super.onUserLeaveHint()
+    }
+
+    @Override
+    override fun onSaveInstanceState(outState: Bundle) {
+        mOnSaveInstanceStateCalled = true
+        super.onSaveInstanceState(outState)
+    }
+
+    @Override
+    protected override fun onDestroy() {
+        super.onDestroy()
+        val prefs: SharedPreferences? = GeneralPreferences.getSharedPreferences(this)
+        prefs?.unregisterOnSharedPreferenceChangeListener(this)
+        mController?.deregisterAllEventHandlers()
+        CalendarController.removeInstance(this)
+    }
+
+    private fun initFragments(timeMillis: Long, viewType: Int, icicle: Bundle?) {
+        if (DEBUG) {
+            Log.d(TAG, "Initializing to $timeMillis for view $viewType")
+        }
+        val ft: FragmentTransaction = getFragmentManager().beginTransaction()
+        if (mShowCalendarControls) {
+            val miniMonthFrag: Fragment = MonthByWeekFragment(timeMillis, true)
+            ft.replace(R.id.mini_month, miniMonthFrag)
+            mController?.registerEventHandler(R.id.mini_month, miniMonthFrag as EventHandler)
+        }
+        if (!mShowCalendarControls || viewType == ViewType.EDIT) {
+            mMiniMonth?.setVisibility(View.GONE)
+            mCalendarsList?.setVisibility(View.GONE)
+        }
+        var info: EventInfo? = null
+        if (viewType == ViewType.EDIT) {
+            mPreviousView = GeneralPreferences.getSharedPreferences(this)?.getInt(
+                GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW
+            ) as Int
+            var eventId: Long = -1
+            val intent: Intent = getIntent()
+            val data: Uri? = intent.getData()
+            if (data != null) {
+                try {
+                    eventId = data?.getLastPathSegment()?.toLong() as Long
+                } catch (e: NumberFormatException) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Create new event")
+                    }
+                }
+            } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) {
+                eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID)
+            }
+            val begin: Long = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1)
+            val end: Long = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1)
+            info = EventInfo()
+            if (end != -1L) {
+                info?.endTime = Time()
+                info?.endTime?.set(end)
+            }
+            if (begin != -1L) {
+                info?.startTime = Time()
+                info?.startTime?.set(begin)
+            }
+            info.id = eventId
+            // We set the viewtype so if the user presses back when they are
+            // done editing the controller knows we were in the Edit Event
+            // screen. Likewise for eventId
+            mController?.viewType = viewType
+            mController?.eventId = eventId
+        } else {
+            mPreviousView = viewType
+        }
+        setMainPane(ft, R.id.main_pane, viewType, timeMillis, true)
+        ft.commit() // this needs to be after setMainPane()
+        val t = Time(mTimeZone)
+        t.set(timeMillis)
+        if (viewType != ViewType.EDIT) {
+            mController?.sendEvent(this as Object?, EventType.GO_TO, t, null, -1, viewType)
+        }
+    }
+
+    @Override
+    override fun onBackPressed() {
+        if (mCurrentView == ViewType.EDIT || mBackToPreviousView) {
+            mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, mPreviousView)
+        } else {
+            super.onBackPressed()
+        }
+    }
+
+    @Override
+    override fun onCreateOptionsMenu(menu: Menu): Boolean {
+        super.onCreateOptionsMenu(menu)
+        mOptionsMenu = menu
+        getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu)
+
+        // Hide the "show/hide controls" button if this is a phone
+        // or the view type is "Month".
+        mControlsMenu = menu.findItem(R.id.action_hide_controls)
+        if (!mShowCalendarControls) {
+            if (mControlsMenu != null) {
+                mControlsMenu?.setVisible(false)
+                mControlsMenu?.setEnabled(false)
+            }
+        } else if (mControlsMenu != null && mController != null &&
+            mController?.viewType == ViewType.MONTH) {
+            mControlsMenu?.setVisible(false)
+            mControlsMenu?.setEnabled(false)
+        } else if (mControlsMenu != null) {
+            mControlsMenu?.setTitle(if (mHideControls) mShowString else mHideString)
+        }
+        val menuItem: MenuItem = menu.findItem(R.id.action_today)
+        if (Utils.isJellybeanOrLater()) {
+            // replace the default top layer drawable of the today icon with a
+            // custom drawable that shows the day of the month of today
+            val icon: LayerDrawable = menuItem.getIcon() as LayerDrawable
+            Utils.setTodayIcon(icon, this, mTimeZone)
+        } else {
+            menuItem.setIcon(R.drawable.ic_menu_today_no_date_holo_light)
+        }
+        return true
+    }
+
+    @Override
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        var t: Time? = null
+        var viewType: Int = ViewType.CURRENT
+        var extras: Long = CalendarController.EXTRA_GOTO_TIME
+        val itemId: Int = item.getItemId()
+        if (itemId == R.id.action_today) {
+            viewType = ViewType.CURRENT
+            t = Time(mTimeZone)
+            t.setToNow()
+            extras = extras or CalendarController.EXTRA_GOTO_TODAY
+        } else if (itemId == R.id.action_hide_controls) {
+            mHideControls = !mHideControls
+            item.setTitle(if (mHideControls) mShowString else mHideString)
+            if (!mHideControls) {
+                mMiniMonth?.setVisibility(View.VISIBLE)
+                mCalendarsList?.setVisibility(View.VISIBLE)
+                mMiniMonthContainer?.setVisibility(View.VISIBLE)
+            }
+            val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt(
+                this, "controlsOffset",
+                if (mHideControls) 0 else mControlsAnimateWidth,
+                if (mHideControls) mControlsAnimateWidth else 0
+            )
+            slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong())
+            ObjectAnimator.setFrameDelay(0)
+            slideAnimation.start()
+            return true
+        } else {
+            Log.d(TAG, "Unsupported itemId: $itemId")
+            return true
+        }
+        mController?.sendEvent(this as Object?, EventType.GO_TO, t, null, t, -1,
+            viewType, extras, null, null)
+        return true
+    }
+
+    /**
+     * Sets the offset of the controls on the right for animating them off/on
+     * screen. ProGuard strips this if it's not in proguard.flags
+     *
+     * @param controlsOffset The current offset in pixels
+     */
+    fun setControlsOffset(controlsOffset: Int) {
+        if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+            mMiniMonth?.setTranslationX(controlsOffset.toFloat())
+            mCalendarsList?.setTranslationX(controlsOffset.toFloat())
+            mControlsParams?.width = Math.max(0, mControlsAnimateWidth - controlsOffset)
+            mMiniMonthContainer?.setLayoutParams(mControlsParams)
+        } else {
+            mMiniMonth?.setTranslationY(controlsOffset.toFloat())
+            mCalendarsList?.setTranslationY(controlsOffset.toFloat())
+            if (mVerticalControlsParams == null) {
+                mVerticalControlsParams = LayoutParams(
+                    LinearLayout.LayoutParams.MATCH_PARENT, mControlsAnimateHeight
+                ) as LinearLayout.LayoutParams?
+            }
+            mVerticalControlsParams?.height = Math.max(0, mControlsAnimateHeight - controlsOffset)
+            mMiniMonthContainer?.setLayoutParams(mVerticalControlsParams)
+        }
+    }
+
+    @Override
+    override fun onSharedPreferenceChanged(prefs: SharedPreferences?, key: String) {
+        if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) {
+            if (mPaused) {
+                mUpdateOnResume = true
+            } else {
+                initFragments(mController?.time as Long, mController?.viewType as Int, null)
+            }
+        }
+    }
+
+    private fun setMainPane(
+        ft: FragmentTransaction?,
+        viewId: Int,
+        viewType: Int,
+        timeMillis: Long,
+        force: Boolean
+    ) {
+        var ft: FragmentTransaction? = ft
+        if (mOnSaveInstanceStateCalled) {
+            return
+        }
+        if (!force && mCurrentView == viewType) {
+            return
+        }
+
+        // Remove this when transition to and from month view looks fine.
+        val doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH
+        val fragmentManager: FragmentManager = getFragmentManager()
+        if (viewType != mCurrentView) {
+            // The rules for this previous view are different than the
+            // controller's and are used for intercepting the back button.
+            if (mCurrentView != ViewType.EDIT && mCurrentView > 0) {
+                mPreviousView = mCurrentView
+            }
+            mCurrentView = viewType
+        }
+        // Create new fragment
+        var frag: Fragment? = null
+        val secFrag: Fragment? = null
+        when (viewType) {
+            ViewType.AGENDA -> {
+            }
+            ViewType.DAY -> {
+                if (mActionBar != null && mActionBar?.getSelectedTab() != mDayTab) {
+                    mActionBar?.selectTab(mDayTab)
+                }
+                if (mActionBarMenuSpinnerAdapter != null) {
+                    mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX)
+                }
+                frag = DayFragment(timeMillis, 1)
+            }
+            ViewType.MONTH -> {
+                if (mActionBar != null && mActionBar?.getSelectedTab() != mMonthTab) {
+                    mActionBar?.selectTab(mMonthTab)
+                }
+                if (mActionBarMenuSpinnerAdapter != null) {
+                    mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX)
+                }
+                frag = MonthByWeekFragment(timeMillis, false)
+            }
+            ViewType.WEEK -> {
+                if (mActionBar != null && mActionBar?.getSelectedTab() != mWeekTab) {
+                    mActionBar?.selectTab(mWeekTab)
+                }
+                if (mActionBarMenuSpinnerAdapter != null) {
+                    mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX)
+                }
+                frag = DayFragment(timeMillis, 7)
+            }
+            else -> {
+                if (mActionBar != null && mActionBar?.getSelectedTab() != mWeekTab) {
+                    mActionBar?.selectTab(mWeekTab)
+                }
+                if (mActionBarMenuSpinnerAdapter != null) {
+                    mActionBar?.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX)
+                }
+                frag = DayFragment(timeMillis, 7)
+            }
+        }
+
+        // Update the current view so that the menu can update its look according to the
+        // current view.
+        if (mActionBarMenuSpinnerAdapter != null) {
+            mActionBarMenuSpinnerAdapter?.setMainView(viewType)
+            if (!mIsTabletConfig) {
+                mActionBarMenuSpinnerAdapter?.setTime(timeMillis)
+            }
+        }
+
+        // Show date only on tablet configurations in views different than Agenda
+        if (!mIsTabletConfig) {
+            mDateRange?.setVisibility(View.GONE)
+        } else {
+            mDateRange?.setVisibility(View.GONE)
+        }
+
+        // Clear unnecessary buttons from the option menu when switching from the agenda view
+        if (viewType != ViewType.AGENDA) {
+            clearOptionsMenu()
+        }
+        var doCommit = false
+        if (ft == null) {
+            doCommit = true
+            ft = fragmentManager.beginTransaction()
+        }
+        if (doTransition) {
+            ft?.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
+        }
+        ft?.replace(viewId, frag)
+        if (DEBUG) {
+            Log.d(TAG, "Adding handler with viewId $viewId and type $viewType")
+        }
+        // If the key is already registered this will replace it
+        mController?.registerEventHandler(viewId, frag as EventHandler?)
+        if (doCommit) {
+            if (DEBUG) {
+                Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing())
+            }
+            ft?.commit()
+        }
+    }
+
+    private fun setTitleInActionBar(event: EventInfo) {
+        if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) {
+            return
+        }
+        val start: Long? = event?.startTime?.toMillis(false /* use isDst */)
+        val end: Long?
+        end = if (event.endTime != null) {
+            event?.endTime?.toMillis(false /* use isDst */)
+        } else {
+            start
+        }
+        val msg: String? = Utils.formatDateRange(this,
+            start as Long,
+            end as Long,
+            event.extraLong.toInt()
+        )
+        val oldDate: CharSequence? = mDateRange?.getText()
+        mDateRange?.setText(msg)
+        updateSecondaryTitleFields(if (event?.selectedTime != null)
+            event?.selectedTime?.toMillis(true) as Long else start)
+        if (!TextUtils.equals(oldDate, msg)) {
+            mDateRange?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+            if (mShowWeekNum && mWeekTextView != null) {
+                mWeekTextView?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+            }
+        }
+    }
+
+    private fun updateSecondaryTitleFields(visibleMillisSinceEpoch: Long) {
+        mShowWeekNum = Utils.getShowWeekNumber(this)
+        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater)
+        if (visibleMillisSinceEpoch != -1L) {
+            val weekNum: Int = Utils.getWeekNumberFromTime(visibleMillisSinceEpoch, this)
+            mWeekNum = weekNum
+        }
+        if (mShowWeekNum && mCurrentView == ViewType.WEEK && mIsTabletConfig &&
+            mWeekTextView != null
+        ) {
+            val weekString: String = getResources().getQuantityString(
+                R.plurals.weekN, mWeekNum,
+                mWeekNum
+            )
+            mWeekTextView?.setText(weekString)
+            mWeekTextView?.setVisibility(View.VISIBLE)
+        } else if (visibleMillisSinceEpoch != -1L && mWeekTextView != null &&
+            mCurrentView == ViewType.DAY && mIsTabletConfig) {
+            val time = Time(mTimeZone)
+            time.set(visibleMillisSinceEpoch)
+            val julianDay: Int = Time.getJulianDay(visibleMillisSinceEpoch, time.gmtoff)
+            time.setToNow()
+            val todayJulianDay: Int = Time.getJulianDay(time.toMillis(false), time.gmtoff)
+            val dayString: String = Utils.getDayOfWeekString(
+                julianDay,
+                todayJulianDay,
+                visibleMillisSinceEpoch,
+                this
+            )
+            mWeekTextView?.setText(dayString)
+            mWeekTextView?.setVisibility(View.VISIBLE)
+        } else if (mWeekTextView != null && (!mIsTabletConfig || mCurrentView != ViewType.DAY)) {
+            mWeekTextView?.setVisibility(View.GONE)
+        }
+        if (mHomeTime != null && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK) &&
+            !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())
+        ) {
+            val time = Time(mTimeZone)
+            time.setToNow()
+            val millis: Long = time.toMillis(true)
+            val isDST = time.isDst !== 0
+            var flags: Int = DateUtils.FORMAT_SHOW_TIME
+            if (DateFormat.is24HourFormat(this)) {
+                flags = flags or DateUtils.FORMAT_24HOUR
+            }
+            // Formats the time as
+            val timeString: String = StringBuilder(
+                Utils.formatDateRange(this, millis, millis, flags)
+            ).append(" ").append(
+                TimeZone.getTimeZone(mTimeZone).getDisplayName(
+                    isDST, TimeZone.SHORT, Locale.getDefault()
+                )
+            ).toString()
+            mHomeTime?.setText(timeString)
+            mHomeTime?.setVisibility(View.VISIBLE)
+            // Update when the minute changes
+            mHomeTime?.removeCallbacks(mHomeTimeUpdater)
+            mHomeTime?.postDelayed(
+                mHomeTimeUpdater,
+                DateUtils.MINUTE_IN_MILLIS - millis % DateUtils.MINUTE_IN_MILLIS
+            )
+        } else if (mHomeTime != null) {
+            mHomeTime?.setVisibility(View.GONE)
+        }
+    }
+
+    @get:Override override val supportedEventTypes: Long
+        get() = EventType.GO_TO or EventType.UPDATE_TITLE
+
+    @Override
+    override fun handleEvent(event: EventInfo?) {
+        var displayTime: Long = -1
+        if (event?.eventType == EventType.GO_TO) {
+            if (event?.extraLong and CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS != 0L) {
+                mBackToPreviousView = true
+            } else if (event?.viewType != mController?.previousViewType &&
+                event?.viewType != ViewType.EDIT
+            ) {
+                // Clear the flag is change to a different view type
+                mBackToPreviousView = false
+            }
+            setMainPane(
+                null, R.id.main_pane, event?.viewType, event?.startTime?.toMillis(false)
+                    as Long, false
+            )
+            if (mShowCalendarControls) {
+                val animationSize =
+                    if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) mControlsAnimateWidth
+                    else mControlsAnimateHeight
+                val noControlsView = event?.viewType == ViewType.MONTH
+                if (mControlsMenu != null) {
+                    mControlsMenu?.setVisible(!noControlsView)
+                    mControlsMenu?.setEnabled(!noControlsView)
+                }
+                if (noControlsView || mHideControls) {
+                    // hide minimonth and calendar frag
+                    mShowSideViews = false
+                    if (!mHideControls) {
+                        val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt(
+                            this,
+                            "controlsOffset", 0, animationSize
+                        )
+                        slideAnimation.addListener(mSlideAnimationDoneListener)
+                        slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong())
+                        ObjectAnimator.setFrameDelay(0)
+                        slideAnimation.start()
+                    } else {
+                        mMiniMonth?.setVisibility(View.GONE)
+                        mCalendarsList?.setVisibility(View.GONE)
+                        mMiniMonthContainer?.setVisibility(View.GONE)
+                    }
+                } else {
+                    // show minimonth and calendar frag
+                    mShowSideViews = true
+                    mMiniMonth?.setVisibility(View.VISIBLE)
+                    mCalendarsList?.setVisibility(View.VISIBLE)
+                    mMiniMonthContainer?.setVisibility(View.VISIBLE)
+                    if (!mHideControls &&
+                        mController?.previousViewType == ViewType.MONTH
+                    ) {
+                        val slideAnimation: ObjectAnimator = ObjectAnimator.ofInt(
+                            this,
+                            "controlsOffset", animationSize, 0
+                        )
+                        slideAnimation.setDuration(mCalendarControlsAnimationTime.toLong())
+                        ObjectAnimator.setFrameDelay(0)
+                        slideAnimation.start()
+                    }
+                }
+            }
+            displayTime =
+                if (event?.selectedTime != null) event?.selectedTime?.toMillis(true) as Long
+                else event?.startTime?.toMillis(true) as Long
+            if (!mIsTabletConfig) {
+                mActionBarMenuSpinnerAdapter?.setTime(displayTime)
+            }
+        } else if (event?.eventType == EventType.UPDATE_TITLE) {
+            setTitleInActionBar(event as CalendarController.EventInfo)
+            if (!mIsTabletConfig) {
+                mActionBarMenuSpinnerAdapter?.setTime(mController?.time as Long)
+            }
+        }
+        updateSecondaryTitleFields(displayTime)
+    }
+
+    @Override
+    override fun eventsChanged() {
+        mController?.sendEvent(this as Object?, EventType.EVENTS_CHANGED, null, null, -1,
+            ViewType.CURRENT)
+    }
+
+    @Override
+    override fun onTabSelected(tab: Tab?, ft: FragmentTransaction?) {
+        Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing())
+        if (tab == mDayTab && mCurrentView != ViewType.DAY) {
+            mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.DAY)
+        } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) {
+            mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.WEEK)
+        } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) {
+            mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1, ViewType.MONTH)
+        } else {
+            Log.w(
+                TAG, "TabSelected event from unknown tab: " +
+                    if (tab == null) "null" else tab.getText()
+            )
+            Log.w(
+                TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab +
+                    " Week:" + mWeekTab + " Month:" + mMonthTab
+            )
+        }
+    }
+
+    @Override
+    override fun onTabReselected(tab: Tab?, ft: FragmentTransaction?) {
+    }
+
+    @Override
+    override fun onTabUnselected(tab: Tab?, ft: FragmentTransaction?) {
+    }
+
+    @Override
+    override fun onNavigationItemSelected(itemPosition: Int, itemId: Long): Boolean {
+        when (itemPosition) {
+            CalendarViewAdapter.DAY_BUTTON_INDEX -> if (mCurrentView != ViewType.DAY) {
+                mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1,
+                    ViewType.DAY)
+            }
+            CalendarViewAdapter.WEEK_BUTTON_INDEX -> if (mCurrentView != ViewType.WEEK) {
+                mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1,
+                    ViewType.WEEK)
+            }
+            CalendarViewAdapter.MONTH_BUTTON_INDEX -> if (mCurrentView != ViewType.MONTH) {
+                mController?.sendEvent(this as Object?, EventType.GO_TO, null, null, -1,
+                    ViewType.MONTH)
+            }
+            CalendarViewAdapter.AGENDA_BUTTON_INDEX -> {
+            }
+            else -> {
+                Log.w(TAG, "ItemSelected event from unknown button: $itemPosition")
+                Log.w(
+                    TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition +
+                        " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab
+                )
+            }
+        }
+        return false
+    }
+
+    companion object {
+        private const val TAG = "AllInOneActivity"
+        private const val DEBUG = false
+        private const val EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment"
+        private const val BUNDLE_KEY_RESTORE_TIME = "key_restore_time"
+        private const val BUNDLE_KEY_EVENT_ID = "key_event_id"
+        private const val BUNDLE_KEY_RESTORE_VIEW = "key_restore_view"
+        private const val BUNDLE_KEY_CHECK_ACCOUNTS = "key_check_for_accounts"
+        private const val HANDLER_KEY = 0
+
+        // Indices of buttons for the drop down menu (tabs replacement)
+        // Must match the strings in the array buttons_list in arrays.xml and the
+        // OnNavigationListener
+        private const val BUTTON_DAY_INDEX = 0
+        private const val BUTTON_WEEK_INDEX = 1
+        private const val BUTTON_MONTH_INDEX = 2
+        private const val BUTTON_AGENDA_INDEX = 3
+        private var mIsMultipane = false
+        private var mIsTabletConfig = false
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/AsyncQueryServiceHelper.java b/src/com/android/calendar/AsyncQueryServiceHelper.java
deleted file mode 100644
index c6e0a2b..0000000
--- a/src/com/android/calendar/AsyncQueryServiceHelper.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import android.app.IntentService;
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.PriorityQueue;
-import java.util.concurrent.Delayed;
-import java.util.concurrent.TimeUnit;
-
-public class AsyncQueryServiceHelper extends IntentService {
-    private static final String TAG = "AsyncQuery";
-
-    public AsyncQueryServiceHelper(String name) {
-        super(name);
-    }
-
-    public AsyncQueryServiceHelper() {
-        super("AsyncQueryServiceHelper");
-    }
-
-    @Override
-    protected void onHandleIntent(Intent intent) {
-    }
-
-    @Override
-    public void onStart(Intent intent, int startId) {
-        super.onStart(intent, startId);
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-    }
-}
diff --git a/src/com/android/calendar/AsyncQueryServiceHelper.kt b/src/com/android/calendar/AsyncQueryServiceHelper.kt
new file mode 100644
index 0000000..4797330
--- /dev/null
+++ b/src/com/android/calendar/AsyncQueryServiceHelper.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import android.app.IntentService
+import android.content.ContentProviderOperation
+import android.content.ContentResolver
+import android.content.ContentValues
+import android.content.Context
+import android.content.Intent
+import android.content.OperationApplicationException
+import android.database.Cursor
+import android.net.Uri
+import android.os.Handler
+import android.os.Message
+import android.os.RemoteException
+import android.os.SystemClock
+import android.util.Log
+import java.util.ArrayList
+import java.util.Arrays
+import java.util.Iterator
+import java.util.PriorityQueue
+import java.util.concurrent.Delayed
+import java.util.concurrent.TimeUnit
+
+class AsyncQueryServiceHelper : IntentService {
+    constructor(name: String?) : super(name) {}
+    constructor() : super("AsyncQueryServiceHelper") {}
+
+    protected override fun onHandleIntent(intent: Intent?) {
+    }
+
+    override fun onStart(intent: Intent?, startId: Int) {
+        super.onStart(intent, startId)
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+    }
+
+    companion object {
+        private const val TAG = "AsyncQuery"
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/CalendarApplication.java b/src/com/android/calendar/CalendarApplication.kt
similarity index 70%
rename from src/com/android/calendar/CalendarApplication.java
rename to src/com/android/calendar/CalendarApplication.kt
index d0ca469..445d725 100644
--- a/src/com/android/calendar/CalendarApplication.java
+++ b/src/com/android/calendar/CalendarApplication.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,20 +13,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.calendar
 
-package com.android.calendar;
+import android.app.Application
 
-import android.app.Application;
-
-public class CalendarApplication extends Application {
-    @Override
-    public void onCreate() {
-        super.onCreate();
+class CalendarApplication : Application() {
+    override fun onCreate() {
+        super.onCreate()
 
         /*
          * Ensure the default values are set for any receiver, activity,
          * service, etc. of Calendar
          */
-        GeneralPreferences.setDefaultValues(this);
+        GeneralPreferences.setDefaultValues(this)
     }
-}
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/CalendarBackupAgent.java b/src/com/android/calendar/CalendarBackupAgent.java
deleted file mode 100644
index 02456fd..0000000
--- a/src/com/android/calendar/CalendarBackupAgent.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.BackupDataInput;
-import android.app.backup.SharedPreferencesBackupHelper;
-import android.content.Context;
-import android.content.SharedPreferences.Editor;
-import android.os.ParcelFileDescriptor;
-
-import java.io.IOException;
-
-public class CalendarBackupAgent extends BackupAgentHelper
-{
-    static final String SHARED_KEY = "shared_pref";
-
-    @Override
-    public void onCreate() {
-    }
-
-    @Override
-    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
-            throws IOException {
-        super.onRestore(data, appVersionCode, newState);
-    }
-}
diff --git a/src/com/android/calendar/CalendarBackupAgent.kt b/src/com/android/calendar/CalendarBackupAgent.kt
new file mode 100644
index 0000000..f3e230a
--- /dev/null
+++ b/src/com/android/calendar/CalendarBackupAgent.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import android.app.backup.BackupAgentHelper
+import android.app.backup.BackupDataInput
+import android.app.backup.SharedPreferencesBackupHelper
+import android.content.Context
+import android.content.SharedPreferences.Editor
+import android.os.ParcelFileDescriptor
+
+import java.io.IOException
+
+class CalendarBackupAgent : BackupAgentHelper() {
+    override fun onCreate() {
+    }
+
+    @Throws(IOException::class)
+    override fun onRestore(data: BackupDataInput?, appVersionCode: Int,
+                           newState: ParcelFileDescriptor?) {
+        super.onRestore(data, appVersionCode, newState)
+    }
+
+    companion object {
+        const val SHARED_KEY = "shared_pref"
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/CalendarController.java b/src/com/android/calendar/CalendarController.java
deleted file mode 100644
index 37286f2..0000000
--- a/src/com/android/calendar/CalendarController.java
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
-import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
-import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
-import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.CalendarContract.Attendees;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.text.format.Time;
-import android.util.Log;
-import android.util.Pair;
-
-import java.lang.ref.WeakReference;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
-import java.util.Map.Entry;
-import java.util.WeakHashMap;
-
-public class CalendarController {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "CalendarController";
-
-    public static final String EVENT_EDIT_ON_LAUNCH = "editMode";
-
-    public static final int MIN_CALENDAR_YEAR = 1970;
-    public static final int MAX_CALENDAR_YEAR = 2036;
-    public static final int MIN_CALENDAR_WEEK = 0;
-    public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037
-
-    private final Context mContext;
-
-    // This uses a LinkedHashMap so that we can replace fragments based on the
-    // view id they are being expanded into since we can't guarantee a reference
-    // to the handler will be findable
-    private final LinkedHashMap<Integer,EventHandler> eventHandlers =
-            new LinkedHashMap<Integer,EventHandler>(5);
-    private final LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>();
-    private final LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap<
-            Integer, EventHandler>();
-    private Pair<Integer, EventHandler> mFirstEventHandler;
-    private Pair<Integer, EventHandler> mToBeAddedFirstEventHandler;
-    private volatile int mDispatchInProgressCounter = 0;
-
-    private static WeakHashMap<Context, WeakReference<CalendarController>> instances =
-        new WeakHashMap<Context, WeakReference<CalendarController>>();
-
-    private final WeakHashMap<Object, Long> filters = new WeakHashMap<Object, Long>(1);
-
-    private int mViewType = -1;
-    private int mDetailViewType = -1;
-    private int mPreviousViewType = -1;
-    private long mEventId = -1;
-    private final Time mTime = new Time();
-    private long mDateFlags = 0;
-
-    private final Runnable mUpdateTimezone = new Runnable() {
-        @Override
-        public void run() {
-            mTime.switchTimezone(Utils.getTimeZone(mContext, this));
-        }
-    };
-
-    /**
-     * One of the event types that are sent to or from the controller
-     */
-    public interface EventType {
-        // Simple view of an event
-        final long VIEW_EVENT = 1L << 1;
-
-        // Full detail view in read only mode
-        final long VIEW_EVENT_DETAILS = 1L << 2;
-
-        // full detail view in edit mode
-        final long EDIT_EVENT = 1L << 3;
-
-        final long GO_TO = 1L << 5;
-
-        final long EVENTS_CHANGED = 1L << 7;
-
-        final long USER_HOME = 1L << 9;
-
-        // date range has changed, update the title
-        final long UPDATE_TITLE = 1L << 10;
-    }
-
-    /**
-     * One of the Agenda/Day/Week/Month view types
-     */
-    public interface ViewType {
-        final int DETAIL = -1;
-        final int CURRENT = 0;
-        final int AGENDA = 1;
-        final int DAY = 2;
-        final int WEEK = 3;
-        final int MONTH = 4;
-        final int EDIT = 5;
-        final int MAX_VALUE = 5;
-    }
-
-    public static class EventInfo {
-
-        private static final long ATTENTEE_STATUS_MASK = 0xFF;
-        private static final long ALL_DAY_MASK = 0x100;
-        private static final int ATTENDEE_STATUS_NONE_MASK = 0x01;
-        private static final int ATTENDEE_STATUS_ACCEPTED_MASK = 0x02;
-        private static final int ATTENDEE_STATUS_DECLINED_MASK = 0x04;
-        private static final int ATTENDEE_STATUS_TENTATIVE_MASK = 0x08;
-
-        public long eventType; // one of the EventType
-        public int viewType; // one of the ViewType
-        public long id; // event id
-        public Time selectedTime; // the selected time in focus
-
-        // Event start and end times.  All-day events are represented in:
-        // - local time for GO_TO commands
-        // - UTC time for VIEW_EVENT and other event-related commands
-        public Time startTime;
-        public Time endTime;
-
-        public int x; // x coordinate in the activity space
-        public int y; // y coordinate in the activity space
-        public String query; // query for a user search
-        public ComponentName componentName;  // used in combination with query
-        public String eventTitle;
-        public long calendarId;
-
-        /**
-         * For EventType.VIEW_EVENT:
-         * It is the default attendee response and an all day event indicator.
-         * Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED,
-         * Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE.
-         * To signal the event is an all-day event, "or" ALL_DAY_MASK with the response.
-         * Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay().
-         * <p>
-         * For EventType.GO_TO:
-         * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time.
-         * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time.
-         * Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view.
-         * Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time.
-         * <p>
-         * For EventType.UPDATE_TITLE:
-         * Set formatting flags for Utils.formatDateRange
-         */
-        public long extraLong;
-
-        public boolean isAllDay() {
-            if (eventType != EventType.VIEW_EVENT) {
-                Log.wtf(TAG, "illegal call to isAllDay , wrong event type " + eventType);
-                return false;
-            }
-            return ((extraLong & ALL_DAY_MASK) != 0) ? true : false;
-        }
-
-        public  int getResponse() {
-            if (eventType != EventType.VIEW_EVENT) {
-                Log.wtf(TAG, "illegal call to getResponse , wrong event type " + eventType);
-                return Attendees.ATTENDEE_STATUS_NONE;
-            }
-
-            int response = (int)(extraLong & ATTENTEE_STATUS_MASK);
-            switch (response) {
-                case ATTENDEE_STATUS_NONE_MASK:
-                    return Attendees.ATTENDEE_STATUS_NONE;
-                case ATTENDEE_STATUS_ACCEPTED_MASK:
-                    return Attendees.ATTENDEE_STATUS_ACCEPTED;
-                case ATTENDEE_STATUS_DECLINED_MASK:
-                    return Attendees.ATTENDEE_STATUS_DECLINED;
-                case ATTENDEE_STATUS_TENTATIVE_MASK:
-                    return Attendees.ATTENDEE_STATUS_TENTATIVE;
-                default:
-                    Log.wtf(TAG,"Unknown attendee response " + response);
-            }
-            return ATTENDEE_STATUS_NONE_MASK;
-        }
-
-        // Used to build the extra long for a VIEW event.
-        public static long buildViewExtraLong(int response, boolean allDay) {
-            long extra = allDay ? ALL_DAY_MASK : 0;
-
-            switch (response) {
-                case Attendees.ATTENDEE_STATUS_NONE:
-                    extra |= ATTENDEE_STATUS_NONE_MASK;
-                    break;
-                case Attendees.ATTENDEE_STATUS_ACCEPTED:
-                    extra |= ATTENDEE_STATUS_ACCEPTED_MASK;
-                    break;
-                case Attendees.ATTENDEE_STATUS_DECLINED:
-                    extra |= ATTENDEE_STATUS_DECLINED_MASK;
-                    break;
-                case Attendees.ATTENDEE_STATUS_TENTATIVE:
-                    extra |= ATTENDEE_STATUS_TENTATIVE_MASK;
-                    break;
-                default:
-                    Log.wtf(TAG,"Unknown attendee response " + response);
-                    extra |= ATTENDEE_STATUS_NONE_MASK;
-                    break;
-            }
-            return extra;
-        }
-    }
-
-    /**
-     * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time
-     * can be ignored
-     */
-    public static final long EXTRA_GOTO_DATE = 1;
-    public static final long EXTRA_GOTO_TIME = 2;
-    public static final long EXTRA_GOTO_BACK_TO_PREVIOUS = 4;
-    public static final long EXTRA_GOTO_TODAY = 8;
-
-    public interface EventHandler {
-        long getSupportedEventTypes();
-        void handleEvent(EventInfo event);
-
-        /**
-         * This notifies the handler that the database has changed and it should
-         * update its view.
-         */
-        void eventsChanged();
-    }
-
-    /**
-     * Creates and/or returns an instance of CalendarController associated with
-     * the supplied context. It is best to pass in the current Activity.
-     *
-     * @param context The activity if at all possible.
-     */
-    public static CalendarController getInstance(Context context) {
-        synchronized (instances) {
-            CalendarController controller = null;
-            WeakReference<CalendarController> weakController = instances.get(context);
-            if (weakController != null) {
-                controller = weakController.get();
-            }
-
-            if (controller == null) {
-                controller = new CalendarController(context);
-                instances.put(context, new WeakReference(controller));
-            }
-            return controller;
-        }
-    }
-
-    /**
-     * Removes an instance when it is no longer needed. This should be called in
-     * an activity's onDestroy method.
-     *
-     * @param context The activity used to create the controller
-     */
-    public static void removeInstance(Context context) {
-        instances.remove(context);
-    }
-
-    private CalendarController(Context context) {
-        mContext = context;
-        mUpdateTimezone.run();
-        mTime.setToNow();
-        mDetailViewType = Utils.getSharedPreference(mContext,
-                GeneralPreferences.KEY_DETAILED_VIEW,
-                GeneralPreferences.DEFAULT_DETAILED_VIEW);
-    }
-
-    public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis,
-            long endMillis, int x, int y, long selectedMillis) {
-        // TODO: pass the real allDay status or at least a status that says we don't know the
-        // status and have the receiver query the data.
-        // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo
-        // so currently the missing allDay status has no effect.
-        sendEventRelatedEventWithExtra(sender, eventType, eventId, startMillis, endMillis, x, y,
-                EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false),
-                selectedMillis);
-    }
-
-    /**
-     * Helper for sending New/View/Edit/Delete events
-     *
-     * @param sender object of the caller
-     * @param eventType one of {@link EventType}
-     * @param eventId event id
-     * @param startMillis start time
-     * @param endMillis end time
-     * @param x x coordinate in the activity space
-     * @param y y coordinate in the activity space
-     * @param extraLong default response value for the "simple event view" and all day indication.
-     *        Use Attendees.ATTENDEE_STATUS_NONE for no response.
-     * @param selectedMillis The time to specify as selected
-     */
-    public void sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId,
-            long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) {
-        sendEventRelatedEventWithExtraWithTitleWithCalendarId(sender, eventType, eventId,
-            startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1);
-    }
-
-    /**
-     * Helper for sending New/View/Edit/Delete events
-     *
-     * @param sender object of the caller
-     * @param eventType one of {@link EventType}
-     * @param eventId event id
-     * @param startMillis start time
-     * @param endMillis end time
-     * @param x x coordinate in the activity space
-     * @param y y coordinate in the activity space
-     * @param extraLong default response value for the "simple event view" and all day indication.
-     *        Use Attendees.ATTENDEE_STATUS_NONE for no response.
-     * @param selectedMillis The time to specify as selected
-     * @param title The title of the event
-     * @param calendarId The id of the calendar which the event belongs to
-     */
-    public void sendEventRelatedEventWithExtraWithTitleWithCalendarId(Object sender, long eventType,
-          long eventId, long startMillis, long endMillis, int x, int y, long extraLong,
-          long selectedMillis, String title, long calendarId) {
-        EventInfo info = new EventInfo();
-        info.eventType = eventType;
-        if (eventType == EventType.VIEW_EVENT_DETAILS) {
-            info.viewType = ViewType.CURRENT;
-        }
-
-        info.id = eventId;
-        info.startTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone));
-        info.startTime.set(startMillis);
-        if (selectedMillis != -1) {
-            info.selectedTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone));
-            info.selectedTime.set(selectedMillis);
-        } else {
-            info.selectedTime = info.startTime;
-        }
-        info.endTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone));
-        info.endTime.set(endMillis);
-        info.x = x;
-        info.y = y;
-        info.extraLong = extraLong;
-        info.eventTitle = title;
-        info.calendarId = calendarId;
-        this.sendEvent(sender, info);
-    }
-    /**
-     * Helper for sending non-calendar-event events
-     *
-     * @param sender object of the caller
-     * @param eventType one of {@link EventType}
-     * @param start start time
-     * @param end end time
-     * @param eventId event id
-     * @param viewType {@link ViewType}
-     */
-    public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
-            int viewType) {
-        sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null,
-                null);
-    }
-
-    /**
-     * sendEvent() variant with extraLong, search query, and search component name.
-     */
-    public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
-            int viewType, long extraLong, String query, ComponentName componentName) {
-        sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query,
-                componentName);
-    }
-
-    public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected,
-            long eventId, int viewType, long extraLong, String query, ComponentName componentName) {
-        EventInfo info = new EventInfo();
-        info.eventType = eventType;
-        info.startTime = start;
-        info.selectedTime = selected;
-        info.endTime = end;
-        info.id = eventId;
-        info.viewType = viewType;
-        info.query = query;
-        info.componentName = componentName;
-        info.extraLong = extraLong;
-        this.sendEvent(sender, info);
-    }
-
-    public void sendEvent(Object sender, final EventInfo event) {
-        // TODO Throw exception on invalid events
-
-        if (DEBUG) {
-            Log.d(TAG, eventInfoToString(event));
-        }
-
-        Long filteredTypes = filters.get(sender);
-        if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) {
-            // Suppress event per filter
-            if (DEBUG) {
-                Log.d(TAG, "Event suppressed");
-            }
-            return;
-        }
-
-        mPreviousViewType = mViewType;
-
-        // Fix up view if not specified
-        if (event.viewType == ViewType.DETAIL) {
-            event.viewType = mDetailViewType;
-            mViewType = mDetailViewType;
-        } else if (event.viewType == ViewType.CURRENT) {
-            event.viewType = mViewType;
-        } else if (event.viewType != ViewType.EDIT) {
-            mViewType = event.viewType;
-
-            if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY
-                    || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) {
-                mDetailViewType = mViewType;
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, "vvvvvvvvvvvvvvv");
-            Log.d(TAG, "Start  " + (event.startTime == null ? "null" : event.startTime.toString()));
-            Log.d(TAG, "End    " + (event.endTime == null ? "null" : event.endTime.toString()));
-            Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
-            Log.d(TAG, "mTime  " + (mTime == null ? "null" : mTime.toString()));
-        }
-
-        long startMillis = 0;
-        if (event.startTime != null) {
-            startMillis = event.startTime.toMillis(false);
-        }
-
-        // Set mTime if selectedTime is set
-        if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) {
-            mTime.set(event.selectedTime);
-        } else {
-            if (startMillis != 0) {
-                // selectedTime is not set so set mTime to startTime iff it is not
-                // within start and end times
-                long mtimeMillis = mTime.toMillis(false);
-                if (mtimeMillis < startMillis
-                        || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) {
-                    mTime.set(event.startTime);
-                }
-            }
-            event.selectedTime = mTime;
-        }
-        // Store the formatting flags if this is an update to the title
-        if (event.eventType == EventType.UPDATE_TITLE) {
-            mDateFlags = event.extraLong;
-        }
-
-        // Fix up start time if not specified
-        if (startMillis == 0) {
-            event.startTime = mTime;
-        }
-        if (DEBUG) {
-            Log.d(TAG, "Start  " + (event.startTime == null ? "null" : event.startTime.toString()));
-            Log.d(TAG, "End    " + (event.endTime == null ? "null" : event.endTime.toString()));
-            Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
-            Log.d(TAG, "mTime  " + (mTime == null ? "null" : mTime.toString()));
-            Log.d(TAG, "^^^^^^^^^^^^^^^");
-        }
-
-        // Store the eventId if we're entering edit event
-        if ((event.eventType
-                & (EventType.VIEW_EVENT_DETAILS))
-                != 0) {
-            if (event.id > 0) {
-                mEventId = event.id;
-            } else {
-                mEventId = -1;
-            }
-        }
-
-        boolean handled = false;
-        synchronized (this) {
-            mDispatchInProgressCounter ++;
-
-            if (DEBUG) {
-                Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers");
-            }
-            // Dispatch to event handler(s)
-            if (mFirstEventHandler != null) {
-                // Handle the 'first' one before handling the others
-                EventHandler handler = mFirstEventHandler.second;
-                if (handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0
-                        && !mToBeRemovedEventHandlers.contains(mFirstEventHandler.first)) {
-                    handler.handleEvent(event);
-                    handled = true;
-                }
-            }
-            for (Iterator<Entry<Integer, EventHandler>> handlers =
-                    eventHandlers.entrySet().iterator(); handlers.hasNext();) {
-                Entry<Integer, EventHandler> entry = handlers.next();
-                int key = entry.getKey();
-                if (mFirstEventHandler != null && key == mFirstEventHandler.first) {
-                    // If this was the 'first' handler it was already handled
-                    continue;
-                }
-                EventHandler eventHandler = entry.getValue();
-                if (eventHandler != null
-                        && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) {
-                    if (mToBeRemovedEventHandlers.contains(key)) {
-                        continue;
-                    }
-                    eventHandler.handleEvent(event);
-                    handled = true;
-                }
-            }
-
-            mDispatchInProgressCounter --;
-
-            if (mDispatchInProgressCounter == 0) {
-
-                // Deregister removed handlers
-                if (mToBeRemovedEventHandlers.size() > 0) {
-                    for (Integer zombie : mToBeRemovedEventHandlers) {
-                        eventHandlers.remove(zombie);
-                        if (mFirstEventHandler != null && zombie.equals(mFirstEventHandler.first)) {
-                            mFirstEventHandler = null;
-                        }
-                    }
-                    mToBeRemovedEventHandlers.clear();
-                }
-                // Add new handlers
-                if (mToBeAddedFirstEventHandler != null) {
-                    mFirstEventHandler = mToBeAddedFirstEventHandler;
-                    mToBeAddedFirstEventHandler = null;
-                }
-                if (mToBeAddedEventHandlers.size() > 0) {
-                    for (Entry<Integer, EventHandler> food : mToBeAddedEventHandlers.entrySet()) {
-                        eventHandlers.put(food.getKey(), food.getValue());
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Adds or updates an event handler. This uses a LinkedHashMap so that we can
-     * replace fragments based on the view id they are being expanded into.
-     *
-     * @param key The view id or placeholder for this handler
-     * @param eventHandler Typically a fragment or activity in the calendar app
-     */
-    public void registerEventHandler(int key, EventHandler eventHandler) {
-        synchronized (this) {
-            if (mDispatchInProgressCounter > 0) {
-                mToBeAddedEventHandlers.put(key, eventHandler);
-            } else {
-                eventHandlers.put(key, eventHandler);
-            }
-        }
-    }
-
-    public void registerFirstEventHandler(int key, EventHandler eventHandler) {
-        synchronized (this) {
-            registerEventHandler(key, eventHandler);
-            if (mDispatchInProgressCounter > 0) {
-                mToBeAddedFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
-            } else {
-                mFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
-            }
-        }
-    }
-
-    public void deregisterEventHandler(Integer key) {
-        synchronized (this) {
-            if (mDispatchInProgressCounter > 0) {
-                // To avoid ConcurrencyException, stash away the event handler for now.
-                mToBeRemovedEventHandlers.add(key);
-            } else {
-                eventHandlers.remove(key);
-                if (mFirstEventHandler != null && mFirstEventHandler.first == key) {
-                    mFirstEventHandler = null;
-                }
-            }
-        }
-    }
-
-    public void deregisterAllEventHandlers() {
-        synchronized (this) {
-            if (mDispatchInProgressCounter > 0) {
-                // To avoid ConcurrencyException, stash away the event handler for now.
-                mToBeRemovedEventHandlers.addAll(eventHandlers.keySet());
-            } else {
-                eventHandlers.clear();
-                mFirstEventHandler = null;
-            }
-        }
-    }
-
-    // FRAG_TODO doesn't work yet
-    public void filterBroadcasts(Object sender, long eventTypes) {
-        filters.put(sender, eventTypes);
-    }
-
-    /**
-     * @return the time that this controller is currently pointed at
-     */
-    public long getTime() {
-        return mTime.toMillis(false);
-    }
-
-    /**
-     * @return the last set of date flags sent with
-     *         {@link EventType#UPDATE_TITLE}
-     */
-    public long getDateFlags() {
-        return mDateFlags;
-    }
-
-    /**
-     * Set the time this controller is currently pointed at
-     *
-     * @param millisTime Time since epoch in millis
-     */
-    public void setTime(long millisTime) {
-        mTime.set(millisTime);
-    }
-
-    /**
-     * @return the last event ID the edit view was launched with
-     */
-    public long getEventId() {
-        return mEventId;
-    }
-
-    public int getViewType() {
-        return mViewType;
-    }
-
-    public int getPreviousViewType() {
-        return mPreviousViewType;
-    }
-
-    public void launchViewEvent(long eventId, long startMillis, long endMillis, int response) {
-        Intent intent = new Intent(Intent.ACTION_VIEW);
-        Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
-        intent.setData(eventUri);
-        intent.setClass(mContext, AllInOneActivity.class);
-        intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis);
-        intent.putExtra(EXTRA_EVENT_END_TIME, endMillis);
-        intent.putExtra(ATTENDEE_STATUS, response);
-        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        mContext.startActivity(intent);
-    }
-
-    // Forces the viewType. Should only be used for initialization.
-    public void setViewType(int viewType) {
-        mViewType = viewType;
-    }
-
-    // Sets the eventId. Should only be used for initialization.
-    public void setEventId(long eventId) {
-        mEventId = eventId;
-    }
-
-    private String eventInfoToString(EventInfo eventInfo) {
-        String tmp = "Unknown";
-
-        StringBuilder builder = new StringBuilder();
-        if ((eventInfo.eventType & EventType.GO_TO) != 0) {
-            tmp = "Go to time/event";
-        } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) {
-            tmp = "View event";
-        } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) {
-            tmp = "View details";
-        } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) {
-            tmp = "Refresh events";
-        } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) {
-            tmp = "Gone home";
-        } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) {
-            tmp = "Update title";
-        }
-        builder.append(tmp);
-        builder.append(": id=");
-        builder.append(eventInfo.id);
-        builder.append(", selected=");
-        builder.append(eventInfo.selectedTime);
-        builder.append(", start=");
-        builder.append(eventInfo.startTime);
-        builder.append(", end=");
-        builder.append(eventInfo.endTime);
-        builder.append(", viewType=");
-        builder.append(eventInfo.viewType);
-        builder.append(", x=");
-        builder.append(eventInfo.x);
-        builder.append(", y=");
-        builder.append(eventInfo.y);
-        return builder.toString();
-    }
-}
diff --git a/src/com/android/calendar/CalendarController.kt b/src/com/android/calendar/CalendarController.kt
new file mode 100644
index 0000000..16ee8fd
--- /dev/null
+++ b/src/com/android/calendar/CalendarController.kt
@@ -0,0 +1,743 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME
+import android.provider.CalendarContract.EXTRA_EVENT_END_TIME
+import android.provider.CalendarContract.Attendees.ATTENDEE_STATUS
+import android.content.ComponentName
+import android.content.ContentUris
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.provider.CalendarContract.Attendees
+import android.provider.CalendarContract.Events
+import android.text.format.Time
+import android.util.Log
+import android.util.Pair
+import java.lang.ref.WeakReference
+import java.util.LinkedHashMap
+import java.util.LinkedList
+import java.util.WeakHashMap
+
+class CalendarController private constructor(context: Context?) {
+    private var mContext: Context? = null
+
+    // This uses a LinkedHashMap so that we can replace fragments based on the
+    // view id they are being expanded into since we can't guarantee a reference
+    // to the handler will be findable
+    private val eventHandlers: LinkedHashMap<Int, EventHandler> =
+        LinkedHashMap<Int, EventHandler>(5)
+    private val mToBeRemovedEventHandlers: LinkedList<Int> = LinkedList<Int>()
+    private val mToBeAddedEventHandlers: LinkedHashMap<Int, EventHandler> =
+        LinkedHashMap<Int, EventHandler>()
+    private var mFirstEventHandler: Pair<Int, EventHandler>? = null
+    private var mToBeAddedFirstEventHandler: Pair<Int, EventHandler>? = null
+
+    @Volatile
+    private var mDispatchInProgressCounter = 0
+    private val filters: WeakHashMap<Object, Long> = WeakHashMap<Object, Long>(1)
+
+    // Forces the viewType. Should only be used for initialization.
+    var viewType = -1
+    private var mDetailViewType = -1
+    var previousViewType = -1
+        private set
+
+    // The last event ID the edit view was launched with
+    var eventId: Long = -1
+    private val mTime: Time? = Time()
+
+    // The last set of date flags sent with
+    var dateFlags: Long = 0
+        private set
+    private val mUpdateTimezone: Runnable = object : Runnable {
+        @Override
+        override fun run() {
+            mTime?.switchTimezone(Utils.getTimeZone(mContext, this))
+        }
+    }
+
+    /**
+     * One of the event types that are sent to or from the controller
+     */
+    interface EventType {
+        companion object {
+            // Simple view of an event
+            const val VIEW_EVENT = 1L shl 1
+
+            // Full detail view in read only mode
+            const val VIEW_EVENT_DETAILS = 1L shl 2
+
+            // full detail view in edit mode
+            const val EDIT_EVENT = 1L shl 3
+            const val GO_TO = 1L shl 5
+            const val EVENTS_CHANGED = 1L shl 7
+            const val USER_HOME = 1L shl 9
+
+            // date range has changed, update the title
+            const val UPDATE_TITLE = 1L shl 10
+        }
+    }
+
+    /**
+     * One of the Agenda/Day/Week/Month view types
+     */
+    interface ViewType {
+        companion object {
+            const val DETAIL = -1
+            const val CURRENT = 0
+            const val AGENDA = 1
+            const val DAY = 2
+            const val WEEK = 3
+            const val MONTH = 4
+            const val EDIT = 5
+            const val MAX_VALUE = 5
+        }
+    }
+
+    class EventInfo {
+        @JvmField var eventType: Long = 0 // one of the EventType
+        @JvmField var viewType = 0 // one of the ViewType
+        @JvmField var id: Long = 0 // event id
+        @JvmField var selectedTime: Time? = null // the selected time in focus
+
+        // Event start and end times.  All-day events are represented in:
+        // - local time for GO_TO commands
+        // - UTC time for VIEW_EVENT and other event-related commands
+        @JvmField var startTime: Time? = null
+        @JvmField var endTime: Time? = null
+        @JvmField var x = 0 // x coordinate in the activity space
+        @JvmField var y = 0 // y coordinate in the activity space
+        @JvmField var query: String? = null // query for a user search
+        @JvmField var componentName: ComponentName? = null // used in combination with query
+        @JvmField var eventTitle: String? = null
+        @JvmField var calendarId: Long = 0
+
+        /**
+         * For EventType.VIEW_EVENT:
+         * It is the default attendee response and an all day event indicator.
+         * Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED,
+         * Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE.
+         * To signal the event is an all-day event, "or" ALL_DAY_MASK with the response.
+         * Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay().
+         *
+         *
+         * For EventType.GO_TO:
+         * Set to [.EXTRA_GOTO_TIME] to go to the specified date/time.
+         * Set to [.EXTRA_GOTO_DATE] to consider the date but ignore the time.
+         * Set to [.EXTRA_GOTO_BACK_TO_PREVIOUS] if back should bring back previous view.
+         * Set to [.EXTRA_GOTO_TODAY] if this is a user request to go to the current time.
+         *
+         *
+         * For EventType.UPDATE_TITLE:
+         * Set formatting flags for Utils.formatDateRange
+         */
+        @JvmField var extraLong: Long = 0
+        val isAllDay: Boolean
+            get() {
+                if (eventType != EventType.VIEW_EVENT) {
+                    Log.wtf(TAG, "illegal call to isAllDay , wrong event type $eventType")
+                    return false
+                }
+                return if (extraLong and ALL_DAY_MASK != 0L) true else false
+            }
+        val response: Int
+            get() {
+                if (eventType != EventType.VIEW_EVENT) {
+                    Log.wtf(TAG, "illegal call to getResponse , wrong event type $eventType")
+                    return Attendees.ATTENDEE_STATUS_NONE
+                }
+                val response = (extraLong and ATTENTEE_STATUS_MASK).toInt()
+                when (response) {
+                    ATTENDEE_STATUS_NONE_MASK -> return Attendees.ATTENDEE_STATUS_NONE
+                    ATTENDEE_STATUS_ACCEPTED_MASK -> return Attendees.ATTENDEE_STATUS_ACCEPTED
+                    ATTENDEE_STATUS_DECLINED_MASK -> return Attendees.ATTENDEE_STATUS_DECLINED
+                    ATTENDEE_STATUS_TENTATIVE_MASK -> return Attendees.ATTENDEE_STATUS_TENTATIVE
+                    else -> Log.wtf(TAG, "Unknown attendee response $response")
+                }
+                return ATTENDEE_STATUS_NONE_MASK
+            }
+
+        companion object {
+            private const val ATTENTEE_STATUS_MASK: Long = 0xFF
+            private const val ALL_DAY_MASK: Long = 0x100
+            private const val ATTENDEE_STATUS_NONE_MASK = 0x01
+            private const val ATTENDEE_STATUS_ACCEPTED_MASK = 0x02
+            private const val ATTENDEE_STATUS_DECLINED_MASK = 0x04
+            private const val ATTENDEE_STATUS_TENTATIVE_MASK = 0x08
+
+            // Used to build the extra long for a VIEW event.
+            @JvmStatic fun buildViewExtraLong(response: Int, allDay: Boolean): Long {
+                var extra = if (allDay) ALL_DAY_MASK else 0
+                extra = when (response) {
+                    Attendees.ATTENDEE_STATUS_NONE -> extra or
+                            ATTENDEE_STATUS_NONE_MASK.toLong()
+                    Attendees.ATTENDEE_STATUS_ACCEPTED -> extra or
+                            ATTENDEE_STATUS_ACCEPTED_MASK.toLong()
+                    Attendees.ATTENDEE_STATUS_DECLINED -> extra or
+                            ATTENDEE_STATUS_DECLINED_MASK.toLong()
+                    Attendees.ATTENDEE_STATUS_TENTATIVE -> extra or
+                            ATTENDEE_STATUS_TENTATIVE_MASK.toLong()
+                    else -> {
+                        Log.wtf(
+                                TAG,
+                                "Unknown attendee response $response"
+                        )
+                        extra or ATTENDEE_STATUS_NONE_MASK.toLong()
+                    }
+                }
+                return extra
+            }
+        }
+    }
+
+    interface EventHandler {
+        val supportedEventTypes: Long
+        fun handleEvent(event: EventInfo?)
+
+        /**
+         * This notifies the handler that the database has changed and it should
+         * update its view.
+         */
+        fun eventsChanged()
+    }
+
+    fun sendEventRelatedEvent(
+        sender: Object?,
+        eventType: Long,
+        eventId: Long,
+        startMillis: Long,
+        endMillis: Long,
+        x: Int,
+        y: Int,
+        selectedMillis: Long
+    ) {
+        // TODO: pass the real allDay status or at least a status that says we don't know the
+        // status and have the receiver query the data.
+        // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo
+        // so currently the missing allDay status has no effect.
+        sendEventRelatedEventWithExtra(
+                sender, eventType, eventId, startMillis, endMillis, x, y,
+                EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false),
+                selectedMillis
+        )
+    }
+
+    /**
+     * Helper for sending New/View/Edit/Delete events
+     *
+     * @param sender object of the caller
+     * @param eventType one of [EventType]
+     * @param eventId event id
+     * @param startMillis start time
+     * @param endMillis end time
+     * @param x x coordinate in the activity space
+     * @param y y coordinate in the activity space
+     * @param extraLong default response value for the "simple event view" and all day indication.
+     * Use Attendees.ATTENDEE_STATUS_NONE for no response.
+     * @param selectedMillis The time to specify as selected
+     */
+    fun sendEventRelatedEventWithExtra(
+        sender: Object?,
+        eventType: Long,
+        eventId: Long,
+        startMillis: Long,
+        endMillis: Long,
+        x: Int,
+        y: Int,
+        extraLong: Long,
+        selectedMillis: Long
+    ) {
+        sendEventRelatedEventWithExtraWithTitleWithCalendarId(
+                sender, eventType, eventId,
+                startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1
+        )
+    }
+
+    /**
+     * Helper for sending New/View/Edit/Delete events
+     *
+     * @param sender object of the caller
+     * @param eventType one of [EventType]
+     * @param eventId event id
+     * @param startMillis start time
+     * @param endMillis end time
+     * @param x x coordinate in the activity space
+     * @param y y coordinate in the activity space
+     * @param extraLong default response value for the "simple event view" and all day indication.
+     * Use Attendees.ATTENDEE_STATUS_NONE for no response.
+     * @param selectedMillis The time to specify as selected
+     * @param title The title of the event
+     * @param calendarId The id of the calendar which the event belongs to
+     */
+    fun sendEventRelatedEventWithExtraWithTitleWithCalendarId(
+        sender: Object?,
+        eventType: Long,
+        eventId: Long,
+        startMillis: Long,
+        endMillis: Long,
+        x: Int,
+        y: Int,
+        extraLong: Long,
+        selectedMillis: Long,
+        title: String?,
+        calendarId: Long
+    ) {
+        val info = EventInfo()
+        info.eventType = eventType
+        if (eventType == EventType.VIEW_EVENT_DETAILS) {
+            info.viewType = ViewType.CURRENT
+        }
+        info.id = eventId
+        info.startTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone))
+        (info.startTime as Time).set(startMillis)
+        if (selectedMillis != -1L) {
+            info.selectedTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone))
+            (info.selectedTime as Time).set(selectedMillis)
+        } else {
+            info.selectedTime = info.startTime
+        }
+        info.endTime = Time(Utils.getTimeZone(mContext, mUpdateTimezone))
+        (info.endTime as Time).set(endMillis)
+        info.x = x
+        info.y = y
+        info.extraLong = extraLong
+        info.eventTitle = title
+        info.calendarId = calendarId
+        this.sendEvent(sender, info)
+    }
+
+    /**
+     * Helper for sending non-calendar-event events
+     *
+     * @param sender object of the caller
+     * @param eventType one of [EventType]
+     * @param start start time
+     * @param end end time
+     * @param eventId event id
+     * @param viewType [ViewType]
+     */
+    fun sendEvent(
+        sender: Object?,
+        eventType: Long,
+        start: Time?,
+        end: Time?,
+        eventId: Long,
+        viewType: Int
+    ) {
+        sendEvent(
+                sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null,
+                null
+        )
+    }
+
+    /**
+     * sendEvent() variant with extraLong, search query, and search component name.
+     */
+    fun sendEvent(
+        sender: Object?,
+        eventType: Long,
+        start: Time?,
+        end: Time?,
+        eventId: Long,
+        viewType: Int,
+        extraLong: Long,
+        query: String?,
+        componentName: ComponentName?
+    ) {
+        sendEvent(
+                sender, eventType, start, end, start, eventId, viewType, extraLong, query,
+                componentName
+        )
+    }
+
+    fun sendEvent(
+        sender: Object?,
+        eventType: Long,
+        start: Time?,
+        end: Time?,
+        selected: Time?,
+        eventId: Long,
+        viewType: Int,
+        extraLong: Long,
+        query: String?,
+        componentName: ComponentName?
+    ) {
+        val info = EventInfo()
+        info.eventType = eventType
+        info.startTime = start
+        info.selectedTime = selected
+        info.endTime = end
+        info.id = eventId
+        info.viewType = viewType
+        info.query = query
+        info.componentName = componentName
+        info.extraLong = extraLong
+        this.sendEvent(sender, info)
+    }
+
+    fun sendEvent(sender: Object?, event: EventInfo) {
+        // TODO Throw exception on invalid events
+        if (DEBUG) {
+            Log.d(TAG, eventInfoToString(event))
+        }
+        val filteredTypes: Long? = filters.get(sender)
+        if (filteredTypes != null && filteredTypes.toLong() and event.eventType != 0L) {
+            // Suppress event per filter
+            if (DEBUG) {
+                Log.d(TAG, "Event suppressed")
+            }
+            return
+        }
+        previousViewType = viewType
+
+        // Fix up view if not specified
+        if (event.viewType == ViewType.DETAIL) {
+            event.viewType = mDetailViewType
+            viewType = mDetailViewType
+        } else if (event.viewType == ViewType.CURRENT) {
+            event.viewType = viewType
+        } else if (event.viewType != ViewType.EDIT) {
+            viewType = event.viewType
+            if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY ||
+                    Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK) {
+                mDetailViewType = viewType
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "vvvvvvvvvvvvvvv")
+            Log.d(
+                    TAG,
+                    "Start  " + if (event.startTime == null) "null" else event.startTime.toString()
+            )
+            Log.d(TAG, "End    " + if (event.endTime == null) "null" else event.endTime.toString())
+            Log.d(
+                    TAG,
+                    "Select " + if (event.selectedTime == null) "null"
+                    else event.selectedTime.toString()
+            )
+            Log.d(TAG, "mTime  " + if (mTime == null) "null" else mTime.toString())
+        }
+        var startMillis: Long = 0
+        val temp = event.startTime
+        if (temp != null) {
+            startMillis = (event.startTime as Time).toMillis(false)
+        }
+
+        // Set mTime if selectedTime is set
+        val temp1 = event.selectedTime
+        if (temp1 != null && temp1?.toMillis(false) != 0L) {
+            mTime?.set(event.selectedTime)
+        } else {
+            if (startMillis != 0L) {
+                // selectedTime is not set so set mTime to startTime iff it is not
+                // within start and end times
+                val mtimeMillis: Long = mTime?.toMillis(false) as Long
+                val temp2 = event.endTime
+                if (mtimeMillis < startMillis ||
+                        temp2 != null && mtimeMillis > temp2.toMillis(false)) {
+                    mTime?.set(event.startTime)
+                }
+            }
+            event.selectedTime = mTime
+        }
+        // Store the formatting flags if this is an update to the title
+        if (event.eventType == EventType.UPDATE_TITLE) {
+            dateFlags = event.extraLong
+        }
+
+        // Fix up start time if not specified
+        if (startMillis == 0L) {
+            event.startTime = mTime
+        }
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "Start  " + if (event.startTime == null) "null" else
+                        event.startTime.toString()
+            )
+            Log.d(TAG, "End    " + if (event.endTime == null) "null" else
+                event.endTime.toString())
+            Log.d(
+                    TAG,
+                    "Select " + if (event.selectedTime == null) "null" else
+                        event.selectedTime.toString()
+            )
+            Log.d(TAG, "mTime  " + if (mTime == null) "null" else mTime.toString())
+            Log.d(TAG, "^^^^^^^^^^^^^^^")
+        }
+
+        // Store the eventId if we're entering edit event
+        if ((event.eventType and EventType.VIEW_EVENT_DETAILS) != 0L) {
+            if (event.id > 0) {
+                eventId = event.id
+            } else {
+                eventId = -1
+            }
+        }
+        var handled = false
+        synchronized(this) {
+            mDispatchInProgressCounter++
+            if (DEBUG) {
+                Log.d(
+                        TAG,
+                        "sendEvent: Dispatching to " + eventHandlers.size.toString() + " handlers"
+                )
+            }
+            // Dispatch to event handler(s)
+            val temp3 = mFirstEventHandler
+            if (temp3 != null) {
+                // Handle the 'first' one before handling the others
+                val handler: EventHandler? = mFirstEventHandler?.second
+                if (handler != null && handler.supportedEventTypes and event.eventType != 0L &&
+                        !mToBeRemovedEventHandlers.contains(mFirstEventHandler?.first)) {
+                    handler.handleEvent(event)
+                    handled = true
+                }
+            }
+            val handlers: MutableIterator<MutableMap.MutableEntry<Int,
+                CalendarController.EventHandler>> = eventHandlers.entries.iterator()
+            while (handlers.hasNext()) {
+                val entry: MutableMap.MutableEntry<Int,
+                    CalendarController.EventHandler> = handlers.next()
+                val key: Int = entry.key.toInt()
+                val temp4 = mFirstEventHandler
+                if (temp4 != null && key.toInt() == temp4.first.toInt()) {
+                    // If this was the 'first' handler it was already handled
+                    continue
+                }
+                val eventHandler: EventHandler = entry.value
+                if (eventHandler != null &&
+                    eventHandler.supportedEventTypes and event.eventType != 0L) {
+                    if (mToBeRemovedEventHandlers.contains(key)) {
+                        continue
+                    }
+                    eventHandler.handleEvent(event)
+                    handled = true
+                }
+            }
+            mDispatchInProgressCounter--
+            if (mDispatchInProgressCounter == 0) {
+
+                // Deregister removed handlers
+                if (mToBeRemovedEventHandlers.size > 0) {
+                    for (zombie in mToBeRemovedEventHandlers) {
+                        eventHandlers.remove(zombie)
+                        val temp5 = mFirstEventHandler
+                        if (temp5 != null && zombie.equals(temp5.first)) {
+                            mFirstEventHandler = null
+                        }
+                    }
+                    mToBeRemovedEventHandlers.clear()
+                }
+                // Add new handlers
+                if (mToBeAddedFirstEventHandler != null) {
+                    mFirstEventHandler = mToBeAddedFirstEventHandler
+                    mToBeAddedFirstEventHandler = null
+                }
+                if (mToBeAddedEventHandlers.size > 0) {
+                    for (food in mToBeAddedEventHandlers.entries) {
+                        eventHandlers.put(food.key, food.value)
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds or updates an event handler. This uses a LinkedHashMap so that we can
+     * replace fragments based on the view id they are being expanded into.
+     *
+     * @param key The view id or placeholder for this handler
+     * @param eventHandler Typically a fragment or activity in the calendar app
+     */
+    fun registerEventHandler(key: Int, eventHandler: EventHandler?) {
+        synchronized(this) {
+            if (mDispatchInProgressCounter > 0) {
+                mToBeAddedEventHandlers.put(key,
+                        eventHandler as CalendarController.EventHandler)
+            } else {
+                eventHandlers.put(key, eventHandler as CalendarController.EventHandler)
+            }
+        }
+    }
+
+    fun registerFirstEventHandler(key: Int, eventHandler: EventHandler?) {
+        synchronized(this) {
+            registerEventHandler(key, eventHandler)
+            if (mDispatchInProgressCounter > 0) {
+                mToBeAddedFirstEventHandler = Pair<Int, EventHandler>(key, eventHandler)
+            } else {
+                mFirstEventHandler = Pair<Int, EventHandler>(key, eventHandler)
+            }
+        }
+    }
+
+    fun deregisterEventHandler(key: Int) {
+        synchronized(this) {
+            if (mDispatchInProgressCounter > 0) {
+                // To avoid ConcurrencyException, stash away the event handler for now.
+                mToBeRemovedEventHandlers.add(key)
+            } else {
+                eventHandlers.remove(key)
+                val temp6 = mFirstEventHandler
+                if (temp6 != null && temp6.first == key) {
+                    mFirstEventHandler = null
+                } else {}
+            }
+        }
+    }
+
+    fun deregisterAllEventHandlers() {
+        synchronized(this) {
+            if (mDispatchInProgressCounter > 0) {
+                // To avoid ConcurrencyException, stash away the event handler for now.
+                mToBeRemovedEventHandlers.addAll(eventHandlers.keys)
+            } else {
+                eventHandlers.clear()
+                mFirstEventHandler = null
+            }
+        }
+    }
+
+    // FRAG_TODO doesn't work yet
+    fun filterBroadcasts(sender: Object?, eventTypes: Long) {
+        filters.put(sender, eventTypes)
+    }
+    /**
+     * @return the time that this controller is currently pointed at
+     */
+    /**
+     * Set the time this controller is currently pointed at
+     *
+     * @param millisTime Time since epoch in millis
+     */
+    var time: Long?
+        get() = mTime?.toMillis(false)
+        set(millisTime) {
+            mTime?.set(millisTime as Long)
+        }
+
+    fun launchViewEvent(eventId: Long, startMillis: Long, endMillis: Long, response: Int) {
+        val intent = Intent(Intent.ACTION_VIEW)
+        val eventUri: Uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId)
+        intent.setData(eventUri)
+        intent.setClass(mContext as Context, AllInOneActivity::class.java)
+        intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis)
+        intent.putExtra(EXTRA_EVENT_END_TIME, endMillis)
+        intent.putExtra(ATTENDEE_STATUS, response)
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+        mContext?.startActivity(intent)
+    }
+
+    private fun eventInfoToString(eventInfo: EventInfo): String {
+        var tmp = "Unknown"
+        val builder = StringBuilder()
+        if (eventInfo.eventType and EventType.GO_TO != 0L) {
+            tmp = "Go to time/event"
+        } else if (eventInfo.eventType and EventType.VIEW_EVENT != 0L) {
+            tmp = "View event"
+        } else if (eventInfo.eventType and EventType.VIEW_EVENT_DETAILS != 0L) {
+            tmp = "View details"
+        } else if (eventInfo.eventType and EventType.EVENTS_CHANGED != 0L) {
+            tmp = "Refresh events"
+        } else if (eventInfo.eventType and EventType.USER_HOME != 0L) {
+            tmp = "Gone home"
+        } else if (eventInfo.eventType and EventType.UPDATE_TITLE != 0L) {
+            tmp = "Update title"
+        }
+        builder.append(tmp)
+        builder.append(": id=")
+        builder.append(eventInfo.id)
+        builder.append(", selected=")
+        builder.append(eventInfo.selectedTime)
+        builder.append(", start=")
+        builder.append(eventInfo.startTime)
+        builder.append(", end=")
+        builder.append(eventInfo.endTime)
+        builder.append(", viewType=")
+        builder.append(eventInfo.viewType)
+        builder.append(", x=")
+        builder.append(eventInfo.x)
+        builder.append(", y=")
+        builder.append(eventInfo.y)
+        return builder.toString()
+    }
+
+    companion object {
+        private const val DEBUG = false
+        private const val TAG = "CalendarController"
+        const val EVENT_EDIT_ON_LAUNCH = "editMode"
+        const val MIN_CALENDAR_YEAR = 1970
+        const val MAX_CALENDAR_YEAR = 2036
+        const val MIN_CALENDAR_WEEK = 0
+        const val MAX_CALENDAR_WEEK = 3497 // weeks between 1/1/1970 and 1/1/2037
+        private val instances: WeakHashMap<Context, WeakReference<CalendarController>> =
+                WeakHashMap<Context, WeakReference<CalendarController>>()
+
+        /**
+         * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time
+         * can be ignored
+         */
+        const val EXTRA_GOTO_DATE: Long = 1
+        const val EXTRA_GOTO_TIME: Long = 2
+        const val EXTRA_GOTO_BACK_TO_PREVIOUS: Long = 4
+        const val EXTRA_GOTO_TODAY: Long = 8
+
+        /**
+         * Creates and/or returns an instance of CalendarController associated with
+         * the supplied context. It is best to pass in the current Activity.
+         *
+         * @param context The activity if at all possible.
+         */
+        @JvmStatic fun getInstance(context: Context?): CalendarController? {
+            synchronized(instances) {
+                var controller: CalendarController? = null
+                val weakController: WeakReference<CalendarController>? = instances.get(context)
+                if (weakController != null) {
+                    controller = weakController.get()
+                }
+                if (controller == null) {
+                    controller = CalendarController(context)
+                    instances.put(context, WeakReference(controller))
+                }
+                return controller
+            }
+        }
+
+        /**
+         * Removes an instance when it is no longer needed. This should be called in
+         * an activity's onDestroy method.
+         *
+         * @param context The activity used to create the controller
+         */
+        @JvmStatic fun removeInstance(context: Context?) {
+            instances.remove(context)
+        }
+    }
+
+    init {
+        mContext = context
+        mUpdateTimezone.run()
+        mTime?.setToNow()
+        mDetailViewType = Utils.getSharedPreference(
+                mContext,
+                GeneralPreferences.KEY_DETAILED_VIEW,
+                GeneralPreferences.DEFAULT_DETAILED_VIEW
+        )
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/CalendarData.java b/src/com/android/calendar/CalendarData.java
deleted file mode 100644
index 5c8456f..0000000
--- a/src/com/android/calendar/CalendarData.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-public final class CalendarData {
-    static final String[] s12HoursNoAmPm = { "12", "1", "2", "3", "4",
-        "5", "6", "7", "8", "9", "10", "11", "12",
-        "1", "2", "3", "4", "5", "6", "7", "8",
-        "9", "10", "11", "12" };
-
-    static final String[] s24Hours = { "00", "01", "02", "03", "04", "05",
-        "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16",
-        "17", "18", "19", "20", "21", "22", "23", "00" };
-}
diff --git a/src/com/android/calendar/CalendarData.kt b/src/com/android/calendar/CalendarData.kt
new file mode 100644
index 0000000..7370f2e
--- /dev/null
+++ b/src/com/android/calendar/CalendarData.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+object CalendarData {
+    @JvmField
+    val s12HoursNoAmPm = arrayOf("12", "1", "2", "3", "4",
+            "5", "6", "7", "8", "9", "10", "11", "12",
+            "1", "2", "3", "4", "5", "6", "7", "8",
+            "9", "10", "11", "12")
+
+    @JvmField
+    val s24Hours = arrayOf("00", "01", "02", "03", "04", "05",
+            "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16",
+            "17", "18", "19", "20", "21", "22", "23", "00")
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/CalendarUtils.java b/src/com/android/calendar/CalendarUtils.java
deleted file mode 100644
index 0238c32..0000000
--- a/src/com/android/calendar/CalendarUtils.java
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.os.Looper;
-import android.provider.CalendarContract.CalendarCache;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.util.Log;
-
-import java.util.Formatter;
-import java.util.HashSet;
-import java.util.Locale;
-
-/**
- * A class containing utility methods related to Calendar apps.
- *
- * This class is expected to move into the app framework eventually.
- */
-public class CalendarUtils {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "CalendarUtils";
-
-    /**
-     * This class contains methods specific to reading and writing time zone
-     * values.
-     */
-    public static class TimeZoneUtils {
-        private static final String[] TIMEZONE_TYPE_ARGS = { CalendarCache.KEY_TIMEZONE_TYPE };
-        private static final String[] TIMEZONE_INSTANCES_ARGS =
-                { CalendarCache.KEY_TIMEZONE_INSTANCES };
-        public static final String[] CALENDAR_CACHE_POJECTION = {
-                CalendarCache.KEY, CalendarCache.VALUE
-        };
-
-        private static StringBuilder mSB = new StringBuilder(50);
-        private static Formatter mF = new Formatter(mSB, Locale.getDefault());
-        private volatile static boolean mFirstTZRequest = true;
-        private volatile static boolean mTZQueryInProgress = false;
-
-        private volatile static boolean mUseHomeTZ = false;
-        private volatile static String mHomeTZ = Time.getCurrentTimezone();
-
-        private static HashSet<Runnable> mTZCallbacks = new HashSet<Runnable>();
-        private static int mToken = 1;
-        private static AsyncTZHandler mHandler;
-
-        // The name of the shared preferences file. This name must be maintained for historical
-        // reasons, as it's what PreferenceManager assigned the first time the file was created.
-        private final String mPrefsName;
-
-        /**
-         * This is the key used for writing whether or not a home time zone should
-         * be used in the Calendar app to the Calendar Preferences.
-         */
-        public static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled";
-        /**
-         * This is the key used for writing the time zone that should be used if
-         * home time zones are enabled for the Calendar app.
-         */
-        public static final String KEY_HOME_TZ = "preferences_home_tz";
-
-        /**
-         * This is a helper class for handling the async queries and updates for the
-         * time zone settings in Calendar.
-         */
-        private class AsyncTZHandler extends AsyncQueryHandler {
-            public AsyncTZHandler(ContentResolver cr) {
-                super(cr);
-            }
-
-            @Override
-            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-                synchronized (mTZCallbacks) {
-                    if (cursor == null) {
-                        mTZQueryInProgress = false;
-                        mFirstTZRequest = true;
-                        return;
-                    }
-
-                    boolean writePrefs = false;
-                    // Check the values in the db
-                    int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY);
-                    int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE);
-                    while(cursor.moveToNext()) {
-                        String key = cursor.getString(keyColumn);
-                        String value = cursor.getString(valueColumn);
-                        if (TextUtils.equals(key, CalendarCache.KEY_TIMEZONE_TYPE)) {
-                            boolean useHomeTZ = !TextUtils.equals(
-                                    value, CalendarCache.TIMEZONE_TYPE_AUTO);
-                            if (useHomeTZ != mUseHomeTZ) {
-                                writePrefs = true;
-                                mUseHomeTZ = useHomeTZ;
-                            }
-                        } else if (TextUtils.equals(
-                                key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) {
-                            if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) {
-                                writePrefs = true;
-                                mHomeTZ = value;
-                            }
-                        }
-                    }
-                    cursor.close();
-                    if (writePrefs) {
-                        SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName);
-                        // Write the prefs
-                        setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ);
-                        setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ);
-                    }
-
-                    mTZQueryInProgress = false;
-                    for (Runnable callback : mTZCallbacks) {
-                        if (callback != null) {
-                            callback.run();
-                        }
-                    }
-                    mTZCallbacks.clear();
-                }
-            }
-        }
-
-        /**
-         * The name of the file where the shared prefs for Calendar are stored
-         * must be provided. All activities within an app should provide the
-         * same preferences name or behavior may become erratic.
-         *
-         * @param prefsName
-         */
-        public TimeZoneUtils(String prefsName) {
-            mPrefsName = prefsName;
-        }
-
-        /**
-         * Formats a date or a time range according to the local conventions.
-         *
-         * This formats a date/time range using Calendar's time zone and the
-         * local conventions for the region of the device.
-         *
-         * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in
-         * the UTC time zone instead.
-         *
-         * @param context the context is required only if the time is shown
-         * @param startMillis the start time in UTC milliseconds
-         * @param endMillis the end time in UTC milliseconds
-         * @param flags a bit mask of options See
-         * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
-         * @return a string containing the formatted date/time range.
-         */
-        public String formatDateRange(Context context, long startMillis,
-                long endMillis, int flags) {
-            String date;
-            String tz;
-            if ((flags & DateUtils.FORMAT_UTC) != 0) {
-                tz = Time.TIMEZONE_UTC;
-            } else {
-                tz = getTimeZone(context, null);
-            }
-            synchronized (mSB) {
-                mSB.setLength(0);
-                date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags,
-                        tz).toString();
-            }
-            return date;
-        }
-
-        /**
-         * Writes a new home time zone to the db.
-         *
-         * Updates the home time zone in the db asynchronously and updates
-         * the local cache. Sending a time zone of
-         * {@link CalendarCache#TIMEZONE_TYPE_AUTO} will cause it to be set
-         * to the device's time zone. null or empty tz will be ignored.
-         *
-         * @param context The calling activity
-         * @param timeZone The time zone to set Calendar to, or
-         * {@link CalendarCache#TIMEZONE_TYPE_AUTO}
-         */
-        public void setTimeZone(Context context, String timeZone) {
-            if (TextUtils.isEmpty(timeZone)) {
-                if (DEBUG) {
-                    Log.d(TAG, "Empty time zone, nothing to be done.");
-                }
-                return;
-            }
-            boolean updatePrefs = false;
-            synchronized (mTZCallbacks) {
-                if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timeZone)) {
-                    if (mUseHomeTZ) {
-                        updatePrefs = true;
-                    }
-                    mUseHomeTZ = false;
-                } else {
-                    if (!mUseHomeTZ || !TextUtils.equals(mHomeTZ, timeZone)) {
-                        updatePrefs = true;
-                    }
-                    mUseHomeTZ = true;
-                    mHomeTZ = timeZone;
-                }
-            }
-            if (updatePrefs) {
-                // Write the prefs
-                SharedPreferences prefs = getSharedPreferences(context, mPrefsName);
-                setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ);
-                setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ);
-
-                // Update the db
-                ContentValues values = new ContentValues();
-                if (mHandler != null) {
-                    mHandler.cancelOperation(mToken);
-                }
-
-                mHandler = new AsyncTZHandler(context.getContentResolver());
-
-                // skip 0 so query can use it
-                if (++mToken == 0) {
-                    mToken = 1;
-                }
-
-                // Write the use home tz setting
-                values.put(CalendarCache.VALUE, mUseHomeTZ ? CalendarCache.TIMEZONE_TYPE_HOME
-                        : CalendarCache.TIMEZONE_TYPE_AUTO);
-                mHandler.startUpdate(mToken, null, CalendarCache.URI, values, "key=?",
-                        TIMEZONE_TYPE_ARGS);
-
-                // If using a home tz write it to the db
-                if (mUseHomeTZ) {
-                    ContentValues values2 = new ContentValues();
-                    values2.put(CalendarCache.VALUE, mHomeTZ);
-                    mHandler.startUpdate(mToken, null, CalendarCache.URI, values2,
-                            "key=?", TIMEZONE_INSTANCES_ARGS);
-                }
-            }
-        }
-
-        /**
-         * Gets the time zone that Calendar should be displayed in
-         *
-         * This is a helper method to get the appropriate time zone for Calendar. If this
-         * is the first time this method has been called it will initiate an asynchronous
-         * query to verify that the data in preferences is correct. The callback supplied
-         * will only be called if this query returns a value other than what is stored in
-         * preferences and should cause the calling activity to refresh anything that
-         * depends on calling this method.
-         *
-         * @param context The calling activity
-         * @param callback The runnable that should execute if a query returns new values
-         * @return The string value representing the time zone Calendar should display
-         */
-        public String getTimeZone(Context context, Runnable callback) {
-            synchronized (mTZCallbacks){
-                if (mFirstTZRequest) {
-                    SharedPreferences prefs = getSharedPreferences(context, mPrefsName);
-                    mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false);
-                    mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone());
-
-                    // Only check content resolver if we have a looper to attach to use
-                    if (Looper.myLooper() != null) {
-                        mTZQueryInProgress = true;
-                        mFirstTZRequest = false;
-
-                        // When the async query returns it should synchronize on
-                        // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the
-                        // preferences, set mTZQueryInProgress to false, and call all
-                        // the runnables in mTZCallbacks.
-                        if (mHandler == null) {
-                            mHandler = new AsyncTZHandler(context.getContentResolver());
-                        }
-                        mHandler.startQuery(0, context, CalendarCache.URI, CALENDAR_CACHE_POJECTION,
-                                null, null, null);
-                    }
-                }
-                if (mTZQueryInProgress) {
-                    mTZCallbacks.add(callback);
-                }
-            }
-            return mUseHomeTZ ? mHomeTZ : Time.getCurrentTimezone();
-        }
-
-        /**
-         * Forces a query of the database to check for changes to the time zone.
-         * This should be called if another app may have modified the db. If a
-         * query is already in progress the callback will be added to the list
-         * of callbacks to be called when it returns.
-         *
-         * @param context The calling activity
-         * @param callback The runnable that should execute if a query returns
-         *            new values
-         */
-        public void forceDBRequery(Context context, Runnable callback) {
-            synchronized (mTZCallbacks){
-                if (mTZQueryInProgress) {
-                    mTZCallbacks.add(callback);
-                    return;
-                }
-                mFirstTZRequest = true;
-                getTimeZone(context, callback);
-            }
-        }
-    }
-
-        /**
-         * A helper method for writing a String value to the preferences
-         * asynchronously.
-         *
-         * @param context A context with access to the correct preferences
-         * @param key The preference to write to
-         * @param value The value to write
-         */
-        public static void setSharedPreference(SharedPreferences prefs, String key, String value) {
-//            SharedPreferences prefs = getSharedPreferences(context);
-            SharedPreferences.Editor editor = prefs.edit();
-            editor.putString(key, value);
-            editor.apply();
-        }
-
-        /**
-         * A helper method for writing a boolean value to the preferences
-         * asynchronously.
-         *
-         * @param context A context with access to the correct preferences
-         * @param key The preference to write to
-         * @param value The value to write
-         */
-        public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) {
-//            SharedPreferences prefs = getSharedPreferences(context, prefsName);
-            SharedPreferences.Editor editor = prefs.edit();
-            editor.putBoolean(key, value);
-            editor.apply();
-        }
-
-        /** Return a properly configured SharedPreferences instance */
-        public static SharedPreferences getSharedPreferences(Context context, String prefsName) {
-            return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE);
-        }
-}
diff --git a/src/com/android/calendar/CalendarUtils.kt b/src/com/android/calendar/CalendarUtils.kt
new file mode 100644
index 0000000..94ca723
--- /dev/null
+++ b/src/com/android/calendar/CalendarUtils.kt
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import android.content.AsyncQueryHandler
+import android.content.ContentResolver
+import android.content.ContentValues
+import android.content.Context
+import android.content.SharedPreferences
+import android.database.Cursor
+import android.os.Looper
+import android.provider.CalendarContract.CalendarCache
+import android.text.TextUtils
+import android.text.format.DateUtils
+import android.text.format.Time
+import android.util.Log
+
+import java.util.Formatter
+import java.util.HashSet
+import java.util.Locale
+
+/**
+ * A class containing utility methods related to Calendar apps.
+ *
+ * This class is expected to move into the app framework eventually.
+ */
+class CalendarUtils {
+
+    companion object {
+        private const val DEBUG = false
+        private const val TAG = "CalendarUtils"
+
+        /**
+         * A helper method for writing a boolean value to the preferences
+         * asynchronously.
+         *
+         * @param context A context with access to the correct preferences
+         * @param key The preference to write to
+         * @param value The value to write
+         */
+        @JvmStatic
+        fun setSharedPreference(prefs: SharedPreferences, key: String?, value: Boolean) {
+            val editor: SharedPreferences.Editor = prefs.edit()
+            editor.putBoolean(key, value)
+            editor.apply()
+        }
+
+        /** Return a properly configured SharedPreferences instance  */
+        @JvmStatic
+        fun getSharedPreferences(context: Context, prefsName: String?): SharedPreferences {
+            return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE)
+        }
+
+        /**
+         * A helper method for writing a String value to the preferences
+         * asynchronously.
+         *
+         * @param context A context with access to the correct preferences
+         * @param key The preference to write to
+         * @param value The value to write
+         */
+        @JvmStatic
+        fun setSharedPreference(prefs: SharedPreferences, key: String?, value: String?) {
+            val editor: SharedPreferences.Editor = prefs.edit()
+            editor.putString(key, value)
+            editor.apply()
+        }
+    }
+
+    /**
+     * This class contains methods specific to reading and writing time zone
+     * values.
+     */
+    class TimeZoneUtils
+    /**
+     * The name of the file where the shared prefs for Calendar are stored
+     * must be provided. All activities within an app should provide the
+     * same preferences name or behavior may become erratic.
+     *
+     * @param prefsName
+     */(  // The name of the shared preferences file. This name must be maintained for historical
+            // reasons, as it's what PreferenceManager assigned the first time the file was created.
+            private val mPrefsName: String) {
+        /**
+         * This is a helper class for handling the async queries and updates for the
+         * time zone settings in Calendar.
+         */
+        private inner class AsyncTZHandler(cr: ContentResolver?) : AsyncQueryHandler(cr) {
+            protected override fun onQueryComplete(token: Int, cookie: Any?, cursor: Cursor?) {
+                synchronized(mTZCallbacks) {
+                    if (cursor == null) {
+                        mTZQueryInProgress = false
+                        mFirstTZRequest = true
+                        return
+                    }
+                    var writePrefs = false
+                    // Check the values in the db
+                    val keyColumn: Int = cursor.getColumnIndexOrThrow(CalendarCache.KEY)
+                    val valueColumn: Int = cursor.getColumnIndexOrThrow(CalendarCache.VALUE)
+                    while (cursor.moveToNext()) {
+                        val key: String = cursor.getString(keyColumn)
+                        val value: String = cursor.getString(valueColumn)
+                        if (TextUtils.equals(key, CalendarCache.KEY_TIMEZONE_TYPE)) {
+                            val useHomeTZ: Boolean = !TextUtils.equals(
+                                    value, CalendarCache.TIMEZONE_TYPE_AUTO)
+                            if (useHomeTZ != mUseHomeTZ) {
+                                writePrefs = true
+                                mUseHomeTZ = useHomeTZ
+                            }
+                        } else if (TextUtils.equals(
+                                        key, CalendarCache.KEY_TIMEZONE_INSTANCES_PREVIOUS)) {
+                            if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) {
+                                writePrefs = true
+                                mHomeTZ = value
+                            }
+                        }
+                    }
+                    cursor.close()
+                    if (writePrefs) {
+                        val prefs: SharedPreferences =
+                        getSharedPreferences(cookie as Context, mPrefsName)
+                        // Write the prefs
+                        setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ)
+                        setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ)
+                    }
+                    mTZQueryInProgress = false
+                    for (callback in mTZCallbacks) {
+                        if (callback != null) {
+                            callback.run()
+                        }
+                    }
+                    mTZCallbacks.clear()
+                }
+            }
+        }
+
+        /**
+         * Formats a date or a time range according to the local conventions.
+         *
+         * This formats a date/time range using Calendar's time zone and the
+         * local conventions for the region of the device.
+         *
+         * If the [DateUtils.FORMAT_UTC] flag is used it will pass in
+         * the UTC time zone instead.
+         *
+         * @param context the context is required only if the time is shown
+         * @param startMillis the start time in UTC milliseconds
+         * @param endMillis the end time in UTC milliseconds
+         * @param flags a bit mask of options See
+         * [formatDateRange][DateUtils.formatDateRange]
+         * @return a string containing the formatted date/time range.
+         */
+        fun formatDateRange(context: Context, startMillis: Long,
+                            endMillis: Long, flags: Int): String {
+            var date: String
+            val tz: String
+            tz = if (flags and DateUtils.FORMAT_UTC !== 0) {
+                Time.TIMEZONE_UTC
+            } else {
+                getTimeZone(context, null)
+            }
+            synchronized(mSB) {
+                mSB.setLength(0)
+                date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags,
+                        tz).toString()
+            }
+            return date
+        }
+
+        /**
+         * Writes a new home time zone to the db.
+         *
+         * Updates the home time zone in the db asynchronously and updates
+         * the local cache. Sending a time zone of
+         * [CalendarCache.TIMEZONE_TYPE_AUTO] will cause it to be set
+         * to the device's time zone. null or empty tz will be ignored.
+         *
+         * @param context The calling activity
+         * @param timeZone The time zone to set Calendar to, or
+         * [CalendarCache.TIMEZONE_TYPE_AUTO]
+         */
+        fun setTimeZone(context: Context, timeZone: String) {
+            if (TextUtils.isEmpty(timeZone)) {
+                if (DEBUG) {
+                    Log.d(TAG, "Empty time zone, nothing to be done.")
+                }
+                return
+            }
+            var updatePrefs = false
+            synchronized(mTZCallbacks) {
+                if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timeZone)) {
+                    if (mUseHomeTZ) {
+                        updatePrefs = true
+                    }
+                    mUseHomeTZ = false
+                } else {
+                    if (!mUseHomeTZ || !TextUtils.equals(mHomeTZ, timeZone)) {
+                        updatePrefs = true
+                    }
+                    mUseHomeTZ = true
+                    mHomeTZ = timeZone
+                }
+            }
+            if (updatePrefs) {
+                // Write the prefs
+                val prefs: SharedPreferences = getSharedPreferences(context, mPrefsName)
+                setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ)
+                setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ)
+
+                // Update the db
+                val values = ContentValues()
+                if (mHandler != null) {
+                    mHandler?.cancelOperation(mToken)
+                }
+                mHandler = AsyncTZHandler(context.getContentResolver())
+
+                // skip 0 so query can use it
+                if (++mToken == 0) {
+                    mToken = 1
+                }
+
+                // Write the use home tz setting
+                values.put(CalendarCache.VALUE, if (mUseHomeTZ) CalendarCache.TIMEZONE_TYPE_HOME
+                           else CalendarCache.TIMEZONE_TYPE_AUTO)
+                mHandler?.startUpdate(mToken, null, CalendarCache.URI, values, "key=?",
+                        TIMEZONE_TYPE_ARGS)
+
+                // If using a home tz write it to the db
+                if (mUseHomeTZ) {
+                    val values2 = ContentValues()
+                    values2.put(CalendarCache.VALUE, mHomeTZ)
+                    mHandler?.startUpdate(mToken, null, CalendarCache.URI, values2,
+                            "key=?", TIMEZONE_INSTANCES_ARGS)
+                }
+            }
+        }
+
+        /**
+         * Gets the time zone that Calendar should be displayed in
+         *
+         * This is a helper method to get the appropriate time zone for Calendar. If this
+         * is the first time this method has been called it will initiate an asynchronous
+         * query to verify that the data in preferences is correct. The callback supplied
+         * will only be called if this query returns a value other than what is stored in
+         * preferences and should cause the calling activity to refresh anything that
+         * depends on calling this method.
+         *
+         * @param context The calling activity
+         * @param callback The runnable that should execute if a query returns new values
+         * @return The string value representing the time zone Calendar should display
+         */
+        fun getTimeZone(context: Context, callback: Runnable?): String {
+            synchronized(mTZCallbacks) {
+                if (mFirstTZRequest) {
+                    val prefs: SharedPreferences = getSharedPreferences(context, mPrefsName)
+                    mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false)
+                    mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()) ?: String()
+
+                    // Only check content resolver if we have a looper to attach to use
+                    if (Looper.myLooper() != null) {
+                        mTZQueryInProgress = true
+                        mFirstTZRequest = false
+
+                        // When the async query returns it should synchronize on
+                        // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the
+                        // preferences, set mTZQueryInProgress to false, and call all
+                        // the runnables in mTZCallbacks.
+                        if (mHandler == null) {
+                            mHandler = AsyncTZHandler(context.getContentResolver())
+                        }
+                        mHandler?.startQuery(0, context, CalendarCache.URI,
+                                             CALENDAR_CACHE_POJECTION, null, null, null)
+                    }
+                }
+                if (mTZQueryInProgress && callback != null) {
+                    mTZCallbacks.add(callback)
+                }
+            }
+            return if (mUseHomeTZ) mHomeTZ else Time.getCurrentTimezone()
+        }
+
+        /**
+         * Forces a query of the database to check for changes to the time zone.
+         * This should be called if another app may have modified the db. If a
+         * query is already in progress the callback will be added to the list
+         * of callbacks to be called when it returns.
+         *
+         * @param context The calling activity
+         * @param callback The runnable that should execute if a query returns
+         * new values
+         */
+        fun forceDBRequery(context: Context, callback: Runnable) {
+            synchronized(mTZCallbacks) {
+                if (mTZQueryInProgress) {
+                    mTZCallbacks.add(callback)
+                    return
+                }
+                mFirstTZRequest = true
+                getTimeZone(context, callback)
+            }
+        }
+
+        companion object {
+            private val TIMEZONE_TYPE_ARGS = arrayOf<String>(CalendarCache.KEY_TIMEZONE_TYPE)
+            private val TIMEZONE_INSTANCES_ARGS =
+            arrayOf<String>(CalendarCache.KEY_TIMEZONE_INSTANCES)
+            val CALENDAR_CACHE_POJECTION = arrayOf<String>(
+                    CalendarCache.KEY, CalendarCache.VALUE
+            )
+            private val mSB: StringBuilder = StringBuilder(50)
+            private val mF: Formatter = Formatter(mSB, Locale.getDefault())
+
+            @Volatile
+            private var mFirstTZRequest = true
+
+            @Volatile
+            private var mTZQueryInProgress = false
+
+            @Volatile
+            private var mUseHomeTZ = false
+
+            @Volatile
+            private var mHomeTZ: String = Time.getCurrentTimezone()
+            private val mTZCallbacks: HashSet<Runnable> = HashSet<Runnable>()
+            private var mToken = 1
+            private var mHandler: AsyncTZHandler? = null
+
+            /**
+             * This is the key used for writing whether or not a home time zone should
+             * be used in the Calendar app to the Calendar Preferences.
+             */
+            const val KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"
+
+            /**
+             * This is the key used for writing the time zone that should be used if
+             * home time zones are enabled for the Calendar app.
+             */
+            const val KEY_HOME_TZ = "preferences_home_tz"
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/CalendarViewAdapter.java b/src/com/android/calendar/CalendarViewAdapter.java
deleted file mode 100644
index 524268f..0000000
--- a/src/com/android/calendar/CalendarViewAdapter.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import com.android.calendar.CalendarController.ViewType;
-
-import android.content.Context;
-import android.os.Handler;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import java.util.Formatter;
-import java.util.Locale;
-
-
-/*
- * The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu
- * for small screen layouts. The pull down menu replaces the tabs uses for big screen layouts
- *
- * The MenuSpinnerAdapter responsible for creating the views used for in the pull down menu.
- */
-
-public class CalendarViewAdapter extends BaseAdapter {
-
-    private static final String TAG = "MenuSpinnerAdapter";
-
-    private final String mButtonNames [];           // Text on buttons
-
-    // Used to define the look of the menu button according to the current view:
-    // Day view: show day of the week + full date underneath
-    // Week view: show the month + year
-    // Month view: show the month + year
-    // Agenda view: show day of the week + full date underneath
-    private int mCurrentMainView;
-
-    private final LayoutInflater mInflater;
-
-    // Defines the types of view returned by this spinner
-    private static final int BUTTON_VIEW_TYPE = 0;
-    static final int VIEW_TYPE_NUM = 1;  // Increase this if you add more view types
-
-    public static final int DAY_BUTTON_INDEX = 0;
-    public static final int WEEK_BUTTON_INDEX = 1;
-    public static final int MONTH_BUTTON_INDEX = 2;
-    public static final int AGENDA_BUTTON_INDEX = 3;
-
-    // The current selected event's time, used to calculate the date and day of the week
-    // for the buttons.
-    private long mMilliTime;
-    private String mTimeZone;
-    private long mTodayJulianDay;
-
-    private final Context mContext;
-    private final Formatter mFormatter;
-    private final StringBuilder mStringBuilder;
-    private Handler mMidnightHandler = null; // Used to run a time update every midnight
-    private final boolean mShowDate;   // Spinner mode indicator (view name or view name with date)
-
-    // Updates time specific variables (time-zone, today's Julian day).
-    private final Runnable mTimeUpdater = new Runnable() {
-        @Override
-        public void run() {
-            refresh(mContext);
-        }
-    };
-
-    public CalendarViewAdapter(Context context, int viewType, boolean showDate) {
-        super();
-
-        mMidnightHandler = new Handler();
-        mCurrentMainView = viewType;
-        mContext = context;
-        mShowDate = showDate;
-
-        // Initialize
-        mButtonNames = context.getResources().getStringArray(R.array.buttons_list);
-        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mStringBuilder = new StringBuilder(50);
-        mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
-
-        // Sets time specific variables and starts a thread for midnight updates
-        if (showDate) {
-            refresh(context);
-        }
-    }
-
-
-    // Sets the time zone and today's Julian day to be used by the adapter.
-    // Also, notify listener on the change and resets the midnight update thread.
-    public void refresh(Context context) {
-        mTimeZone = Utils.getTimeZone(context, mTimeUpdater);
-        Time time = new Time(mTimeZone);
-        long now = System.currentTimeMillis();
-        time.set(now);
-        mTodayJulianDay = Time.getJulianDay(now, time.gmtoff);
-        notifyDataSetChanged();
-        setMidnightHandler();
-    }
-
-    // Sets a thread to run 1 second after midnight and update the current date
-    // This is used to display correctly the date of yesterday/today/tomorrow
-    private void setMidnightHandler() {
-        mMidnightHandler.removeCallbacks(mTimeUpdater);
-        // Set the time updater to run at 1 second after midnight
-        long now = System.currentTimeMillis();
-        Time time = new Time(mTimeZone);
-        time.set(now);
-        long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 -
-                time.second + 1) * 1000;
-        mMidnightHandler.postDelayed(mTimeUpdater, runInMillis);
-    }
-
-    // Stops the midnight update thread, called by the activity when it is paused.
-    public void onPause() {
-        mMidnightHandler.removeCallbacks(mTimeUpdater);
-    }
-
-    // Returns the amount of buttons in the menu
-    @Override
-    public int getCount() {
-        return mButtonNames.length;
-    }
-
-
-    @Override
-    public Object getItem(int position) {
-        if (position < mButtonNames.length) {
-            return mButtonNames[position];
-        }
-        return null;
-    }
-
-    @Override
-    public long getItemId(int position) {
-        // Item ID is its location in the list
-        return position;
-    }
-
-    @Override
-    public boolean hasStableIds() {
-        return false;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-
-        View v;
-
-        if (mShowDate) {
-            // Check if can recycle the view
-            if (convertView == null || ((Integer) convertView.getTag()).intValue()
-                    != R.layout.actionbar_pulldown_menu_top_button) {
-                v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false);
-                // Set the tag to make sure you can recycle it when you get it
-                // as a convert view
-                v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button));
-            } else {
-                v = convertView;
-            }
-            TextView weekDay = (TextView) v.findViewById(R.id.top_button_weekday);
-            TextView date = (TextView) v.findViewById(R.id.top_button_date);
-
-            switch (mCurrentMainView) {
-                case ViewType.DAY:
-                    weekDay.setVisibility(View.VISIBLE);
-                    weekDay.setText(buildDayOfWeek());
-                    date.setText(buildFullDate());
-                    break;
-                case ViewType.WEEK:
-                    if (Utils.getShowWeekNumber(mContext)) {
-                        weekDay.setVisibility(View.VISIBLE);
-                        weekDay.setText(buildWeekNum());
-                    } else {
-                        weekDay.setVisibility(View.GONE);
-                    }
-                    date.setText(buildMonthYearDate());
-                    break;
-                case ViewType.MONTH:
-                    weekDay.setVisibility(View.GONE);
-                    date.setText(buildMonthYearDate());
-                    break;
-                default:
-                    v = null;
-                    break;
-            }
-        } else {
-            if (convertView == null || ((Integer) convertView.getTag()).intValue()
-                    != R.layout.actionbar_pulldown_menu_top_button_no_date) {
-                v = mInflater.inflate(
-                        R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false);
-                // Set the tag to make sure you can recycle it when you get it
-                // as a convert view
-                v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button_no_date));
-            } else {
-                v = convertView;
-            }
-            TextView title = (TextView) v;
-            switch (mCurrentMainView) {
-                case ViewType.DAY:
-                    title.setText(mButtonNames [DAY_BUTTON_INDEX]);
-                    break;
-                case ViewType.WEEK:
-                    title.setText(mButtonNames [WEEK_BUTTON_INDEX]);
-                    break;
-                case ViewType.MONTH:
-                    title.setText(mButtonNames [MONTH_BUTTON_INDEX]);
-                    break;
-                default:
-                    v = null;
-                    break;
-            }
-        }
-        return v;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        // Only one kind of view is used
-        return BUTTON_VIEW_TYPE;
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return VIEW_TYPE_NUM;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return (mButtonNames.length == 0);
-    }
-
-    @Override
-    public View getDropDownView(int position, View convertView, ViewGroup parent) {
-        View v = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false);
-        TextView viewType = (TextView)v.findViewById(R.id.button_view);
-        TextView date = (TextView)v.findViewById(R.id.button_date);
-        switch (position) {
-            case DAY_BUTTON_INDEX:
-                viewType.setText(mButtonNames [DAY_BUTTON_INDEX]);
-                if (mShowDate) {
-                    date.setText(buildMonthDayDate());
-                }
-                break;
-            case WEEK_BUTTON_INDEX:
-                viewType.setText(mButtonNames [WEEK_BUTTON_INDEX]);
-                if (mShowDate) {
-                    date.setText(buildWeekDate());
-                }
-                break;
-            case MONTH_BUTTON_INDEX:
-                viewType.setText(mButtonNames [MONTH_BUTTON_INDEX]);
-                if (mShowDate) {
-                    date.setText(buildMonthDate());
-                }
-                break;
-            default:
-                v = convertView;
-                break;
-        }
-        return v;
-    }
-
-    // Updates the current viewType
-    // Used to match the label on the menu button with the calendar view
-    public void setMainView(int viewType) {
-        mCurrentMainView = viewType;
-        notifyDataSetChanged();
-    }
-
-    // Update the date that is displayed on buttons
-    // Used when the user selects a new day/week/month to watch
-    public void setTime(long time) {
-        mMilliTime = time;
-        notifyDataSetChanged();
-    }
-
-    // Builds a string with the day of the week and the word yesterday/today/tomorrow
-    // before it if applicable.
-    private String buildDayOfWeek() {
-
-        Time t = new Time(mTimeZone);
-        t.set(mMilliTime);
-        long julianDay = Time.getJulianDay(mMilliTime,t.gmtoff);
-        String dayOfWeek = null;
-        mStringBuilder.setLength(0);
-
-        if (julianDay == mTodayJulianDay) {
-            dayOfWeek = mContext.getString(R.string.agenda_today,
-                    DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
-                            DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
-        } else if (julianDay == mTodayJulianDay - 1) {
-            dayOfWeek = mContext.getString(R.string.agenda_yesterday,
-                    DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
-                            DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
-        } else if (julianDay == mTodayJulianDay + 1) {
-            dayOfWeek = mContext.getString(R.string.agenda_tomorrow,
-                    DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
-                            DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
-        } else {
-            dayOfWeek = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
-                    DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString();
-        }
-        return dayOfWeek.toUpperCase();
-    }
-
-    // Builds strings with different formats:
-    // Full date: Month,day Year
-    // Month year
-    // Month day
-    // Month
-    // Week:  month day-day or month day - month day
-    private String buildFullDate() {
-        mStringBuilder.setLength(0);
-        String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
-                DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString();
-        return date;
-    }
-
-    private String buildMonthYearDate() {
-        mStringBuilder.setLength(0);
-        String date = DateUtils.formatDateRange(
-                mContext,
-                mFormatter,
-                mMilliTime,
-                mMilliTime,
-                DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
-                        | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString();
-        return date;
-    }
-
-    private String buildMonthDayDate() {
-        mStringBuilder.setLength(0);
-        String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
-                DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR, mTimeZone).toString();
-        return date;
-    }
-
-    private String buildMonthDate() {
-        mStringBuilder.setLength(0);
-        String date = DateUtils.formatDateRange(
-                mContext,
-                mFormatter,
-                mMilliTime,
-                mMilliTime,
-                DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR
-                        | DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString();
-        return date;
-    }
-
-    private String buildWeekDate() {
-        // Calculate the start of the week, taking into account the "first day of the week"
-        // setting.
-
-        Time t = new Time(mTimeZone);
-        t.set(mMilliTime);
-        int firstDayOfWeek = Utils.getFirstDayOfWeek(mContext);
-        int dayOfWeek = t.weekDay;
-        int diff = dayOfWeek - firstDayOfWeek;
-        if (diff != 0) {
-            if (diff < 0) {
-                diff += 7;
-            }
-            t.monthDay -= diff;
-            t.normalize(true /* ignore isDst */);
-        }
-
-        long weekStartTime = t.toMillis(true);
-        // The end of the week is 6 days after the start of the week
-        long weekEndTime = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS;
-
-        // If week start and end is in 2 different months, use short months names
-        Time t1 = new Time(mTimeZone);
-        t.set(weekEndTime);
-        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
-        if (t.month != t1.month) {
-            flags |= DateUtils.FORMAT_ABBREV_MONTH;
-        }
-
-        mStringBuilder.setLength(0);
-        String date = DateUtils.formatDateRange(mContext, mFormatter, weekStartTime,
-                weekEndTime, flags, mTimeZone).toString();
-         return date;
-    }
-
-    private String buildWeekNum() {
-        int week = Utils.getWeekNumberFromTime(mMilliTime, mContext);
-        return mContext.getResources().getQuantityString(R.plurals.weekN, week, week);
-    }
-
-}
diff --git a/src/com/android/calendar/CalendarViewAdapter.kt b/src/com/android/calendar/CalendarViewAdapter.kt
new file mode 100644
index 0000000..2fe1027
--- /dev/null
+++ b/src/com/android/calendar/CalendarViewAdapter.kt
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import com.android.calendar.CalendarController.ViewType
+import android.content.Context
+import android.os.Handler
+import android.text.format.DateUtils
+import android.text.format.Time
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+import android.widget.TextView
+import java.util.Formatter
+import java.util.Locale
+
+/*
+ * The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu
+ * for small screen layouts. The pull down menu replaces the tabs uses for big screen layouts
+ *
+ * The MenuSpinnerAdapter responsible for creating the views used for in the pull down menu.
+ */
+class CalendarViewAdapter(context: Context, viewType: Int, showDate: Boolean) : BaseAdapter() {
+    private val mButtonNames: Array<String> // Text on buttons
+
+    // Used to define the look of the menu button according to the current view:
+    // Day view: show day of the week + full date underneath
+    // Week view: show the month + year
+    // Month view: show the month + year
+    // Agenda view: show day of the week + full date underneath
+    private var mCurrentMainView: Int
+    private val mInflater: LayoutInflater
+
+    // The current selected event's time, used to calculate the date and day of the week
+    // for the buttons.
+    private var mMilliTime: Long = 0
+    private var mTimeZone: String? = null
+    private var mTodayJulianDay: Long = 0
+    private val mContext: Context = context
+    private val mFormatter: Formatter
+    private val mStringBuilder: StringBuilder
+    private var mMidnightHandler: Handler? = null // Used to run a time update every midnight
+    private val mShowDate: Boolean // Spinner mode indicator (view name or view name with date)
+
+    // Updates time specific variables (time-zone, today's Julian day).
+    private val mTimeUpdater: Runnable = object : Runnable {
+        @Override
+        override fun run() {
+            refresh(mContext)
+        }
+    }
+
+    // Sets the time zone and today's Julian day to be used by the adapter.
+    // Also, notify listener on the change and resets the midnight update thread.
+    fun refresh(context: Context?) {
+        mTimeZone = Utils.getTimeZone(context, mTimeUpdater)
+        val time = Time(mTimeZone)
+        val now: Long = System.currentTimeMillis()
+        time.set(now)
+        mTodayJulianDay = Time.getJulianDay(now, time.gmtoff).toLong()
+        notifyDataSetChanged()
+        setMidnightHandler()
+    }
+
+    // Sets a thread to run 1 second after midnight and update the current date
+    // This is used to display correctly the date of yesterday/today/tomorrow
+    private fun setMidnightHandler() {
+        mMidnightHandler?.removeCallbacks(mTimeUpdater)
+        // Set the time updater to run at 1 second after midnight
+        val now: Long = System.currentTimeMillis()
+        val time = Time(mTimeZone)
+        time.set(now)
+        val runInMillis: Long = ((24 * 3600 - time.hour * 3600 - time.minute * 60 -
+                time.second + 1) * 1000).toLong()
+        mMidnightHandler?.postDelayed(mTimeUpdater, runInMillis)
+    }
+
+    // Stops the midnight update thread, called by the activity when it is paused.
+    fun onPause() {
+        mMidnightHandler?.removeCallbacks(mTimeUpdater)
+    }
+
+    // Returns the amount of buttons in the menu
+    @Override
+    override fun getCount(): Int {
+        return mButtonNames.size
+    }
+
+    @Override
+    override fun getItem(position: Int): Any? {
+        return if (position < mButtonNames.size) {
+            mButtonNames[position]
+        } else null
+    }
+
+    @Override
+    override fun getItemId(position: Int): Long {
+        // Item ID is its location in the list
+        return position.toLong()
+    }
+
+    @Override
+    override fun hasStableIds(): Boolean {
+        return false
+    }
+
+    @Override
+    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
+        var v: View?
+        if (mShowDate) {
+            // Check if can recycle the view
+            if (convertView == null || (convertView.getTag() as Int)
+                    != R.layout.actionbar_pulldown_menu_top_button as Int) {
+                v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false)
+                // Set the tag to make sure you can recycle it when you get it
+                // as a convert view
+                v.setTag(Integer(R.layout.actionbar_pulldown_menu_top_button))
+            } else {
+                v = convertView
+            }
+            val weekDay: TextView = v?.findViewById(R.id.top_button_weekday) as TextView
+            val date: TextView = v?.findViewById(R.id.top_button_date) as TextView
+            when (mCurrentMainView) {
+                ViewType.DAY -> {
+                    weekDay.setVisibility(View.VISIBLE)
+                    weekDay.setText(buildDayOfWeek())
+                    date.setText(buildFullDate())
+                }
+                ViewType.WEEK -> {
+                    if (Utils.getShowWeekNumber(mContext)) {
+                        weekDay.setVisibility(View.VISIBLE)
+                        weekDay.setText(buildWeekNum())
+                    } else {
+                        weekDay.setVisibility(View.GONE)
+                    }
+                    date.setText(buildMonthYearDate())
+                }
+                ViewType.MONTH -> {
+                    weekDay.setVisibility(View.GONE)
+                    date.setText(buildMonthYearDate())
+                }
+                else -> v = null
+            }
+        } else {
+            if (convertView == null || (convertView.getTag() as Int)
+                    != R.layout.actionbar_pulldown_menu_top_button_no_date as Int) {
+                v = mInflater.inflate(
+                        R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false)
+                // Set the tag to make sure you can recycle it when you get it
+                // as a convert view
+                v.setTag(Integer(R.layout.actionbar_pulldown_menu_top_button_no_date))
+            } else {
+                v = convertView
+            }
+            val title: TextView? = v as TextView?
+            when (mCurrentMainView) {
+                ViewType.DAY -> title?.setText(mButtonNames[DAY_BUTTON_INDEX])
+                ViewType.WEEK -> title?.setText(mButtonNames[WEEK_BUTTON_INDEX])
+                ViewType.MONTH -> title?.setText(mButtonNames[MONTH_BUTTON_INDEX])
+                else -> v = null
+            }
+        }
+        return v
+    }
+
+    @Override
+    override fun getItemViewType(position: Int): Int {
+        // Only one kind of view is used
+        return BUTTON_VIEW_TYPE
+    }
+
+    @Override
+    override fun getViewTypeCount(): Int {
+        return VIEW_TYPE_NUM
+    }
+
+    @Override
+    override fun isEmpty(): Boolean {
+        return mButtonNames.size == 0
+    }
+
+    @Override
+    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup?): View? {
+        var v: View? = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false)
+        val viewType: TextView? = v?.findViewById(R.id.button_view) as? TextView
+        val date: TextView? = v?.findViewById(R.id.button_date) as? TextView
+        when (position) {
+            DAY_BUTTON_INDEX -> {
+                viewType?.setText(mButtonNames[DAY_BUTTON_INDEX])
+                if (mShowDate) {
+                    date?.setText(buildMonthDayDate())
+                }
+            }
+            WEEK_BUTTON_INDEX -> {
+                viewType?.setText(mButtonNames[WEEK_BUTTON_INDEX])
+                if (mShowDate) {
+                    date?.setText(buildWeekDate())
+                }
+            }
+            MONTH_BUTTON_INDEX -> {
+                viewType?.setText(mButtonNames[MONTH_BUTTON_INDEX])
+                if (mShowDate) {
+                    date?.setText(buildMonthDate())
+                }
+            }
+            else -> v = convertView
+        }
+        return v
+    }
+
+    // Updates the current viewType
+    // Used to match the label on the menu button with the calendar view
+    fun setMainView(viewType: Int) {
+        mCurrentMainView = viewType
+        notifyDataSetChanged()
+    }
+
+    // Update the date that is displayed on buttons
+    // Used when the user selects a new day/week/month to watch
+    fun setTime(time: Long) {
+        mMilliTime = time
+        notifyDataSetChanged()
+    }
+
+    // Builds a string with the day of the week and the word yesterday/today/tomorrow
+    // before it if applicable.
+    private fun buildDayOfWeek(): String {
+        val t = Time(mTimeZone)
+        t.set(mMilliTime)
+        val julianDay: Long = Time.getJulianDay(mMilliTime, t.gmtoff).toLong()
+        var dayOfWeek: String? = null
+        mStringBuilder.setLength(0)
+        dayOfWeek = if (julianDay == mTodayJulianDay) {
+            mContext.getString(R.string.agenda_today,
+                    DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
+                            DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString())
+        } else if (julianDay == mTodayJulianDay - 1) {
+            mContext.getString(R.string.agenda_yesterday,
+                    DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
+                            DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString())
+        } else if (julianDay == mTodayJulianDay + 1) {
+            mContext.getString(R.string.agenda_tomorrow,
+                    DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
+                            DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString())
+        } else {
+            DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
+                    DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString()
+        }
+        return dayOfWeek.toUpperCase()
+    }
+
+    // Builds strings with different formats:
+    // Full date: Month,day Year
+    // Month year
+    // Month day
+    // Month
+    // Week:  month day-day or month day - month day
+    private fun buildFullDate(): String {
+        mStringBuilder.setLength(0)
+        return DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
+                DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString()
+    }
+
+    private fun buildMonthYearDate(): String {
+        mStringBuilder.setLength(0)
+        return DateUtils.formatDateRange(
+                mContext,
+                mFormatter,
+                mMilliTime,
+                mMilliTime,
+                DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_MONTH_DAY
+                        or DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString()
+    }
+
+    private fun buildMonthDayDate(): String {
+        mStringBuilder.setLength(0)
+        return DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
+                DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR, mTimeZone).toString()
+    }
+
+    private fun buildMonthDate(): String {
+        mStringBuilder.setLength(0)
+        return DateUtils.formatDateRange(
+                mContext,
+                mFormatter,
+                mMilliTime,
+                mMilliTime,
+                DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR
+                        or DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString()
+    }
+
+    private fun buildWeekDate(): String {
+        // Calculate the start of the week, taking into account the "first day of the week"
+        // setting.
+        val t = Time(mTimeZone)
+        t.set(mMilliTime)
+        val firstDayOfWeek: Int = Utils.getFirstDayOfWeek(mContext)
+        val dayOfWeek: Int = t.weekDay
+        var diff = dayOfWeek - firstDayOfWeek
+        if (diff != 0) {
+            if (diff < 0) {
+                diff += 7
+            }
+            t.monthDay -= diff
+            t.normalize(true /* ignore isDst */)
+        }
+        val weekStartTime: Long = t.toMillis(true)
+        // The end of the week is 6 days after the start of the week
+        val weekEndTime: Long = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS
+
+        // If week start and end is in 2 different months, use short months names
+        val t1 = Time(mTimeZone)
+        t.set(weekEndTime)
+        var flags: Int = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_NO_YEAR
+        if (t.month !== t1.month) {
+            flags = flags or DateUtils.FORMAT_ABBREV_MONTH
+        }
+        mStringBuilder.setLength(0)
+        return DateUtils.formatDateRange(mContext, mFormatter, weekStartTime,
+                weekEndTime, flags, mTimeZone).toString()
+    }
+
+    private fun buildWeekNum(): String {
+        val week: Int = Utils.getWeekNumberFromTime(mMilliTime, mContext)
+        return mContext.getResources().getQuantityString(R.plurals.weekN, week, week)
+    }
+
+    companion object {
+        private const val TAG = "MenuSpinnerAdapter"
+
+        // Defines the types of view returned by this spinner
+        private const val BUTTON_VIEW_TYPE = 0
+        const val VIEW_TYPE_NUM = 1 // Increase this if you add more view types
+        const val DAY_BUTTON_INDEX = 0
+        const val WEEK_BUTTON_INDEX = 1
+        const val MONTH_BUTTON_INDEX = 2
+        const val AGENDA_BUTTON_INDEX = 3
+    }
+
+    init {
+        mMidnightHandler = Handler()
+        mCurrentMainView = viewType
+        mShowDate = showDate
+
+        // Initialize
+        mButtonNames = context.getResources().getStringArray(R.array.buttons_list)
+        mInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        mStringBuilder = StringBuilder(50)
+        mFormatter = Formatter(mStringBuilder, Locale.getDefault())
+
+        // Sets time specific variables and starts a thread for midnight updates
+        if (showDate) {
+            refresh(context)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/DayFragment.java b/src/com/android/calendar/DayFragment.java
deleted file mode 100644
index a9fb39e..0000000
--- a/src/com/android/calendar/DayFragment.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import com.android.calendar.CalendarController.EventInfo;
-import com.android.calendar.CalendarController.EventType;
-
-import android.app.Fragment;
-import android.content.Context;
-import android.os.Bundle;
-import android.text.format.Time;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.ProgressBar;
-import android.widget.ViewSwitcher;
-import android.widget.ViewSwitcher.ViewFactory;
-
-/**
- * This is the base class for Day and Week Activities.
- */
-public class DayFragment extends Fragment implements CalendarController.EventHandler, ViewFactory {
-    /**
-     * The view id used for all the views we create. It's OK to have all child
-     * views have the same ID. This ID is used to pick which view receives
-     * focus when a view hierarchy is saved / restore
-     */
-    private static final int VIEW_ID = 1;
-
-    protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
-
-    protected ProgressBar mProgressBar;
-    protected ViewSwitcher mViewSwitcher;
-    protected Animation mInAnimationForward;
-    protected Animation mOutAnimationForward;
-    protected Animation mInAnimationBackward;
-    protected Animation mOutAnimationBackward;
-    EventLoader mEventLoader;
-
-    Time mSelectedDay = new Time();
-
-    private final Runnable mTZUpdater = new Runnable() {
-        @Override
-        public void run() {
-            if (!DayFragment.this.isAdded()) {
-                return;
-            }
-            String tz = Utils.getTimeZone(getActivity(), mTZUpdater);
-            mSelectedDay.timezone = tz;
-            mSelectedDay.normalize(true);
-        }
-    };
-
-    private int mNumDays;
-
-    public DayFragment() {
-        mSelectedDay.setToNow();
-    }
-
-    public DayFragment(long timeMillis, int numOfDays) {
-        mNumDays = numOfDays;
-        if (timeMillis == 0) {
-            mSelectedDay.setToNow();
-        } else {
-            mSelectedDay.set(timeMillis);
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        Context context = getActivity();
-
-        mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in);
-        mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out);
-        mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in);
-        mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out);
-
-        mEventLoader = new EventLoader(context);
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        View v = inflater.inflate(R.layout.day_activity, null);
-
-        mViewSwitcher = (ViewSwitcher) v.findViewById(R.id.switcher);
-        mViewSwitcher.setFactory(this);
-        mViewSwitcher.getCurrentView().requestFocus();
-        ((DayView) mViewSwitcher.getCurrentView()).updateTitle();
-
-        return v;
-    }
-
-    public View makeView() {
-        mTZUpdater.run();
-        DayView view = new DayView(getActivity(), CalendarController
-                .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays);
-        view.setId(VIEW_ID);
-        view.setLayoutParams(new ViewSwitcher.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-        view.setSelected(mSelectedDay, false, false);
-        return view;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mEventLoader.startBackgroundThread();
-        mTZUpdater.run();
-        eventsChanged();
-        DayView view = (DayView) mViewSwitcher.getCurrentView();
-        view.handleOnResume();
-        view.restartCurrentTimeUpdates();
-
-        view = (DayView) mViewSwitcher.getNextView();
-        view.handleOnResume();
-        view.restartCurrentTimeUpdates();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        DayView view = (DayView) mViewSwitcher.getCurrentView();
-        view.cleanup();
-        view = (DayView) mViewSwitcher.getNextView();
-        view.cleanup();
-        mEventLoader.stopBackgroundThread();
-
-        // Stop events cross-fade animation
-        view.stopEventsAnimation();
-        ((DayView) mViewSwitcher.getNextView()).stopEventsAnimation();
-    }
-
-    void startProgressSpinner() {
-        // start the progress spinner
-        mProgressBar.setVisibility(View.VISIBLE);
-    }
-
-    void stopProgressSpinner() {
-        // stop the progress spinner
-        mProgressBar.setVisibility(View.GONE);
-    }
-
-    private void goTo(Time goToTime, boolean ignoreTime, boolean animateToday) {
-        if (mViewSwitcher == null) {
-            // The view hasn't been set yet. Just save the time and use it later.
-            mSelectedDay.set(goToTime);
-            return;
-        }
-
-        DayView currentView = (DayView) mViewSwitcher.getCurrentView();
-
-        // How does goTo time compared to what's already displaying?
-        int diff = currentView.compareToVisibleTimeRange(goToTime);
-
-        if (diff == 0) {
-            // In visible range. No need to switch view
-            currentView.setSelected(goToTime, ignoreTime, animateToday);
-        } else {
-            // Figure out which way to animate
-            if (diff > 0) {
-                mViewSwitcher.setInAnimation(mInAnimationForward);
-                mViewSwitcher.setOutAnimation(mOutAnimationForward);
-            } else {
-                mViewSwitcher.setInAnimation(mInAnimationBackward);
-                mViewSwitcher.setOutAnimation(mOutAnimationBackward);
-            }
-
-            DayView next = (DayView) mViewSwitcher.getNextView();
-            if (ignoreTime) {
-                next.setFirstVisibleHour(currentView.getFirstVisibleHour());
-            }
-
-            next.setSelected(goToTime, ignoreTime, animateToday);
-            next.reloadEvents();
-            mViewSwitcher.showNext();
-            next.requestFocus();
-            next.updateTitle();
-            next.restartCurrentTimeUpdates();
-        }
-    }
-
-    /**
-     * Returns the selected time in milliseconds. The milliseconds are measured
-     * in UTC milliseconds from the epoch and uniquely specifies any selectable
-     * time.
-     *
-     * @return the selected time in milliseconds
-     */
-    public long getSelectedTimeInMillis() {
-        if (mViewSwitcher == null) {
-            return -1;
-        }
-        DayView view = (DayView) mViewSwitcher.getCurrentView();
-        if (view == null) {
-            return -1;
-        }
-        return view.getSelectedTimeInMillis();
-    }
-
-    public void eventsChanged() {
-        if (mViewSwitcher == null) {
-            return;
-        }
-        DayView view = (DayView) mViewSwitcher.getCurrentView();
-        view.clearCachedEvents();
-        view.reloadEvents();
-
-        view = (DayView) mViewSwitcher.getNextView();
-        view.clearCachedEvents();
-    }
-
-    public DayView getNextView() {
-        return (DayView) mViewSwitcher.getNextView();
-    }
-
-    public long getSupportedEventTypes() {
-        return EventType.GO_TO | EventType.EVENTS_CHANGED;
-    }
-
-    public void handleEvent(EventInfo msg) {
-        if (msg.eventType == EventType.GO_TO) {
-// TODO support a range of time
-// TODO support event_id
-// TODO support select message
-            goTo(msg.selectedTime, (msg.extraLong & CalendarController.EXTRA_GOTO_DATE) != 0,
-                    (msg.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0);
-        } else if (msg.eventType == EventType.EVENTS_CHANGED) {
-            eventsChanged();
-        }
-    }
-}
diff --git a/src/com/android/calendar/DayFragment.kt b/src/com/android/calendar/DayFragment.kt
new file mode 100644
index 0000000..39e92f5
--- /dev/null
+++ b/src/com/android/calendar/DayFragment.kt
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import com.android.calendar.CalendarController.EventInfo
+import com.android.calendar.CalendarController.EventType
+import android.app.Fragment
+import android.content.Context
+import android.os.Bundle
+import android.text.format.Time
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout.LayoutParams
+import android.view.animation.Animation
+import android.view.animation.AnimationUtils
+import android.widget.ProgressBar
+import android.widget.ViewSwitcher
+import android.widget.ViewSwitcher.ViewFactory
+
+/**
+ * This is the base class for Day and Week Activities.
+ */
+class DayFragment : Fragment, CalendarController.EventHandler, ViewFactory {
+    protected var mProgressBar: ProgressBar? = null
+    protected var mViewSwitcher: ViewSwitcher? = null
+    protected var mInAnimationForward: Animation? = null
+    protected var mOutAnimationForward: Animation? = null
+    protected var mInAnimationBackward: Animation? = null
+    protected var mOutAnimationBackward: Animation? = null
+    var mEventLoader: EventLoader? = null
+    var mSelectedDay: Time = Time()
+    private val mTZUpdater: Runnable = object : Runnable {
+        override fun run() {
+            if (!this@DayFragment.isAdded()) {
+                return
+            }
+            val tz: String? = Utils.getTimeZone(getActivity(), this)
+            mSelectedDay.timezone = tz
+            mSelectedDay.normalize(true)
+        }
+    }
+    private var mNumDays = 0
+
+    constructor() {
+        mSelectedDay.setToNow()
+    }
+
+    constructor(timeMillis: Long, numOfDays: Int) {
+        mNumDays = numOfDays
+        if (timeMillis == 0L) {
+            mSelectedDay.setToNow()
+        } else {
+            mSelectedDay.set(timeMillis)
+        }
+    }
+
+    override fun onCreate(icicle: Bundle?) {
+        super.onCreate(icicle)
+        val context: Context = getActivity()
+        mInAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_in)
+        mOutAnimationForward = AnimationUtils.loadAnimation(context, R.anim.slide_left_out)
+        mInAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_in)
+        mOutAnimationBackward = AnimationUtils.loadAnimation(context, R.anim.slide_right_out)
+        mEventLoader = EventLoader(context)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater?,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        val v: View? = inflater?.inflate(R.layout.day_activity, null)
+        mViewSwitcher = v?.findViewById(R.id.switcher) as? ViewSwitcher
+        mViewSwitcher?.setFactory(this)
+        mViewSwitcher?.getCurrentView()?.requestFocus()
+        (mViewSwitcher?.getCurrentView() as? DayView)?.updateTitle()
+        return v
+    }
+
+    override fun makeView(): View {
+        mTZUpdater.run()
+        val view = DayView(getActivity(), CalendarController
+                .getInstance(getActivity()), mViewSwitcher, mEventLoader, mNumDays)
+        view.setId(DayFragment.Companion.VIEW_ID)
+        view.setLayoutParams(LayoutParams(
+                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
+        view.setSelected(mSelectedDay, false, false)
+        return view
+    }
+
+    override fun onResume() {
+        super.onResume()
+        mEventLoader!!.startBackgroundThread()
+        mTZUpdater.run()
+        eventsChanged()
+        var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView
+        view?.handleOnResume()
+        view?.restartCurrentTimeUpdates()
+        view = mViewSwitcher?.getNextView() as? DayView
+        view?.handleOnResume()
+        view?.restartCurrentTimeUpdates()
+    }
+
+    override fun onSaveInstanceState(outState: Bundle?) {
+        super.onSaveInstanceState(outState)
+    }
+
+    override fun onPause() {
+        super.onPause()
+        var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView
+        view?.cleanup()
+        view = mViewSwitcher?.getNextView() as? DayView
+        view?.cleanup()
+        mEventLoader!!.stopBackgroundThread()
+
+        // Stop events cross-fade animation
+        view?.stopEventsAnimation()
+        (mViewSwitcher?.getNextView() as? DayView)?.stopEventsAnimation()
+    }
+
+    fun startProgressSpinner() {
+        // start the progress spinner
+        mProgressBar?.setVisibility(View.VISIBLE)
+    }
+
+    fun stopProgressSpinner() {
+        // stop the progress spinner
+        mProgressBar?.setVisibility(View.GONE)
+    }
+
+    private fun goTo(goToTime: Time?, ignoreTime: Boolean, animateToday: Boolean) {
+        if (mViewSwitcher == null) {
+            // The view hasn't been set yet. Just save the time and use it later.
+            mSelectedDay.set(goToTime)
+            return
+        }
+        val currentView: DayView? = mViewSwitcher?.getCurrentView() as? DayView
+
+        // How does goTo time compared to what's already displaying?
+        val diff: Int = currentView?.compareToVisibleTimeRange(goToTime as Time) as Int
+        if (diff == 0) {
+            // In visible range. No need to switch view
+            currentView?.setSelected(goToTime, ignoreTime, animateToday)
+        } else {
+            // Figure out which way to animate
+            if (diff > 0) {
+                mViewSwitcher?.setInAnimation(mInAnimationForward)
+                mViewSwitcher?.setOutAnimation(mOutAnimationForward)
+            } else {
+                mViewSwitcher?.setInAnimation(mInAnimationBackward)
+                mViewSwitcher?.setOutAnimation(mOutAnimationBackward)
+            }
+            val next: DayView? = mViewSwitcher?.getNextView() as? DayView
+            if (ignoreTime) {
+                next!!.firstVisibleHour = currentView.firstVisibleHour
+            }
+            next?.setSelected(goToTime, ignoreTime, animateToday)
+            next?.reloadEvents()
+            mViewSwitcher?.showNext()
+            next?.requestFocus()
+            next?.updateTitle()
+            next?.restartCurrentTimeUpdates()
+        }
+    }
+
+    /**
+     * Returns the selected time in milliseconds. The milliseconds are measured
+     * in UTC milliseconds from the epoch and uniquely specifies any selectable
+     * time.
+     *
+     * @return the selected time in milliseconds
+     */
+    val selectedTimeInMillis: Long
+        get() {
+            if (mViewSwitcher == null) {
+                return -1
+            }
+            val view: DayView = mViewSwitcher?.getCurrentView() as DayView ?: return -1
+            return view.selectedTimeInMillis
+        }
+
+    override fun eventsChanged() {
+        if (mViewSwitcher == null) {
+            return
+        }
+        var view: DayView? = mViewSwitcher?.getCurrentView() as? DayView
+        view?.clearCachedEvents()
+        view?.reloadEvents()
+        view = mViewSwitcher?.getNextView() as? DayView
+        view?.clearCachedEvents()
+    }
+
+    val nextView: DayView?
+        get() = mViewSwitcher?.getNextView() as? DayView
+    override val supportedEventTypes: Long
+        get() = CalendarController.EventType.GO_TO or CalendarController.EventType.EVENTS_CHANGED
+
+    override fun handleEvent(msg: CalendarController.EventInfo?) {
+        if (msg?.eventType == CalendarController.EventType.GO_TO) {
+// TODO support a range of time
+// TODO support event_id
+// TODO support select message
+            goTo(msg?.selectedTime, msg?.extraLong and CalendarController.EXTRA_GOTO_DATE != 0L,
+                    msg?.extraLong and CalendarController.EXTRA_GOTO_TODAY != 0L)
+        } else if (msg?.eventType == CalendarController.EventType.EVENTS_CHANGED) {
+            eventsChanged()
+        }
+    }
+
+    companion object {
+        /**
+         * The view id used for all the views we create. It's OK to have all child
+         * views have the same ID. This ID is used to pick which view receives
+         * focus when a view hierarchy is saved / restore
+         */
+        private const val VIEW_ID = 1
+        protected const val BUNDLE_KEY_RESTORE_TIME = "key_restore_time"
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/DayOfMonthDrawable.java b/src/com/android/calendar/DayOfMonthDrawable.java
deleted file mode 100644
index 461ab31..0000000
--- a/src/com/android/calendar/DayOfMonthDrawable.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-
-/**
- * A custom view to draw the day of the month in the today button in the options menu
- */
-
-public class DayOfMonthDrawable extends Drawable {
-
-    private String mDayOfMonth = "1";
-    private final Paint mPaint;
-    private final Rect mTextBounds = new Rect();
-    private static float mTextSize = 14;
-
-    public DayOfMonthDrawable(Context c) {
-        mTextSize = c.getResources().getDimension(R.dimen.today_icon_text_size);
-        mPaint = new Paint();
-        mPaint.setAlpha(255);
-        mPaint.setColor(0xFF777777);
-        mPaint.setTypeface(Typeface.DEFAULT_BOLD);
-        mPaint.setTextSize(mTextSize);
-        mPaint.setTextAlign(Paint.Align.CENTER);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        mPaint.getTextBounds(mDayOfMonth, 0, mDayOfMonth.length(), mTextBounds);
-        int textHeight = mTextBounds.bottom - mTextBounds.top;
-        Rect bounds = getBounds();
-        canvas.drawText(mDayOfMonth, bounds.right / 2, ((float) bounds.bottom + textHeight + 1) / 2,
-                mPaint);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mPaint.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        // Ignore
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.UNKNOWN;
-    }
-
-    public void setDayOfMonth(int day) {
-        mDayOfMonth = Integer.toString(day);
-        invalidateSelf();
-    }
-}
diff --git a/src/com/android/calendar/DayOfMonthDrawable.kt b/src/com/android/calendar/DayOfMonthDrawable.kt
new file mode 100644
index 0000000..e348b5a
--- /dev/null
+++ b/src/com/android/calendar/DayOfMonthDrawable.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.ColorFilter
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.graphics.Rect
+import android.graphics.Typeface
+import android.graphics.drawable.Drawable
+
+/**
+ * A custom view to draw the day of the month in the today button in the options menu
+ */
+class DayOfMonthDrawable(c: Context) : Drawable() {
+    private var mDayOfMonth = "1"
+    private val mPaint: Paint
+    private val mTextBounds: Rect = Rect()
+    override fun draw(canvas: Canvas) {
+        mPaint.getTextBounds(mDayOfMonth, 0, mDayOfMonth.length, mTextBounds)
+        val textHeight: Int = mTextBounds.bottom - mTextBounds.top
+        val bounds: Rect = getBounds()
+        canvas.drawText(
+            mDayOfMonth, (bounds.right).toFloat() / 2f, ((bounds.bottom).toFloat() +
+                textHeight + 1) / 2f, mPaint
+        )
+    }
+
+    override fun setAlpha(alpha: Int) {
+        mPaint.setAlpha(alpha)
+    }
+
+    override fun setColorFilter(cf: ColorFilter?) {
+        // Ignore
+    }
+
+    override fun getOpacity(): Int {
+        return PixelFormat.UNKNOWN
+    }
+
+    fun setDayOfMonth(day: Int) {
+        mDayOfMonth = Integer.toString(day)
+        invalidateSelf()
+    }
+
+    companion object {
+        private var mTextSize = 14f
+    }
+
+    init {
+        mTextSize = c.getResources().getDimension(R.dimen.today_icon_text_size)
+        mPaint = Paint()
+        mPaint.setAlpha(255)
+        mPaint.setColor(-0x888889)
+        mPaint.setTypeface(Typeface.DEFAULT_BOLD)
+        mPaint.setTextSize(mTextSize)
+        mPaint.setTextAlign(Paint.Align.CENTER)
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/calendar/DayView.java b/src/com/android/calendar/DayView.java
deleted file mode 100644
index 2fc00b3..0000000
--- a/src/com/android/calendar/DayView.java
+++ /dev/null
@@ -1,4008 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.calendar;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.app.AlertDialog;
-import android.app.Service;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.database.Cursor;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.Paint.Style;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Handler;
-import android.provider.CalendarContract.Attendees;
-import android.provider.CalendarContract.Calendars;
-import android.provider.CalendarContract.Events;
-import android.text.Layout.Alignment;
-import android.text.SpannableStringBuilder;
-import android.text.StaticLayout;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.text.style.StyleSpan;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.GestureDetector;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Animation;
-import android.view.animation.Interpolator;
-import android.view.animation.TranslateAnimation;
-import android.widget.EdgeEffect;
-import android.widget.ImageView;
-import android.widget.OverScroller;
-import android.widget.PopupWindow;
-import android.widget.TextView;
-import android.widget.ViewSwitcher;
-
-import com.android.calendar.CalendarController.EventType;
-import com.android.calendar.CalendarController.ViewType;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Formatter;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * View for multi-day view. So far only 1 and 7 day have been tested.
- */
-public class DayView extends View implements View.OnCreateContextMenuListener,
-        ScaleGestureDetector.OnScaleGestureListener, View.OnClickListener, View.OnLongClickListener
-        {
-    private static String TAG = "DayView";
-    private static boolean DEBUG = false;
-    private static boolean DEBUG_SCALING = false;
-    private static final String PERIOD_SPACE = ". ";
-
-    private static float mScale = 0; // Used for supporting different screen densities
-    private static final long INVALID_EVENT_ID = -1; //This is used for remembering a null event
-    // Duration of the allday expansion
-    private static final long ANIMATION_DURATION = 400;
-    // duration of the more allday event text fade
-    private static final long ANIMATION_SECONDARY_DURATION = 200;
-    // duration of the scroll to go to a specified time
-    private static final int GOTO_SCROLL_DURATION = 200;
-    // duration for events' cross-fade animation
-    private static final int EVENTS_CROSS_FADE_DURATION = 400;
-    // duration to show the event clicked
-    private static final int CLICK_DISPLAY_DURATION = 50;
-
-    private static final int MENU_DAY = 3;
-    private static final int MENU_EVENT_VIEW = 5;
-    private static final int MENU_EVENT_CREATE = 6;
-    private static final int MENU_EVENT_EDIT = 7;
-    private static final int MENU_EVENT_DELETE = 8;
-
-    private static int DEFAULT_CELL_HEIGHT = 64;
-    private static int MAX_CELL_HEIGHT = 150;
-    private static int MIN_Y_SPAN = 100;
-
-    private boolean mOnFlingCalled;
-    private boolean mStartingScroll = false;
-    protected boolean mPaused = true;
-    private Handler mHandler;
-    /**
-     * ID of the last event which was displayed with the toast popup.
-     *
-     * This is used to prevent popping up multiple quick views for the same event, especially
-     * during calendar syncs. This becomes valid when an event is selected, either by default
-     * on starting calendar or by scrolling to an event. It becomes invalid when the user
-     * explicitly scrolls to an empty time slot, changes views, or deletes the event.
-     */
-    private long mLastPopupEventID;
-
-    protected Context mContext;
-
-    private static final String[] CALENDARS_PROJECTION = new String[] {
-        Calendars._ID,          // 0
-        Calendars.CALENDAR_ACCESS_LEVEL, // 1
-        Calendars.OWNER_ACCOUNT, // 2
-    };
-    private static final int CALENDARS_INDEX_ACCESS_LEVEL = 1;
-    private static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2;
-    private static final String CALENDARS_WHERE = Calendars._ID + "=%d";
-
-    private static final int FROM_NONE = 0;
-    private static final int FROM_ABOVE = 1;
-    private static final int FROM_BELOW = 2;
-    private static final int FROM_LEFT = 4;
-    private static final int FROM_RIGHT = 8;
-
-    private static final int ACCESS_LEVEL_NONE = 0;
-    private static final int ACCESS_LEVEL_DELETE = 1;
-    private static final int ACCESS_LEVEL_EDIT = 2;
-
-    private static int mHorizontalSnapBackThreshold = 128;
-
-    private final ContinueScroll mContinueScroll = new ContinueScroll();
-
-    // Make this visible within the package for more informative debugging
-    Time mBaseDate;
-    private Time mCurrentTime;
-    //Update the current time line every five minutes if the window is left open that long
-    private static final int UPDATE_CURRENT_TIME_DELAY = 300000;
-    private final UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime();
-    private int mTodayJulianDay;
-
-    private final Typeface mBold = Typeface.DEFAULT_BOLD;
-    private int mFirstJulianDay;
-    private int mLoadedFirstJulianDay = -1;
-    private int mLastJulianDay;
-
-    private int mMonthLength;
-    private int mFirstVisibleDate;
-    private int mFirstVisibleDayOfWeek;
-    private int[] mEarliestStartHour;    // indexed by the week day offset
-    private boolean[] mHasAllDayEvent;   // indexed by the week day offset
-    private String mEventCountTemplate;
-    private Event mClickedEvent;           // The event the user clicked on
-    private Event mSavedClickedEvent;
-    private static int mOnDownDelay;
-    private int mClickedYLocation;
-    private long mDownTouchTime;
-
-    private int mEventsAlpha = 255;
-    private ObjectAnimator mEventsCrossFadeAnimation;
-
-    protected static StringBuilder mStringBuilder = new StringBuilder(50);
-    // TODO recreate formatter when locale changes
-    protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
-
-    private final Runnable mTZUpdater = new Runnable() {
-        @Override
-        public void run() {
-            String tz = Utils.getTimeZone(mContext, this);
-            mBaseDate.timezone = tz;
-            mBaseDate.normalize(true);
-            mCurrentTime.switchTimezone(tz);
-            invalidate();
-        }
-    };
-
-    // Sets the "clicked" color from the clicked event
-    private final Runnable mSetClick = new Runnable() {
-        @Override
-        public void run() {
-                mClickedEvent = mSavedClickedEvent;
-                mSavedClickedEvent = null;
-                DayView.this.invalidate();
-        }
-    };
-
-    // Clears the "clicked" color from the clicked event and launch the event
-    private final Runnable mClearClick = new Runnable() {
-        @Override
-        public void run() {
-            if (mClickedEvent != null) {
-                mController.sendEventRelatedEvent(this, EventType.VIEW_EVENT, mClickedEvent.id,
-                        mClickedEvent.startMillis, mClickedEvent.endMillis,
-                        DayView.this.getWidth() / 2, mClickedYLocation,
-                        getSelectedTimeInMillis());
-            }
-            mClickedEvent = null;
-            DayView.this.invalidate();
-        }
-    };
-
-    private final TodayAnimatorListener mTodayAnimatorListener = new TodayAnimatorListener();
-
-    class TodayAnimatorListener extends AnimatorListenerAdapter {
-        private volatile Animator mAnimator = null;
-        private volatile boolean mFadingIn = false;
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            synchronized (this) {
-                if (mAnimator != animation) {
-                    animation.removeAllListeners();
-                    animation.cancel();
-                    return;
-                }
-                if (mFadingIn) {
-                    if (mTodayAnimator != null) {
-                        mTodayAnimator.removeAllListeners();
-                        mTodayAnimator.cancel();
-                    }
-                    mTodayAnimator = ObjectAnimator
-                            .ofInt(DayView.this, "animateTodayAlpha", 255, 0);
-                    mAnimator = mTodayAnimator;
-                    mFadingIn = false;
-                    mTodayAnimator.addListener(this);
-                    mTodayAnimator.setDuration(600);
-                    mTodayAnimator.start();
-                } else {
-                    mAnimateToday = false;
-                    mAnimateTodayAlpha = 0;
-                    mAnimator.removeAllListeners();
-                    mAnimator = null;
-                    mTodayAnimator = null;
-                    invalidate();
-                }
-            }
-        }
-
-        public void setAnimator(Animator animation) {
-            mAnimator = animation;
-        }
-
-        public void setFadingIn(boolean fadingIn) {
-            mFadingIn = fadingIn;
-        }
-
-    }
-
-    AnimatorListenerAdapter mAnimatorListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationStart(Animator animation) {
-            mScrolling = true;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            mScrolling = false;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mScrolling = false;
-            resetSelectedHour();
-            invalidate();
-        }
-    };
-
-    /**
-     * This variable helps to avoid unnecessarily reloading events by keeping
-     * track of the start millis parameter used for the most recent loading
-     * of events.  If the next reload matches this, then the events are not
-     * reloaded.  To force a reload, set this to zero (this is set to zero
-     * in the method clearCachedEvents()).
-     */
-    private long mLastReloadMillis;
-
-    private ArrayList<Event> mEvents = new ArrayList<Event>();
-    private ArrayList<Event> mAllDayEvents = new ArrayList<Event>();
-    private StaticLayout[] mLayouts = null;
-    private StaticLayout[] mAllDayLayouts = null;
-    private int mSelectionDay;        // Julian day
-    private int mSelectionHour;
-
-    boolean mSelectionAllday;
-
-    // Current selection info for accessibility
-    private int mSelectionDayForAccessibility;        // Julian day
-    private int mSelectionHourForAccessibility;
-    private Event mSelectedEventForAccessibility;
-    // Last selection info for accessibility
-    private int mLastSelectionDayForAccessibility;
-    private int mLastSelectionHourForAccessibility;
-    private Event mLastSelectedEventForAccessibility;
-
-
-    /** Width of a day or non-conflicting event */
-    private int mCellWidth;
-
-    // Pre-allocate these objects and re-use them
-    private final Rect mRect = new Rect();
-    private final Rect mDestRect = new Rect();
-    private final Rect mSelectionRect = new Rect();
-    // This encloses the more allDay events icon
-    private final Rect mExpandAllDayRect = new Rect();
-    // TODO Clean up paint usage
-    private final Paint mPaint = new Paint();
-    private final Paint mEventTextPaint = new Paint();
-    private final Paint mSelectionPaint = new Paint();
-    private float[] mLines;
-
-    private int mFirstDayOfWeek; // First day of the week
-
-    private PopupWindow mPopup;
-    private View mPopupView;
-
-    // The number of milliseconds to show the popup window
-    private static final int POPUP_DISMISS_DELAY = 3000;
-    private final DismissPopup mDismissPopup = new DismissPopup();
-
-    private boolean mRemeasure = true;
-
-    private final EventLoader mEventLoader;
-    protected final EventGeometry mEventGeometry;
-
-    private static float GRID_LINE_LEFT_MARGIN = 0;
-    private static final float GRID_LINE_INNER_WIDTH = 1;
-
-    private static final int DAY_GAP = 1;
-    private static final int HOUR_GAP = 1;
-    // This is the standard height of an allday event with no restrictions
-    private static int SINGLE_ALLDAY_HEIGHT = 34;
-    /**
-    * This is the minimum desired height of a allday event.
-    * When unexpanded, allday events will use this height.
-    * When expanded allDay events will attempt to grow to fit all
-    * events at this height.
-    */
-    private static float MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0F; // in pixels
-    /**
-     * This is how big the unexpanded allday height is allowed to be.
-     * It will get adjusted based on screen size
-     */
-    private static int MAX_UNEXPANDED_ALLDAY_HEIGHT =
-            (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
-    /**
-     * This is the minimum size reserved for displaying regular events.
-     * The expanded allDay region can't expand into this.
-     */
-    private static int MIN_HOURS_HEIGHT = 180;
-    private static int ALLDAY_TOP_MARGIN = 1;
-    // The largest a single allDay event will become.
-    private static int MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34;
-
-    private static int HOURS_TOP_MARGIN = 2;
-    private static int HOURS_LEFT_MARGIN = 2;
-    private static int HOURS_RIGHT_MARGIN = 4;
-    private static int HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
-    private static int NEW_EVENT_MARGIN = 4;
-    private static int NEW_EVENT_WIDTH = 2;
-    private static int NEW_EVENT_MAX_LENGTH = 16;
-
-    private static int CURRENT_TIME_LINE_SIDE_BUFFER = 4;
-    private static int CURRENT_TIME_LINE_TOP_OFFSET = 2;
-
-    /* package */ static final int MINUTES_PER_HOUR = 60;
-    /* package */ static final int MINUTES_PER_DAY = MINUTES_PER_HOUR * 24;
-    /* package */ static final int MILLIS_PER_MINUTE = 60 * 1000;
-    /* package */ static final int MILLIS_PER_HOUR = (3600 * 1000);
-    /* package */ static final int MILLIS_PER_DAY = MILLIS_PER_HOUR * 24;
-
-    // More events text will transition between invisible and this alpha
-    private static final int MORE_EVENTS_MAX_ALPHA = 0x4C;
-    private static int DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0;
-    private static int DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5;
-    private static int DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6;
-    private static int DAY_HEADER_RIGHT_MARGIN = 4;
-    private static int DAY_HEADER_BOTTOM_MARGIN = 3;
-    private static float DAY_HEADER_FONT_SIZE = 14;
-    private static float DATE_HEADER_FONT_SIZE = 32;
-    private static float NORMAL_FONT_SIZE = 12;
-    private static float EVENT_TEXT_FONT_SIZE = 12;
-    private static float HOURS_TEXT_SIZE = 12;
-    private static float AMPM_TEXT_SIZE = 9;
-    private static int MIN_HOURS_WIDTH = 96;
-    private static int MIN_CELL_WIDTH_FOR_TEXT = 20;
-    private static final int MAX_EVENT_TEXT_LEN = 500;
-    // smallest height to draw an event with
-    private static float MIN_EVENT_HEIGHT = 24.0F; // in pixels
-    private static int CALENDAR_COLOR_SQUARE_SIZE = 10;
-    private static int EVENT_RECT_TOP_MARGIN = 1;
-    private static int EVENT_RECT_BOTTOM_MARGIN = 0;
-    private static int EVENT_RECT_LEFT_MARGIN = 1;
-    private static int EVENT_RECT_RIGHT_MARGIN = 0;
-    private static int EVENT_RECT_STROKE_WIDTH = 2;
-    private static int EVENT_TEXT_TOP_MARGIN = 2;
-    private static int EVENT_TEXT_BOTTOM_MARGIN = 2;
-    private static int EVENT_TEXT_LEFT_MARGIN = 6;
-    private static int EVENT_TEXT_RIGHT_MARGIN = 6;
-    private static int ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1;
-    private static int EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN;
-    private static int EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN;
-    private static int EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
-    private static int EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN;
-    // margins and sizing for the expand allday icon
-    private static int EXPAND_ALL_DAY_BOTTOM_MARGIN = 10;
-    // sizing for "box +n" in allDay events
-    private static int EVENT_SQUARE_WIDTH = 10;
-    private static int EVENT_LINE_PADDING = 4;
-    private static int NEW_EVENT_HINT_FONT_SIZE = 12;
-
-    private static int mEventTextColor;
-    private static int mMoreEventsTextColor;
-
-    private static int mWeek_saturdayColor;
-    private static int mWeek_sundayColor;
-    private static int mCalendarDateBannerTextColor;
-    private static int mCalendarAmPmLabel;
-    private static int mCalendarGridAreaSelected;
-    private static int mCalendarGridLineInnerHorizontalColor;
-    private static int mCalendarGridLineInnerVerticalColor;
-    private static int mFutureBgColor;
-    private static int mFutureBgColorRes;
-    private static int mBgColor;
-    private static int mNewEventHintColor;
-    private static int mCalendarHourLabelColor;
-    private static int mMoreAlldayEventsTextAlpha = MORE_EVENTS_MAX_ALPHA;
-
-    private float mAnimationDistance = 0;
-    private int mViewStartX;
-    private int mViewStartY;
-    private int mMaxViewStartY;
-    private int mViewHeight;
-    private int mViewWidth;
-    private int mGridAreaHeight = -1;
-    private static int mCellHeight = 0; // shared among all DayViews
-    private static int mMinCellHeight = 32;
-    private int mScrollStartY;
-    private int mPreviousDirection;
-    private static int mScaledPagingTouchSlop = 0;
-
-    /**
-     * Vertical distance or span between the two touch points at the start of a
-     * scaling gesture
-     */
-    private float mStartingSpanY = 0;
-    /** Height of 1 hour in pixels at the start of a scaling gesture */
-    private int mCellHeightBeforeScaleGesture;
-    /** The hour at the center two touch points */
-    private float mGestureCenterHour = 0;
-
-    private boolean mRecalCenterHour = false;
-
-    /**
-     * Flag to decide whether to handle the up event. Cases where up events
-     * should be ignored are 1) right after a scale gesture and 2) finger was
-     * down before app launch
-     */
-    private boolean mHandleActionUp = true;
-
-    private int mHoursTextHeight;
-    /**
-     * The height of the area used for allday events
-     */
-    private int mAlldayHeight;
-    /**
-     * The height of the allday event area used during animation
-     */
-    private int mAnimateDayHeight = 0;
-    /**
-     * The height of an individual allday event during animation
-     */
-    private int mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
-    /**
-     * Whether to use the expand or collapse icon.
-     */
-    private static boolean mUseExpandIcon = true;
-    /**
-     * The height of the day names/numbers
-     */
-    private static int DAY_HEADER_HEIGHT = 45;
-    /**
-     * The height of the day names/numbers for multi-day views
-     */
-    private static int MULTI_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
-    /**
-     * The height of the day names/numbers when viewing a single day
-     */
-    private static int ONE_DAY_HEADER_HEIGHT = DAY_HEADER_HEIGHT;
-    /**
-     * Max of all day events in a given day in this view.
-     */
-    private int mMaxAlldayEvents;
-    /**
-     * A count of the number of allday events that were not drawn for each day
-     */
-    private int[] mSkippedAlldayEvents;
-    /**
-     * The number of allDay events at which point we start hiding allDay events.
-     */
-    private int mMaxUnexpandedAlldayEventCount = 4;
-    /**
-     * Whether or not to expand the allDay area to fill the screen
-     */
-    private static boolean mShowAllAllDayEvents = false;
-
-    protected int mNumDays = 7;
-    private int mNumHours = 10;
-
-    /** Width of the time line (list of hours) to the left. */
-    private int mHoursWidth;
-    private int mDateStrWidth;
-    /** Top of the scrollable region i.e. below date labels and all day events */
-    private int mFirstCell;
-    /** First fully visibile hour */
-    private int mFirstHour = -1;
-    /** Distance between the mFirstCell and the top of first fully visible hour. */
-    private int mFirstHourOffset;
-    private String[] mHourStrs;
-    private String[] mDayStrs;
-    private String[] mDayStrs2Letter;
-    private boolean mIs24HourFormat;
-
-    private final ArrayList<Event> mSelectedEvents = new ArrayList<Event>();
-    private boolean mComputeSelectedEvents;
-    private boolean mUpdateToast;
-    private Event mSelectedEvent;
-    private Event mPrevSelectedEvent;
-    private final Rect mPrevBox = new Rect();
-    protected final Resources mResources;
-    protected final Drawable mCurrentTimeLine;
-    protected final Drawable mCurrentTimeAnimateLine;
-    protected final Drawable mTodayHeaderDrawable;
-    protected final Drawable mExpandAlldayDrawable;
-    protected final Drawable mCollapseAlldayDrawable;
-    protected Drawable mAcceptedOrTentativeEventBoxDrawable;
-    private String mAmString;
-    private String mPmString;
-    private static int sCounter = 0;
-
-    ScaleGestureDetector mScaleGestureDetector;
-
-    /**
-     * The initial state of the touch mode when we enter this view.
-     */
-    private static final int TOUCH_MODE_INITIAL_STATE = 0;
-
-    /**
-     * Indicates we just received the touch event and we are waiting to see if
-     * it is a tap or a scroll gesture.
-     */
-    private static final int TOUCH_MODE_DOWN = 1;
-
-    /**
-     * Indicates the touch gesture is a vertical scroll
-     */
-    private static final int TOUCH_MODE_VSCROLL = 0x20;
-
-    /**
-     * Indicates the touch gesture is a horizontal scroll
-     */
-    private static final int TOUCH_MODE_HSCROLL = 0x40;
-
-    private int mTouchMode = TOUCH_MODE_INITIAL_STATE;
-
-    /**
-     * The selection modes are HIDDEN, PRESSED, SELECTED, and LONGPRESS.
-     */
-    private static final int SELECTION_HIDDEN = 0;
-    private static final int SELECTION_PRESSED = 1; // D-pad down but not up yet
-    private static final int SELECTION_SELECTED = 2;
-    private static final int SELECTION_LONGPRESS = 3;
-
-    private int mSelectionMode = SELECTION_HIDDEN;
-
-    private boolean mScrolling = false;
-
-    // Pixels scrolled
-    private float mInitialScrollX;
-    private float mInitialScrollY;
-
-    private boolean mAnimateToday = false;
-    private int mAnimateTodayAlpha = 0;
-
-    // Animates the height of the allday region
-    ObjectAnimator mAlldayAnimator;
-    // Animates the height of events in the allday region
-    ObjectAnimator mAlldayEventAnimator;
-    // Animates the transparency of the more events text
-    ObjectAnimator mMoreAlldayEventsAnimator;
-    // Animates the current time marker when Today is pressed
-    ObjectAnimator mTodayAnimator;
-    // whether or not an event is stopping because it was cancelled
-    private boolean mCancellingAnimations = false;
-    // tracks whether a touch originated in the allday area
-    private boolean mTouchStartedInAlldayArea = false;
-
-    private final CalendarController mController;
-    private final ViewSwitcher mViewSwitcher;
-    private final GestureDetector mGestureDetector;
-    private final OverScroller mScroller;
-    private final EdgeEffect mEdgeEffectTop;
-    private final EdgeEffect mEdgeEffectBottom;
-    private boolean mCallEdgeEffectOnAbsorb;
-    private final int OVERFLING_DISTANCE;
-    private float mLastVelocity;
-
-    private final ScrollInterpolator mHScrollInterpolator;
-    private AccessibilityManager mAccessibilityMgr = null;
-    private boolean mIsAccessibilityEnabled = false;
-    private boolean mTouchExplorationEnabled = false;
-    private final String mNewEventHintString;
-
-    public DayView(Context context, CalendarController controller,
-            ViewSwitcher viewSwitcher, EventLoader eventLoader, int numDays) {
-        super(context);
-        mContext = context;
-        initAccessibilityVariables();
-
-        mResources = context.getResources();
-        mNewEventHintString = mResources.getString(R.string.day_view_new_event_hint);
-        mNumDays = numDays;
-
-        DATE_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.date_header_text_size);
-        DAY_HEADER_FONT_SIZE = (int) mResources.getDimension(R.dimen.day_label_text_size);
-        ONE_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.one_day_header_height);
-        DAY_HEADER_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.day_header_bottom_margin);
-        EXPAND_ALL_DAY_BOTTOM_MARGIN = (int) mResources.getDimension(R.dimen.all_day_bottom_margin);
-        HOURS_TEXT_SIZE = (int) mResources.getDimension(R.dimen.hours_text_size);
-        AMPM_TEXT_SIZE = (int) mResources.getDimension(R.dimen.ampm_text_size);
-        MIN_HOURS_WIDTH = (int) mResources.getDimension(R.dimen.min_hours_width);
-        HOURS_LEFT_MARGIN = (int) mResources.getDimension(R.dimen.hours_left_margin);
-        HOURS_RIGHT_MARGIN = (int) mResources.getDimension(R.dimen.hours_right_margin);
-        MULTI_DAY_HEADER_HEIGHT = (int) mResources.getDimension(R.dimen.day_header_height);
-        int eventTextSizeId;
-        if (mNumDays == 1) {
-            eventTextSizeId = R.dimen.day_view_event_text_size;
-        } else {
-            eventTextSizeId = R.dimen.week_view_event_text_size;
-        }
-        EVENT_TEXT_FONT_SIZE = (int) mResources.getDimension(eventTextSizeId);
-        NEW_EVENT_HINT_FONT_SIZE = (int) mResources.getDimension(R.dimen.new_event_hint_text_size);
-        MIN_EVENT_HEIGHT = mResources.getDimension(R.dimen.event_min_height);
-        MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = MIN_EVENT_HEIGHT;
-        EVENT_TEXT_TOP_MARGIN = (int) mResources.getDimension(R.dimen.event_text_vertical_margin);
-        EVENT_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN;
-        EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN;
-        EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_TOP_MARGIN;
-
-        EVENT_TEXT_LEFT_MARGIN = (int) mResources
-                .getDimension(R.dimen.event_text_horizontal_margin);
-        EVENT_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
-        EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
-        EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_LEFT_MARGIN;
-
-        if (mScale == 0) {
-
-            mScale = mResources.getDisplayMetrics().density;
-            if (mScale != 1) {
-                SINGLE_ALLDAY_HEIGHT *= mScale;
-                ALLDAY_TOP_MARGIN *= mScale;
-                MAX_HEIGHT_OF_ONE_ALLDAY_EVENT *= mScale;
-
-                NORMAL_FONT_SIZE *= mScale;
-                GRID_LINE_LEFT_MARGIN *= mScale;
-                HOURS_TOP_MARGIN *= mScale;
-                MIN_CELL_WIDTH_FOR_TEXT *= mScale;
-                MAX_UNEXPANDED_ALLDAY_HEIGHT *= mScale;
-                mAnimateDayEventHeight = (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
-
-                CURRENT_TIME_LINE_SIDE_BUFFER *= mScale;
-                CURRENT_TIME_LINE_TOP_OFFSET *= mScale;
-
-                MIN_Y_SPAN *= mScale;
-                MAX_CELL_HEIGHT *= mScale;
-                DEFAULT_CELL_HEIGHT *= mScale;
-                DAY_HEADER_HEIGHT *= mScale;
-                DAY_HEADER_RIGHT_MARGIN *= mScale;
-                DAY_HEADER_ONE_DAY_LEFT_MARGIN *= mScale;
-                DAY_HEADER_ONE_DAY_RIGHT_MARGIN *= mScale;
-                DAY_HEADER_ONE_DAY_BOTTOM_MARGIN *= mScale;
-                CALENDAR_COLOR_SQUARE_SIZE *= mScale;
-                EVENT_RECT_TOP_MARGIN *= mScale;
-                EVENT_RECT_BOTTOM_MARGIN *= mScale;
-                ALL_DAY_EVENT_RECT_BOTTOM_MARGIN *= mScale;
-                EVENT_RECT_LEFT_MARGIN *= mScale;
-                EVENT_RECT_RIGHT_MARGIN *= mScale;
-                EVENT_RECT_STROKE_WIDTH *= mScale;
-                EVENT_SQUARE_WIDTH *= mScale;
-                EVENT_LINE_PADDING *= mScale;
-                NEW_EVENT_MARGIN *= mScale;
-                NEW_EVENT_WIDTH *= mScale;
-                NEW_EVENT_MAX_LENGTH *= mScale;
-            }
-        }
-        HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN;
-        DAY_HEADER_HEIGHT = mNumDays == 1 ? ONE_DAY_HEADER_HEIGHT : MULTI_DAY_HEADER_HEIGHT;
-
-        mCurrentTimeLine = mResources.getDrawable(R.drawable.timeline_indicator_holo_light);
-        mCurrentTimeAnimateLine = mResources
-                .getDrawable(R.drawable.timeline_indicator_activated_holo_light);
-        mTodayHeaderDrawable = mResources.getDrawable(R.drawable.today_blue_week_holo_light);
-        mExpandAlldayDrawable = mResources.getDrawable(R.drawable.ic_expand_holo_light);
-        mCollapseAlldayDrawable = mResources.getDrawable(R.drawable.ic_collapse_holo_light);
-        mNewEventHintColor =  mResources.getColor(R.color.new_event_hint_text_color);
-        mAcceptedOrTentativeEventBoxDrawable = mResources
-                .getDrawable(R.drawable.panel_month_event_holo_light);
-
-        mEventLoader = eventLoader;
-        mEventGeometry = new EventGeometry();
-        mEventGeometry.setMinEventHeight(MIN_EVENT_HEIGHT);
-        mEventGeometry.setHourGap(HOUR_GAP);
-        mEventGeometry.setCellMargin(DAY_GAP);
-        mLastPopupEventID = INVALID_EVENT_ID;
-        mController = controller;
-        mViewSwitcher = viewSwitcher;
-        mGestureDetector = new GestureDetector(context, new CalendarGestureListener());
-        mScaleGestureDetector = new ScaleGestureDetector(getContext(), this);
-        if (mCellHeight == 0) {
-            mCellHeight = Utils.getSharedPreference(mContext,
-                    GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT, DEFAULT_CELL_HEIGHT);
-        }
-        mScroller = new OverScroller(context);
-        mHScrollInterpolator = new ScrollInterpolator();
-        mEdgeEffectTop = new EdgeEffect(context);
-        mEdgeEffectBottom = new EdgeEffect(context);
-        ViewConfiguration vc = ViewConfiguration.get(context);
-        mScaledPagingTouchSlop = vc.getScaledPagingTouchSlop();
-        mOnDownDelay = ViewConfiguration.getTapTimeout();
-        OVERFLING_DISTANCE = vc.getScaledOverflingDistance();
-
-        init(context);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        if (mHandler == null) {
-            mHandler = getHandler();
-            mHandler.post(mUpdateCurrentTime);
-        }
-    }
-
-    private void init(Context context) {
-        setFocusable(true);
-
-        // Allow focus in touch mode so that we can do keyboard shortcuts
-        // even after we've entered touch mode.
-        setFocusableInTouchMode(true);
-        setClickable(true);
-        setOnCreateContextMenuListener(this);
-
-        mFirstDayOfWeek = Utils.getFirstDayOfWeek(context);
-
-        mCurrentTime = new Time(Utils.getTimeZone(context, mTZUpdater));
-        long currentTime = System.currentTimeMillis();
-        mCurrentTime.set(currentTime);
-        mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
-
-        mWeek_saturdayColor = mResources.getColor(R.color.week_saturday);
-        mWeek_sundayColor = mResources.getColor(R.color.week_sunday);
-        mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color);
-        mFutureBgColorRes = mResources.getColor(R.color.calendar_future_bg_color);
-        mBgColor = mResources.getColor(R.color.calendar_hour_background);
-        mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label);
-        mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected);
-        mCalendarGridLineInnerHorizontalColor = mResources
-                .getColor(R.color.calendar_grid_line_inner_horizontal_color);
-        mCalendarGridLineInnerVerticalColor = mResources
-                .getColor(R.color.calendar_grid_line_inner_vertical_color);
-        mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label);
-        mEventTextColor = mResources.getColor(R.color.calendar_event_text_color);
-        mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color);
-
-        mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE);
-        mEventTextPaint.setTextAlign(Paint.Align.LEFT);
-        mEventTextPaint.setAntiAlias(true);
-
-        int gridLineColor = mResources.getColor(R.color.calendar_grid_line_highlight_color);
-        Paint p = mSelectionPaint;
-        p.setColor(gridLineColor);
-        p.setStyle(Style.FILL);
-        p.setAntiAlias(false);
-
-        p = mPaint;
-        p.setAntiAlias(true);
-
-        // Allocate space for 2 weeks worth of weekday names so that we can
-        // easily start the week display at any week day.
-        mDayStrs = new String[14];
-
-        // Also create an array of 2-letter abbreviations.
-        mDayStrs2Letter = new String[14];
-
-        for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) {
-            int index = i - Calendar.SUNDAY;
-            // e.g. Tue for Tuesday
-            mDayStrs[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM)
-                    .toUpperCase();
-            mDayStrs[index + 7] = mDayStrs[index];
-            // e.g. Tu for Tuesday
-            mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT)
-                    .toUpperCase();
-
-            // If we don't have 2-letter day strings, fall back to 1-letter.
-            if (mDayStrs2Letter[index].equals(mDayStrs[index])) {
-                mDayStrs2Letter[index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORTEST);
-            }
-
-            mDayStrs2Letter[index + 7] = mDayStrs2Letter[index];
-        }
-
-        // Figure out how much space we need for the 3-letter abbrev names
-        // in the worst case.
-        p.setTextSize(DATE_HEADER_FONT_SIZE);
-        p.setTypeface(mBold);
-        String[] dateStrs = {" 28", " 30"};
-        mDateStrWidth = computeMaxStringWidth(0, dateStrs, p);
-        p.setTextSize(DAY_HEADER_FONT_SIZE);
-        mDateStrWidth += computeMaxStringWidth(0, mDayStrs, p);
-
-        p.setTextSize(HOURS_TEXT_SIZE);
-        p.setTypeface(null);
-        handleOnResume();
-
-        mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase();
-        mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase();
-        String[] ampm = {mAmString, mPmString};
-        p.setTextSize(AMPM_TEXT_SIZE);
-        mHoursWidth = Math.max(HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p)
-                + HOURS_RIGHT_MARGIN);
-        mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth);
-
-        LayoutInflater inflater;
-        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mPopupView = inflater.inflate(R.layout.bubble_event, null);
-        mPopupView.setLayoutParams(new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT));
-        mPopup = new PopupWindow(context);
-        mPopup.setContentView(mPopupView);
-        Resources.Theme dialogTheme = getResources().newTheme();
-        dialogTheme.applyStyle(android.R.style.Theme_Dialog, true);
-        TypedArray ta = dialogTheme.obtainStyledAttributes(new int[] {
-            android.R.attr.windowBackground });
-        mPopup.setBackgroundDrawable(ta.getDrawable(0));
-        ta.recycle();
-
-        // Enable touching the popup window
-        mPopupView.setOnClickListener(this);
-        // Catch long clicks for creating a new event
-        setOnLongClickListener(this);
-
-        mBaseDate = new Time(Utils.getTimeZone(context, mTZUpdater));
-        long millis = System.currentTimeMillis();
-        mBaseDate.set(millis);
-
-        mEarliestStartHour = new int[mNumDays];
-        mHasAllDayEvent = new boolean[mNumDays];
-
-        // mLines is the array of points used with Canvas.drawLines() in
-        // drawGridBackground() and drawAllDayEvents().  Its size depends
-        // on the max number of lines that can ever be drawn by any single
-        // drawLines() call in either of those methods.
-        final int maxGridLines = (24 + 1)  // max horizontal lines we might draw
-                + (mNumDays + 1); // max vertical lines we might draw
-        mLines = new float[maxGridLines * 4];
-    }
-
-    /**
-     * This is called when the popup window is pressed.
-     */
-    public void onClick(View v) {
-        if (v == mPopupView) {
-            // Pretend it was a trackball click because that will always
-            // jump to the "View event" screen.
-            switchViews(true /* trackball */);
-        }
-    }
-
-    public void handleOnResume() {
-        initAccessibilityVariables();
-        if(Utils.getSharedPreference(mContext, OtherPreferences.KEY_OTHER_1, false)) {
-            mFutureBgColor = 0;
-        } else {
-            mFutureBgColor = mFutureBgColorRes;
-        }
-        mIs24HourFormat = DateFormat.is24HourFormat(mContext);
-        mHourStrs = mIs24HourFormat ? CalendarData.s24Hours : CalendarData.s12HoursNoAmPm;
-        mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext);
-        mLastSelectionDayForAccessibility = 0;
-        mLastSelectionHourForAccessibility = 0;
-        mLastSelectedEventForAccessibility = null;
-        mSelectionMode = SELECTION_HIDDEN;
-    }
-
-    private void initAccessibilityVariables() {
-        mAccessibilityMgr = (AccessibilityManager) mContext
-                .getSystemService(Service.ACCESSIBILITY_SERVICE);
-        mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr.isEnabled();
-        mTouchExplorationEnabled = isTouchExplorationEnabled();
-    }
-
-    /**
-     * Returns the start of the selected time in milliseconds since the epoch.
-     *
-     * @return selected time in UTC milliseconds since the epoch.
-     */
-    long getSelectedTimeInMillis() {
-        Time time = new Time(mBaseDate);
-        time.setJulianDay(mSelectionDay);
-        time.hour = mSelectionHour;
-
-        // We ignore the "isDst" field because we want normalize() to figure
-        // out the correct DST value and not adjust the selected time based
-        // on the current setting of DST.
-        return time.normalize(true /* ignore isDst */);
-    }
-
-    Time getSelectedTime() {
-        Time time = new Time(mBaseDate);
-        time.setJulianDay(mSelectionDay);
-        time.hour = mSelectionHour;
-
-        // We ignore the "isDst" field because we want normalize() to figure
-        // out the correct DST value and not adjust the selected time based
-        // on the current setting of DST.
-        time.normalize(true /* ignore isDst */);
-        return time;
-    }
-
-    Time getSelectedTimeForAccessibility() {
-        Time time = new Time(mBaseDate);
-        time.setJulianDay(mSelectionDayForAccessibility);
-        time.hour = mSelectionHourForAccessibility;
-
-        // We ignore the "isDst" field because we want normalize() to figure
-        // out the correct DST value and not adjust the selected time based
-        // on the current setting of DST.
-        time.normalize(true /* ignore isDst */);
-        return time;
-    }
-
-    /**
-     * Returns the start of the selected time in minutes since midnight,
-     * local time.  The derived class must ensure that this is consistent
-     * with the return value from getSelectedTimeInMillis().
-     */
-    int getSelectedMinutesSinceMidnight() {
-        return mSelectionHour * MINUTES_PER_HOUR;
-    }
-
-    int getFirstVisibleHour() {
-        return mFirstHour;
-    }
-
-    void setFirstVisibleHour(int firstHour) {
-        mFirstHour = firstHour;
-        mFirstHourOffset = 0;
-    }
-
-    public void setSelected(Time time, boolean ignoreTime, boolean animateToday) {
-        mBaseDate.set(time);
-        setSelectedHour(mBaseDate.hour);
-        setSelectedEvent(null);
-        mPrevSelectedEvent = null;
-        long millis = mBaseDate.toMillis(false /* use isDst */);
-        setSelectedDay(Time.getJulianDay(millis, mBaseDate.gmtoff));
-        mSelectedEvents.clear();
-        mComputeSelectedEvents = true;
-
-        int gotoY = Integer.MIN_VALUE;
-
-        if (!ignoreTime && mGridAreaHeight != -1) {
-            int lastHour = 0;
-
-            if (mBaseDate.hour < mFirstHour) {
-                // Above visible region
-                gotoY = mBaseDate.hour * (mCellHeight + HOUR_GAP);
-            } else {
-                lastHour = (mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP)
-                        + mFirstHour;
-
-                if (mBaseDate.hour >= lastHour) {
-                    // Below visible region
-
-                    // target hour + 1 (to give it room to see the event) -
-                    // grid height (to get the y of the top of the visible
-                    // region)
-                    gotoY = (int) ((mBaseDate.hour + 1 + mBaseDate.minute / 60.0f)
-                            * (mCellHeight + HOUR_GAP) - mGridAreaHeight);
-                }
-            }
-
-            if (DEBUG) {
-                Log.e(TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH "
-                        + (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight
-                        + " ymax " + mMaxViewStartY);
-            }
-
-            if (gotoY > mMaxViewStartY) {
-                gotoY = mMaxViewStartY;
-            } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) {
-                gotoY = 0;
-            }
-        }
-
-        recalc();
-
-        mRemeasure = true;
-        invalidate();
-
-        boolean delayAnimateToday = false;
-        if (gotoY != Integer.MIN_VALUE) {
-            ValueAnimator scrollAnim = ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY);
-            scrollAnim.setDuration(GOTO_SCROLL_DURATION);
-            scrollAnim.setInterpolator(new AccelerateDecelerateInterpolator());
-            scrollAnim.addListener(mAnimatorListener);
-            scrollAnim.start();
-            delayAnimateToday = true;
-        }
-        if (animateToday) {
-            synchronized (mTodayAnimatorListener) {
-                if (mTodayAnimator != null) {
-                    mTodayAnimator.removeAllListeners();
-                    mTodayAnimator.cancel();
-                }
-                mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha",
-                        mAnimateTodayAlpha, 255);
-                mAnimateToday = true;
-                mTodayAnimatorListener.setFadingIn(true);
-                mTodayAnimatorListener.setAnimator(mTodayAnimator);
-                mTodayAnimator.addListener(mTodayAnimatorListener);
-                mTodayAnimator.setDuration(150);
-                if (delayAnimateToday) {
-                    mTodayAnimator.setStartDelay(GOTO_SCROLL_DURATION);
-                }
-                mTodayAnimator.start();
-            }
-        }
-        sendAccessibilityEventAsNeeded(false);
-    }
-
-    // Called from animation framework via reflection. Do not remove
-    public void setViewStartY(int viewStartY) {
-        if (viewStartY > mMaxViewStartY) {
-            viewStartY = mMaxViewStartY;
-        }
-
-        mViewStartY = viewStartY;
-
-        computeFirstHour();
-        invalidate();
-    }
-
-    public void setAnimateTodayAlpha(int todayAlpha) {
-        mAnimateTodayAlpha = todayAlpha;
-        invalidate();
-    }
-
-    public Time getSelectedDay() {
-        Time time = new Time(mBaseDate);
-        time.setJulianDay(mSelectionDay);
-        time.hour = mSelectionHour;
-
-        // We ignore the "isDst" field because we want normalize() to figure
-        // out the correct DST value and not adjust the selected time based
-        // on the current setting of DST.
-        time.normalize(true /* ignore isDst */);
-        return time;
-    }
-
-    public void updateTitle() {
-        Time start = new Time(mBaseDate);
-        start.normalize(true);
-        Time end = new Time(start);
-        end.monthDay += mNumDays - 1;
-        // Move it forward one minute so the formatter doesn't lose a day
-        end.minute += 1;
-        end.normalize(true);
-
-        long formatFlags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
-        if (mNumDays != 1) {
-            // Don't show day of the month if for multi-day view
-            formatFlags |= DateUtils.FORMAT_NO_MONTH_DAY;
-
-            // Abbreviate the month if showing multiple months
-            if (start.month != end.month) {
-                formatFlags |= DateUtils.FORMAT_ABBREV_MONTH;
-            }
-        }
-
-        mController.sendEvent(this, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT,
-                formatFlags, null, null);
-    }
-
-    /**
-     * return a negative number if "time" is comes before the visible time
-     * range, a positive number if "time" is after the visible time range, and 0
-     * if it is in the visible time range.
-     */
-    public int compareToVisibleTimeRange(Time time) {
-
-        int savedHour = mBaseDate.hour;
-        int savedMinute = mBaseDate.minute;
-        int savedSec = mBaseDate.second;
-
-        mBaseDate.hour = 0;
-        mBaseDate.minute = 0;
-        mBaseDate.second = 0;
-
-        if (DEBUG) {
-            Log.d(TAG, "Begin " + mBaseDate.toString());
-            Log.d(TAG, "Diff  " + time.toString());
-        }
-
-        // Compare beginning of range
-        int diff = Time.compare(time, mBaseDate);
-        if (diff > 0) {
-            // Compare end of range
-            mBaseDate.monthDay += mNumDays;
-            mBaseDate.normalize(true);
-            diff = Time.compare(time, mBaseDate);
-
-            if (DEBUG) Log.d(TAG, "End   " + mBaseDate.toString());
-
-            mBaseDate.monthDay -= mNumDays;
-            mBaseDate.normalize(true);
-            if (diff < 0) {
-                // in visible time
-                diff = 0;
-            } else if (diff == 0) {
-                // Midnight of following day
-                diff = 1;
-            }
-        }
-
-        if (DEBUG) Log.d(TAG, "Diff: " + diff);
-
-        mBaseDate.hour = savedHour;
-        mBaseDate.minute = savedMinute;
-        mBaseDate.second = savedSec;
-        return diff;
-    }
-
-    private void recalc() {
-        // Set the base date to the beginning of the week if we are displaying
-        // 7 days at a time.
-        if (mNumDays == 7) {
-            adjustToBeginningOfWeek(mBaseDate);
-        }
-
-        final long start = mBaseDate.toMillis(false /* use isDst */);
-        mFirstJulianDay = Time.getJulianDay(start, mBaseDate.gmtoff);
-        mLastJulianDay = mFirstJulianDay + mNumDays - 1;
-
-        mMonthLength = mBaseDate.getActualMaximum(Time.MONTH_DAY);
-        mFirstVisibleDate = mBaseDate.monthDay;
-        mFirstVisibleDayOfWeek = mBaseDate.weekDay;
-    }
-
-    private void adjustToBeginningOfWeek(Time time) {
-        int dayOfWeek = time.weekDay;
-        int diff = dayOfWeek - mFirstDayOfWeek;
-        if (diff != 0) {
-            if (diff < 0) {
-                diff += 7;
-            }
-            time.monthDay -= diff;
-            time.normalize(true /* ignore isDst */);
-        }
-    }
-
-    @Override
-    protected void onSizeChanged(int width, int height, int oldw, int oldh) {
-        mViewWidth = width;
-        mViewHeight = height;
-        mEdgeEffectTop.setSize(mViewWidth, mViewHeight);
-        mEdgeEffectBottom.setSize(mViewWidth, mViewHeight);
-        int gridAreaWidth = width - mHoursWidth;
-        mCellWidth = (gridAreaWidth - (mNumDays * DAY_GAP)) / mNumDays;
-
-        // This would be about 1 day worth in a 7 day view
-        mHorizontalSnapBackThreshold = width / 7;
-
-        Paint p = new Paint();
-        p.setTextSize(HOURS_TEXT_SIZE);
-        mHoursTextHeight = (int) Math.abs(p.ascent());
-        remeasure(width, height);
-    }
-
-    /**
-     * Measures the space needed for various parts of the view after
-     * loading new events.  This can change if there are all-day events.
-     */
-    private void remeasure(int width, int height) {
-        // Shrink to fit available space but make sure we can display at least two events
-        MAX_UNEXPANDED_ALLDAY_HEIGHT = (int) (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4);
-        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6);
-        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(MAX_UNEXPANDED_ALLDAY_HEIGHT,
-                (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 2);
-        mMaxUnexpandedAlldayEventCount =
-                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
-
-        // First, clear the array of earliest start times, and the array
-        // indicating presence of an all-day event.
-        for (int day = 0; day < mNumDays; day++) {
-            mEarliestStartHour[day] = 25;  // some big number
-            mHasAllDayEvent[day] = false;
-        }
-
-        int maxAllDayEvents = mMaxAlldayEvents;
-
-        // The min is where 24 hours cover the entire visible area
-        mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, (int) MIN_EVENT_HEIGHT);
-        if (mCellHeight < mMinCellHeight) {
-            mCellHeight = mMinCellHeight;
-        }
-
-        // Calculate mAllDayHeight
-        mFirstCell = DAY_HEADER_HEIGHT;
-        int allDayHeight = 0;
-        if (maxAllDayEvents > 0) {
-            int maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
-            // If there is at most one all-day event per day, then use less
-            // space (but more than the space for a single event).
-            if (maxAllDayEvents == 1) {
-                allDayHeight = SINGLE_ALLDAY_HEIGHT;
-            } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount){
-                // Allow the all-day area to grow in height depending on the
-                // number of all-day events we need to show, up to a limit.
-                allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
-                if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
-                    allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT;
-                }
-            } else {
-                // if we have more than the magic number, check if we're animating
-                // and if not adjust the sizes appropriately
-                if (mAnimateDayHeight != 0) {
-                    // Don't shrink the space past the final allDay space. The animation
-                    // continues to hide the last event so the more events text can
-                    // fade in.
-                    allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT);
-                } else {
-                    // Try to fit all the events in
-                    allDayHeight = (int) (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
-                    // But clip the area depending on which mode we're in
-                    if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
-                        allDayHeight = (int) (mMaxUnexpandedAlldayEventCount *
-                                MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT);
-                    } else if (allDayHeight > maxAllAllDayHeight) {
-                        allDayHeight = maxAllAllDayHeight;
-                    }
-                }
-            }
-            mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN;
-        } else {
-            mSelectionAllday = false;
-        }
-        mAlldayHeight = allDayHeight;
-
-        mGridAreaHeight = height - mFirstCell;
-
-        // Set up the expand icon position
-        int allDayIconWidth = mExpandAlldayDrawable.getIntrinsicWidth();
-        mExpandAllDayRect.left = Math.max((mHoursWidth - allDayIconWidth) / 2,
-                EVENT_ALL_DAY_TEXT_LEFT_MARGIN);
-        mExpandAllDayRect.right = Math.min(mExpandAllDayRect.left + allDayIconWidth, mHoursWidth
-                - EVENT_ALL_DAY_TEXT_RIGHT_MARGIN);
-        mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN;
-        mExpandAllDayRect.top = mExpandAllDayRect.bottom
-                - mExpandAlldayDrawable.getIntrinsicHeight();
-
-        mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP);
-        mEventGeometry.setHourHeight(mCellHeight);
-
-        final long minimumDurationMillis = (long)
-                (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f));
-        Event.computePositions(mEvents, minimumDurationMillis);
-
-        // Compute the top of our reachable view
-        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
-        if (DEBUG) {
-            Log.e(TAG, "mViewStartY: " + mViewStartY);
-            Log.e(TAG, "mMaxViewStartY: " + mMaxViewStartY);
-        }
-        if (mViewStartY > mMaxViewStartY) {
-            mViewStartY = mMaxViewStartY;
-            computeFirstHour();
-        }
-
-        if (mFirstHour == -1) {
-            initFirstHour();
-            mFirstHourOffset = 0;
-        }
-
-        // When we change the base date, the number of all-day events may
-        // change and that changes the cell height.  When we switch dates,
-        // we use the mFirstHourOffset from the previous view, but that may
-        // be too large for the new view if the cell height is smaller.
-        if (mFirstHourOffset >= mCellHeight + HOUR_GAP) {
-            mFirstHourOffset = mCellHeight + HOUR_GAP - 1;
-        }
-        mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset;
-
-        final int eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP);
-        //When we get new events we don't want to dismiss the popup unless the event changes
-        if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent.id) {
-            mPopup.dismiss();
-        }
-        mPopup.setWidth(eventAreaWidth - 20);
-        mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
-    }
-
-    /**
-     * Initialize the state for another view.  The given view is one that has
-     * its own bitmap and will use an animation to replace the current view.
-     * The current view and new view are either both Week views or both Day
-     * views.  They differ in their base date.
-     *
-     * @param view the view to initialize.
-     */
-    private void initView(DayView view) {
-        view.setSelectedHour(mSelectionHour);
-        view.mSelectedEvents.clear();
-        view.mComputeSelectedEvents = true;
-        view.mFirstHour = mFirstHour;
-        view.mFirstHourOffset = mFirstHourOffset;
-        view.remeasure(getWidth(), getHeight());
-        view.initAllDayHeights();
-
-        view.setSelectedEvent(null);
-        view.mPrevSelectedEvent = null;
-        view.mFirstDayOfWeek = mFirstDayOfWeek;
-        if (view.mEvents.size() > 0) {
-            view.mSelectionAllday = mSelectionAllday;
-        } else {
-            view.mSelectionAllday = false;
-        }
-
-        // Redraw the screen so that the selection box will be redrawn.  We may
-        // have scrolled to a different part of the day in some other view
-        // so the selection box in this view may no longer be visible.
-        view.recalc();
-    }
-
-    /**
-     * Switch to another view based on what was selected (an event or a free
-     * slot) and how it was selected (by touch or by trackball).
-     *
-     * @param trackBallSelection true if the selection was made using the
-     * trackball.
-     */
-    private void switchViews(boolean trackBallSelection) {
-        Event selectedEvent = mSelectedEvent;
-
-        mPopup.dismiss();
-        mLastPopupEventID = INVALID_EVENT_ID;
-        if (mNumDays > 1) {
-            // This is the Week view.
-            // With touch, we always switch to Day/Agenda View
-            // With track ball, if we selected a free slot, then create an event.
-            // If we selected a specific event, switch to EventInfo view.
-            if (trackBallSelection) {
-                if (selectedEvent != null) {
-                    if (mIsAccessibilityEnabled) {
-                        mAccessibilityMgr.interrupt();
-                    }
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        mScrolling = false;
-        return super.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        return super.onKeyDown(keyCode, event);
-    }
-
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        return true;
-    }
-
-    private boolean isTouchExplorationEnabled() {
-        return mIsAccessibilityEnabled && mAccessibilityMgr.isTouchExplorationEnabled();
-    }
-
-    private void sendAccessibilityEventAsNeeded(boolean speakEvents) {
-        if (!mIsAccessibilityEnabled) {
-            return;
-        }
-        boolean dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility;
-        boolean hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility;
-        if (dayChanged || hourChanged ||
-                mLastSelectedEventForAccessibility != mSelectedEventForAccessibility) {
-            mLastSelectionDayForAccessibility = mSelectionDayForAccessibility;
-            mLastSelectionHourForAccessibility = mSelectionHourForAccessibility;
-            mLastSelectedEventForAccessibility = mSelectedEventForAccessibility;
-
-            StringBuilder b = new StringBuilder();
-
-            // Announce only the changes i.e. day or hour or both
-            if (dayChanged) {
-                b.append(getSelectedTimeForAccessibility().format("%A "));
-            }
-            if (hourChanged) {
-                b.append(getSelectedTimeForAccessibility().format(mIs24HourFormat ? "%k" : "%l%p"));
-            }
-            if (dayChanged || hourChanged) {
-                b.append(PERIOD_SPACE);
-            }
-
-            if (speakEvents) {
-                if (mEventCountTemplate == null) {
-                    mEventCountTemplate = mContext.getString(R.string.template_announce_item_index);
-                }
-
-                // Read out the relevant event(s)
-                int numEvents = mSelectedEvents.size();
-                if (numEvents > 0) {
-                    if (mSelectedEventForAccessibility == null) {
-                        // Read out all the events
-                        int i = 1;
-                        for (Event calEvent : mSelectedEvents) {
-                            if (numEvents > 1) {
-                                // Read out x of numEvents if there are more than one event
-                                mStringBuilder.setLength(0);
-                                b.append(mFormatter.format(mEventCountTemplate, i++, numEvents));
-                                b.append(" ");
-                            }
-                            appendEventAccessibilityString(b, calEvent);
-                        }
-                    } else {
-                        if (numEvents > 1) {
-                            // Read out x of numEvents if there are more than one event
-                            mStringBuilder.setLength(0);
-                            b.append(mFormatter.format(mEventCountTemplate, mSelectedEvents
-                                    .indexOf(mSelectedEventForAccessibility) + 1, numEvents));
-                            b.append(" ");
-                        }
-                        appendEventAccessibilityString(b, mSelectedEventForAccessibility);
-                    }
-                }
-            }
-
-            if (dayChanged || hourChanged || speakEvents) {
-                AccessibilityEvent event = AccessibilityEvent
-                        .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
-                CharSequence msg = b.toString();
-                event.getText().add(msg);
-                event.setAddedCount(msg.length());
-                sendAccessibilityEventUnchecked(event);
-            }
-        }
-    }
-
-    /**
-     * @param b
-     * @param calEvent
-     */
-    private void appendEventAccessibilityString(StringBuilder b, Event calEvent) {
-        b.append(calEvent.getTitleAndLocation());
-        b.append(PERIOD_SPACE);
-        String when;
-        int flags = DateUtils.FORMAT_SHOW_DATE;
-        if (calEvent.allDay) {
-            flags |= DateUtils.FORMAT_UTC | DateUtils.FORMAT_SHOW_WEEKDAY;
-        } else {
-            flags |= DateUtils.FORMAT_SHOW_TIME;
-            if (DateFormat.is24HourFormat(mContext)) {
-                flags |= DateUtils.FORMAT_24HOUR;
-            }
-        }
-        when = Utils.formatDateRange(mContext, calEvent.startMillis, calEvent.endMillis, flags);
-        b.append(when);
-        b.append(PERIOD_SPACE);
-    }
-
-    private class GotoBroadcaster implements Animation.AnimationListener {
-        private final int mCounter;
-        private final Time mStart;
-        private final Time mEnd;
-
-        public GotoBroadcaster(Time start, Time end) {
-            mCounter = ++sCounter;
-            mStart = start;
-            mEnd = end;
-        }
-
-        @Override
-        public void onAnimationEnd(Animation animation) {
-            DayView view = (DayView) mViewSwitcher.getCurrentView();
-            view.mViewStartX = 0;
-            view = (DayView) mViewSwitcher.getNextView();
-            view.mViewStartX = 0;
-
-            if (mCounter == sCounter) {
-                mController.sendEvent(this, EventType.GO_TO, mStart, mEnd, null, -1,
-                        ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null);
-            }
-        }
-
-        @Override
-        public void onAnimationRepeat(Animation animation) {
-        }
-
-        @Override
-        public void onAnimationStart(Animation animation) {
-        }
-    }
-
-    private View switchViews(boolean forward, float xOffSet, float width, float velocity) {
-        mAnimationDistance = width - xOffSet;
-        if (DEBUG) {
-            Log.d(TAG, "switchViews(" + forward + ") O:" + xOffSet + " Dist:" + mAnimationDistance);
-        }
-
-        float progress = Math.abs(xOffSet) / width;
-        if (progress > 1.0f) {
-            progress = 1.0f;
-        }
-
-        float inFromXValue, inToXValue;
-        float outFromXValue, outToXValue;
-        if (forward) {
-            inFromXValue = 1.0f - progress;
-            inToXValue = 0.0f;
-            outFromXValue = -progress;
-            outToXValue = -1.0f;
-        } else {
-            inFromXValue = progress - 1.0f;
-            inToXValue = 0.0f;
-            outFromXValue = progress;
-            outToXValue = 1.0f;
-        }
-
-        final Time start = new Time(mBaseDate.timezone);
-        start.set(mController.getTime());
-        if (forward) {
-            start.monthDay += mNumDays;
-        } else {
-            start.monthDay -= mNumDays;
-        }
-        mController.setTime(start.normalize(true));
-
-        Time newSelected = start;
-
-        if (mNumDays == 7) {
-            newSelected = new Time(start);
-            adjustToBeginningOfWeek(start);
-        }
-
-        final Time end = new Time(start);
-        end.monthDay += mNumDays - 1;
-
-        // We have to allocate these animation objects each time we switch views
-        // because that is the only way to set the animation parameters.
-        TranslateAnimation inAnimation = new TranslateAnimation(
-                Animation.RELATIVE_TO_SELF, inFromXValue,
-                Animation.RELATIVE_TO_SELF, inToXValue,
-                Animation.ABSOLUTE, 0.0f,
-                Animation.ABSOLUTE, 0.0f);
-
-        TranslateAnimation outAnimation = new TranslateAnimation(
-                Animation.RELATIVE_TO_SELF, outFromXValue,
-                Animation.RELATIVE_TO_SELF, outToXValue,
-                Animation.ABSOLUTE, 0.0f,
-                Animation.ABSOLUTE, 0.0f);
-
-        long duration = calculateDuration(width - Math.abs(xOffSet), width, velocity);
-        inAnimation.setDuration(duration);
-        inAnimation.setInterpolator(mHScrollInterpolator);
-        outAnimation.setInterpolator(mHScrollInterpolator);
-        outAnimation.setDuration(duration);
-        outAnimation.setAnimationListener(new GotoBroadcaster(start, end));
-        mViewSwitcher.setInAnimation(inAnimation);
-        mViewSwitcher.setOutAnimation(outAnimation);
-
-        DayView view = (DayView) mViewSwitcher.getCurrentView();
-        view.cleanup();
-        mViewSwitcher.showNext();
-        view = (DayView) mViewSwitcher.getCurrentView();
-        view.setSelected(newSelected, true, false);
-        view.requestFocus();
-        view.reloadEvents();
-        view.updateTitle();
-        view.restartCurrentTimeUpdates();
-
-        return view;
-    }
-
-    // This is called after scrolling stops to move the selected hour
-    // to the visible part of the screen.
-    private void resetSelectedHour() {
-        if (mSelectionHour < mFirstHour + 1) {
-            setSelectedHour(mFirstHour + 1);
-            setSelectedEvent(null);
-            mSelectedEvents.clear();
-            mComputeSelectedEvents = true;
-        } else if (mSelectionHour > mFirstHour + mNumHours - 3) {
-            setSelectedHour(mFirstHour + mNumHours - 3);
-            setSelectedEvent(null);
-            mSelectedEvents.clear();
-            mComputeSelectedEvents = true;
-        }
-    }
-
-    private void initFirstHour() {
-        mFirstHour = mSelectionHour - mNumHours / 5;
-        if (mFirstHour < 0) {
-            mFirstHour = 0;
-        } else if (mFirstHour + mNumHours > 24) {
-            mFirstHour = 24 - mNumHours;
-        }
-    }
-
-    /**
-     * Recomputes the first full hour that is visible on screen after the
-     * screen is scrolled.
-     */
-    private void computeFirstHour() {
-        // Compute the first full hour that is visible on screen
-        mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP);
-        mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY;
-    }
-
-    private void adjustHourSelection() {
-        if (mSelectionHour < 0) {
-            setSelectedHour(0);
-            if (mMaxAlldayEvents > 0) {
-                mPrevSelectedEvent = null;
-                mSelectionAllday = true;
-            }
-        }
-
-        if (mSelectionHour > 23) {
-            setSelectedHour(23);
-        }
-
-        // If the selected hour is at least 2 time slots from the top and
-        // bottom of the screen, then don't scroll the view.
-        if (mSelectionHour < mFirstHour + 1) {
-            // If there are all-days events for the selected day but there
-            // are no more normal events earlier in the day, then jump to
-            // the all-day event area.
-            // Exception 1: allow the user to scroll to 8am with the trackball
-            // before jumping to the all-day event area.
-            // Exception 2: if 12am is on screen, then allow the user to select
-            // 12am before going up to the all-day event area.
-            int daynum = mSelectionDay - mFirstJulianDay;
-            if (daynum < mEarliestStartHour.length && daynum >= 0
-                    && mMaxAlldayEvents > 0
-                    && mEarliestStartHour[daynum] > mSelectionHour
-                    && mFirstHour > 0 && mFirstHour < 8) {
-                mPrevSelectedEvent = null;
-                mSelectionAllday = true;
-                setSelectedHour(mFirstHour + 1);
-                return;
-            }
-
-            if (mFirstHour > 0) {
-                mFirstHour -= 1;
-                mViewStartY -= (mCellHeight + HOUR_GAP);
-                if (mViewStartY < 0) {
-                    mViewStartY = 0;
-                }
-                return;
-            }
-        }
-
-        if (mSelectionHour > mFirstHour + mNumHours - 3) {
-            if (mFirstHour < 24 - mNumHours) {
-                mFirstHour += 1;
-                mViewStartY += (mCellHeight + HOUR_GAP);
-                if (mViewStartY > mMaxViewStartY) {
-                    mViewStartY = mMaxViewStartY;
-                }
-                return;
-            } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) {
-                mViewStartY = mMaxViewStartY;
-            }
-        }
-    }
-
-    void clearCachedEvents() {
-        mLastReloadMillis = 0;
-    }
-
-    private final Runnable mCancelCallback = new Runnable() {
-        public void run() {
-            clearCachedEvents();
-        }
-    };
-
-    /* package */ void reloadEvents() {
-        // Protect against this being called before this view has been
-        // initialized.
-//        if (mContext == null) {
-//            return;
-//        }
-
-        // Make sure our time zones are up to date
-        mTZUpdater.run();
-
-        setSelectedEvent(null);
-        mPrevSelectedEvent = null;
-        mSelectedEvents.clear();
-
-        // The start date is the beginning of the week at 12am
-        Time weekStart = new Time(Utils.getTimeZone(mContext, mTZUpdater));
-        weekStart.set(mBaseDate);
-        weekStart.hour = 0;
-        weekStart.minute = 0;
-        weekStart.second = 0;
-        long millis = weekStart.normalize(true /* ignore isDst */);
-
-        // Avoid reloading events unnecessarily.
-        if (millis == mLastReloadMillis) {
-            return;
-        }
-        mLastReloadMillis = millis;
-
-        // load events in the background
-//        mContext.startProgressSpinner();
-        final ArrayList<Event> events = new ArrayList<Event>();
-        mEventLoader.loadEventsInBackground(mNumDays, events, mFirstJulianDay, new Runnable() {
-
-            public void run() {
-                boolean fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay;
-                mEvents = events;
-                mLoadedFirstJulianDay = mFirstJulianDay;
-                if (mAllDayEvents == null) {
-                    mAllDayEvents = new ArrayList<Event>();
-                } else {
-                    mAllDayEvents.clear();
-                }
-
-                // Create a shorter array for all day events
-                for (Event e : events) {
-                    if (e.drawAsAllday()) {
-                        mAllDayEvents.add(e);
-                    }
-                }
-
-                // New events, new layouts
-                if (mLayouts == null || mLayouts.length < events.size()) {
-                    mLayouts = new StaticLayout[events.size()];
-                } else {
-                    Arrays.fill(mLayouts, null);
-                }
-
-                if (mAllDayLayouts == null || mAllDayLayouts.length < mAllDayEvents.size()) {
-                    mAllDayLayouts = new StaticLayout[events.size()];
-                } else {
-                    Arrays.fill(mAllDayLayouts, null);
-                }
-
-                computeEventRelations();
-
-                mRemeasure = true;
-                mComputeSelectedEvents = true;
-                recalc();
-
-                // Start animation to cross fade the events
-                if (fadeinEvents) {
-                    if (mEventsCrossFadeAnimation == null) {
-                        mEventsCrossFadeAnimation =
-                                ObjectAnimator.ofInt(DayView.this, "EventsAlpha", 0, 255);
-                        mEventsCrossFadeAnimation.setDuration(EVENTS_CROSS_FADE_DURATION);
-                    }
-                    mEventsCrossFadeAnimation.start();
-                } else{
-                    invalidate();
-                }
-            }
-        }, mCancelCallback);
-    }
-
-    public void setEventsAlpha(int alpha) {
-        mEventsAlpha = alpha;
-        invalidate();
-    }
-
-    public int getEventsAlpha() {
-        return mEventsAlpha;
-    }
-
-    public void stopEventsAnimation() {
-        if (mEventsCrossFadeAnimation != null) {
-            mEventsCrossFadeAnimation.cancel();
-        }
-        mEventsAlpha = 255;
-    }
-
-    private void computeEventRelations() {
-        // Compute the layout relation between each event before measuring cell
-        // width, as the cell width should be adjusted along with the relation.
-        //
-        // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm)
-        // We should mark them as "overwapped". Though they are not overwapped logically, but
-        // minimum cell height implicitly expands the cell height of A and it should look like
-        // (1:00pm - 1:15pm) after the cell height adjustment.
-
-        // Compute the space needed for the all-day events, if any.
-        // Make a pass over all the events, and keep track of the maximum
-        // number of all-day events in any one day.  Also, keep track of
-        // the earliest event in each day.
-        int maxAllDayEvents = 0;
-        final ArrayList<Event> events = mEvents;
-        final int len = events.size();
-        // Num of all-day-events on each day.
-        final int eventsCount[] = new int[mLastJulianDay - mFirstJulianDay + 1];
-        Arrays.fill(eventsCount, 0);
-        for (int ii = 0; ii < len; ii++) {
-            Event event = events.get(ii);
-            if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) {
-                continue;
-            }
-            if (event.drawAsAllday()) {
-                // Count all the events being drawn as allDay events
-                final int firstDay = Math.max(event.startDay, mFirstJulianDay);
-                final int lastDay = Math.min(event.endDay, mLastJulianDay);
-                for (int day = firstDay; day <= lastDay; day++) {
-                    final int count = ++eventsCount[day - mFirstJulianDay];
-                    if (maxAllDayEvents < count) {
-                        maxAllDayEvents = count;
-                    }
-                }
-
-                int daynum = event.startDay - mFirstJulianDay;
-                int durationDays = event.endDay - event.startDay + 1;
-                if (daynum < 0) {
-                    durationDays += daynum;
-                    daynum = 0;
-                }
-                if (daynum + durationDays > mNumDays) {
-                    durationDays = mNumDays - daynum;
-                }
-                for (int day = daynum; durationDays > 0; day++, durationDays--) {
-                    mHasAllDayEvent[day] = true;
-                }
-            } else {
-                int daynum = event.startDay - mFirstJulianDay;
-                int hour = event.startTime / 60;
-                if (daynum >= 0 && hour < mEarliestStartHour[daynum]) {
-                    mEarliestStartHour[daynum] = hour;
-                }
-
-                // Also check the end hour in case the event spans more than
-                // one day.
-                daynum = event.endDay - mFirstJulianDay;
-                hour = event.endTime / 60;
-                if (daynum < mNumDays && hour < mEarliestStartHour[daynum]) {
-                    mEarliestStartHour[daynum] = hour;
-                }
-            }
-        }
-        mMaxAlldayEvents = maxAllDayEvents;
-        initAllDayHeights();
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mRemeasure) {
-            remeasure(getWidth(), getHeight());
-            mRemeasure = false;
-        }
-        canvas.save();
-
-        float yTranslate = -mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight;
-        // offset canvas by the current drag and header position
-        canvas.translate(-mViewStartX, yTranslate);
-        // clip to everything below the allDay area
-        Rect dest = mDestRect;
-        dest.top = (int) (mFirstCell - yTranslate);
-        dest.bottom = (int) (mViewHeight - yTranslate);
-        dest.left = 0;
-        dest.right = mViewWidth;
-        canvas.save();
-        canvas.clipRect(dest);
-        // Draw the movable part of the view
-        doDraw(canvas);
-        // restore to having no clip
-        canvas.restore();
-
-        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
-            float xTranslate;
-            if (mViewStartX > 0) {
-                xTranslate = mViewWidth;
-            } else {
-                xTranslate = -mViewWidth;
-            }
-            // Move the canvas around to prep it for the next view
-            // specifically, shift it by a screen and undo the
-            // yTranslation which will be redone in the nextView's onDraw().
-            canvas.translate(xTranslate, -yTranslate);
-            DayView nextView = (DayView) mViewSwitcher.getNextView();
-
-            // Prevent infinite recursive calls to onDraw().
-            nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE;
-
-            nextView.onDraw(canvas);
-            // Move it back for this view
-            canvas.translate(-xTranslate, 0);
-        } else {
-            // If we drew another view we already translated it back
-            // If we didn't draw another view we should be at the edge of the
-            // screen
-            canvas.translate(mViewStartX, -yTranslate);
-        }
-
-        // Draw the fixed areas (that don't scroll) directly to the canvas.
-        drawAfterScroll(canvas);
-        if (mComputeSelectedEvents && mUpdateToast) {
-            mUpdateToast = false;
-        }
-        mComputeSelectedEvents = false;
-
-        // Draw overscroll glow
-        if (!mEdgeEffectTop.isFinished()) {
-            if (DAY_HEADER_HEIGHT != 0) {
-                canvas.translate(0, DAY_HEADER_HEIGHT);
-            }
-            if (mEdgeEffectTop.draw(canvas)) {
-                invalidate();
-            }
-            if (DAY_HEADER_HEIGHT != 0) {
-                canvas.translate(0, -DAY_HEADER_HEIGHT);
-            }
-        }
-        if (!mEdgeEffectBottom.isFinished()) {
-            canvas.rotate(180, mViewWidth/2, mViewHeight/2);
-            if (mEdgeEffectBottom.draw(canvas)) {
-                invalidate();
-            }
-        }
-        canvas.restore();
-    }
-
-    private void drawAfterScroll(Canvas canvas) {
-        Paint p = mPaint;
-        Rect r = mRect;
-
-        drawAllDayHighlights(r, canvas, p);
-        if (mMaxAlldayEvents != 0) {
-            drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p);
-            drawUpperLeftCorner(r, canvas, p);
-        }
-
-        drawScrollLine(r, canvas, p);
-        drawDayHeaderLoop(r, canvas, p);
-
-        // Draw the AM and PM indicators if we're in 12 hour mode
-        if (!mIs24HourFormat) {
-            drawAmPm(canvas, p);
-        }
-    }
-
-    // This isn't really the upper-left corner. It's the square area just
-    // below the upper-left corner, above the hours and to the left of the
-    // all-day area.
-    private void drawUpperLeftCorner(Rect r, Canvas canvas, Paint p) {
-        setupHourTextPaint(p);
-        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
-            // Draw the allDay expand/collapse icon
-            if (mUseExpandIcon) {
-                mExpandAlldayDrawable.setBounds(mExpandAllDayRect);
-                mExpandAlldayDrawable.draw(canvas);
-            } else {
-                mCollapseAlldayDrawable.setBounds(mExpandAllDayRect);
-                mCollapseAlldayDrawable.draw(canvas);
-            }
-        }
-    }
-
-    private void drawScrollLine(Rect r, Canvas canvas, Paint p) {
-        final int right = computeDayLeftPosition(mNumDays);
-        final int y = mFirstCell - 1;
-
-        p.setAntiAlias(false);
-        p.setStyle(Style.FILL);
-
-        p.setColor(mCalendarGridLineInnerHorizontalColor);
-        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
-        canvas.drawLine(GRID_LINE_LEFT_MARGIN, y, right, y, p);
-        p.setAntiAlias(true);
-    }
-
-    // Computes the x position for the left side of the given day (base 0)
-    private int computeDayLeftPosition(int day) {
-        int effectiveWidth = mViewWidth - mHoursWidth;
-        return day * effectiveWidth / mNumDays + mHoursWidth;
-    }
-
-    private void drawAllDayHighlights(Rect r, Canvas canvas, Paint p) {
-        if (mFutureBgColor != 0) {
-            // First, color the labels area light gray
-            r.top = 0;
-            r.bottom = DAY_HEADER_HEIGHT;
-            r.left = 0;
-            r.right = mViewWidth;
-            p.setColor(mBgColor);
-            p.setStyle(Style.FILL);
-            canvas.drawRect(r, p);
-            // and the area that says All day
-            r.top = DAY_HEADER_HEIGHT;
-            r.bottom = mFirstCell - 1;
-            r.left = 0;
-            r.right = mHoursWidth;
-            canvas.drawRect(r, p);
-
-            int startIndex = -1;
-
-            int todayIndex = mTodayJulianDay - mFirstJulianDay;
-            if (todayIndex < 0) {
-                // Future
-                startIndex = 0;
-            } else if (todayIndex >= 1 && todayIndex + 1 < mNumDays) {
-                // Multiday - tomorrow is visible.
-                startIndex = todayIndex + 1;
-            }
-
-            if (startIndex >= 0) {
-                // Draw the future highlight
-                r.top = 0;
-                r.bottom = mFirstCell - 1;
-                r.left = computeDayLeftPosition(startIndex) + 1;
-                r.right = computeDayLeftPosition(mNumDays);
-                p.setColor(mFutureBgColor);
-                p.setStyle(Style.FILL);
-                canvas.drawRect(r, p);
-            }
-        }
-    }
-
-    private void drawDayHeaderLoop(Rect r, Canvas canvas, Paint p) {
-        // Draw the horizontal day background banner
-        // p.setColor(mCalendarDateBannerBackground);
-        // r.top = 0;
-        // r.bottom = DAY_HEADER_HEIGHT;
-        // r.left = 0;
-        // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP);
-        // canvas.drawRect(r, p);
-        //
-        // Fill the extra space on the right side with the default background
-        // r.left = r.right;
-        // r.right = mViewWidth;
-        // p.setColor(mCalendarGridAreaBackground);
-        // canvas.drawRect(r, p);
-        if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) {
-            return;
-        }
-
-        p.setTypeface(mBold);
-        p.setTextAlign(Paint.Align.RIGHT);
-        int cell = mFirstJulianDay;
-
-        String[] dayNames;
-        if (mDateStrWidth < mCellWidth) {
-            dayNames = mDayStrs;
-        } else {
-            dayNames = mDayStrs2Letter;
-        }
-
-        p.setAntiAlias(true);
-        for (int day = 0; day < mNumDays; day++, cell++) {
-            int dayOfWeek = day + mFirstVisibleDayOfWeek;
-            if (dayOfWeek >= 14) {
-                dayOfWeek -= 14;
-            }
-
-            int color = mCalendarDateBannerTextColor;
-            if (mNumDays == 1) {
-                if (dayOfWeek == Time.SATURDAY) {
-                    color = mWeek_saturdayColor;
-                } else if (dayOfWeek == Time.SUNDAY) {
-                    color = mWeek_sundayColor;
-                }
-            } else {
-                final int column = day % 7;
-                if (Utils.isSaturday(column, mFirstDayOfWeek)) {
-                    color = mWeek_saturdayColor;
-                } else if (Utils.isSunday(column, mFirstDayOfWeek)) {
-                    color = mWeek_sundayColor;
-                }
-            }
-
-            p.setColor(color);
-            drawDayHeader(dayNames[dayOfWeek], day, cell, canvas, p);
-        }
-        p.setTypeface(null);
-    }
-
-    private void drawAmPm(Canvas canvas, Paint p) {
-        p.setColor(mCalendarAmPmLabel);
-        p.setTextSize(AMPM_TEXT_SIZE);
-        p.setTypeface(mBold);
-        p.setAntiAlias(true);
-        p.setTextAlign(Paint.Align.RIGHT);
-        String text = mAmString;
-        if (mFirstHour >= 12) {
-            text = mPmString;
-        }
-        int y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP;
-        canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
-
-        if (mFirstHour < 12 && mFirstHour + mNumHours > 12) {
-            // Also draw the "PM"
-            text = mPmString;
-            y = mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP)
-                    + 2 * mHoursTextHeight + HOUR_GAP;
-            canvas.drawText(text, HOURS_LEFT_MARGIN, y, p);
-        }
-    }
-
-    private void drawCurrentTimeLine(Rect r, final int day, final int top, Canvas canvas,
-            Paint p) {
-        r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1;
-        r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1;
-
-        r.top = top - CURRENT_TIME_LINE_TOP_OFFSET;
-        r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight();
-
-        mCurrentTimeLine.setBounds(r);
-        mCurrentTimeLine.draw(canvas);
-        if (mAnimateToday) {
-            mCurrentTimeAnimateLine.setBounds(r);
-            mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha);
-            mCurrentTimeAnimateLine.draw(canvas);
-        }
-    }
-
-    private void doDraw(Canvas canvas) {
-        Paint p = mPaint;
-        Rect r = mRect;
-
-        if (mFutureBgColor != 0) {
-            drawBgColors(r, canvas, p);
-        }
-        drawGridBackground(r, canvas, p);
-        drawHours(r, canvas, p);
-
-        // Draw each day
-        int cell = mFirstJulianDay;
-        p.setAntiAlias(false);
-        int alpha = p.getAlpha();
-        p.setAlpha(mEventsAlpha);
-        for (int day = 0; day < mNumDays; day++, cell++) {
-            // TODO Wow, this needs cleanup. drawEvents loop through all the
-            // events on every call.
-            drawEvents(cell, day, HOUR_GAP, canvas, p);
-            // If this is today
-            if (cell == mTodayJulianDay) {
-                int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
-                        + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
-
-                // And the current time shows up somewhere on the screen
-                if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) {
-                    drawCurrentTimeLine(r, day, lineY, canvas, p);
-                }
-            }
-        }
-        p.setAntiAlias(true);
-        p.setAlpha(alpha);
-    }
-
-    private void drawHours(Rect r, Canvas canvas, Paint p) {
-        setupHourTextPaint(p);
-
-        int y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN;
-
-        for (int i = 0; i < 24; i++) {
-            String time = mHourStrs[i];
-            canvas.drawText(time, HOURS_LEFT_MARGIN, y, p);
-            y += mCellHeight + HOUR_GAP;
-        }
-    }
-
-    private void setupHourTextPaint(Paint p) {
-        p.setColor(mCalendarHourLabelColor);
-        p.setTextSize(HOURS_TEXT_SIZE);
-        p.setTypeface(Typeface.DEFAULT);
-        p.setTextAlign(Paint.Align.RIGHT);
-        p.setAntiAlias(true);
-    }
-
-    private void drawDayHeader(String dayStr, int day, int cell, Canvas canvas, Paint p) {
-        int dateNum = mFirstVisibleDate + day;
-        int x;
-        if (dateNum > mMonthLength) {
-            dateNum -= mMonthLength;
-        }
-        p.setAntiAlias(true);
-
-        int todayIndex = mTodayJulianDay - mFirstJulianDay;
-        // Draw day of the month
-        String dateNumStr = String.valueOf(dateNum);
-        if (mNumDays > 1) {
-            float y = DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN;
-
-            // Draw day of the month
-            x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN;
-            p.setTextAlign(Align.RIGHT);
-            p.setTextSize(DATE_HEADER_FONT_SIZE);
-
-            p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT);
-            canvas.drawText(dateNumStr, x, y, p);
-
-            // Draw day of the week
-            x -= p.measureText(" " + dateNumStr);
-            p.setTextSize(DAY_HEADER_FONT_SIZE);
-            p.setTypeface(Typeface.DEFAULT);
-            canvas.drawText(dayStr, x, y, p);
-        } else {
-            float y = ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN;
-            p.setTextAlign(Align.LEFT);
-
-
-            // Draw day of the week
-            x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN;
-            p.setTextSize(DAY_HEADER_FONT_SIZE);
-            p.setTypeface(Typeface.DEFAULT);
-            canvas.drawText(dayStr, x, y, p);
-
-            // Draw day of the month
-            x += p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN;
-            p.setTextSize(DATE_HEADER_FONT_SIZE);
-            p.setTypeface(todayIndex == day ? mBold : Typeface.DEFAULT);
-            canvas.drawText(dateNumStr, x, y, p);
-        }
-    }
-
-    private void drawGridBackground(Rect r, Canvas canvas, Paint p) {
-        Paint.Style savedStyle = p.getStyle();
-
-        final float stopX = computeDayLeftPosition(mNumDays);
-        float y = 0;
-        final float deltaY = mCellHeight + HOUR_GAP;
-        int linesIndex = 0;
-        final float startY = 0;
-        final float stopY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP);
-        float x = mHoursWidth;
-
-        // Draw the inner horizontal grid lines
-        p.setColor(mCalendarGridLineInnerHorizontalColor);
-        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
-        p.setAntiAlias(false);
-        y = 0;
-        linesIndex = 0;
-        for (int hour = 0; hour <= 24; hour++) {
-            mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
-            mLines[linesIndex++] = y;
-            mLines[linesIndex++] = stopX;
-            mLines[linesIndex++] = y;
-            y += deltaY;
-        }
-        if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) {
-            canvas.drawLines(mLines, 0, linesIndex, p);
-            linesIndex = 0;
-            p.setColor(mCalendarGridLineInnerVerticalColor);
-        }
-
-        // Draw the inner vertical grid lines
-        for (int day = 0; day <= mNumDays; day++) {
-            x = computeDayLeftPosition(day);
-            mLines[linesIndex++] = x;
-            mLines[linesIndex++] = startY;
-            mLines[linesIndex++] = x;
-            mLines[linesIndex++] = stopY;
-        }
-        canvas.drawLines(mLines, 0, linesIndex, p);
-
-        // Restore the saved style.
-        p.setStyle(savedStyle);
-        p.setAntiAlias(true);
-    }
-
-    /**
-     * @param r
-     * @param canvas
-     * @param p
-     */
-    private void drawBgColors(Rect r, Canvas canvas, Paint p) {
-        int todayIndex = mTodayJulianDay - mFirstJulianDay;
-        // Draw the hours background color
-        r.top = mDestRect.top;
-        r.bottom = mDestRect.bottom;
-        r.left = 0;
-        r.right = mHoursWidth;
-        p.setColor(mBgColor);
-        p.setStyle(Style.FILL);
-        p.setAntiAlias(false);
-        canvas.drawRect(r, p);
-
-        // Draw background for grid area
-        if (mNumDays == 1 && todayIndex == 0) {
-            // Draw a white background for the time later than current time
-            int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
-                    + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
-            if (lineY < mViewStartY + mViewHeight) {
-                lineY = Math.max(lineY, mViewStartY);
-                r.left = mHoursWidth;
-                r.right = mViewWidth;
-                r.top = lineY;
-                r.bottom = mViewStartY + mViewHeight;
-                p.setColor(mFutureBgColor);
-                canvas.drawRect(r, p);
-            }
-        } else if (todayIndex >= 0 && todayIndex < mNumDays) {
-            // Draw today with a white background for the time later than current time
-            int lineY = mCurrentTime.hour * (mCellHeight + HOUR_GAP)
-                    + ((mCurrentTime.minute * mCellHeight) / 60) + 1;
-            if (lineY < mViewStartY + mViewHeight) {
-                lineY = Math.max(lineY, mViewStartY);
-                r.left = computeDayLeftPosition(todayIndex) + 1;
-                r.right = computeDayLeftPosition(todayIndex + 1);
-                r.top = lineY;
-                r.bottom = mViewStartY + mViewHeight;
-                p.setColor(mFutureBgColor);
-                canvas.drawRect(r, p);
-            }
-
-            // Paint Tomorrow and later days with future color
-            if (todayIndex + 1 < mNumDays) {
-                r.left = computeDayLeftPosition(todayIndex + 1) + 1;
-                r.right = computeDayLeftPosition(mNumDays);
-                r.top = mDestRect.top;
-                r.bottom = mDestRect.bottom;
-                p.setColor(mFutureBgColor);
-                canvas.drawRect(r, p);
-            }
-        } else if (todayIndex < 0) {
-            // Future
-            r.left = computeDayLeftPosition(0) + 1;
-            r.right = computeDayLeftPosition(mNumDays);
-            r.top = mDestRect.top;
-            r.bottom = mDestRect.bottom;
-            p.setColor(mFutureBgColor);
-            canvas.drawRect(r, p);
-        }
-        p.setAntiAlias(true);
-    }
-
-    private int computeMaxStringWidth(int currentMax, String[] strings, Paint p) {
-        float maxWidthF = 0.0f;
-
-        int len = strings.length;
-        for (int i = 0; i < len; i++) {
-            float width = p.measureText(strings[i]);
-            maxWidthF = Math.max(width, maxWidthF);
-        }
-        int maxWidth = (int) (maxWidthF + 0.5);
-        if (maxWidth < currentMax) {
-            maxWidth = currentMax;
-        }
-        return maxWidth;
-    }
-
-    private void saveSelectionPosition(float left, float top, float right, float bottom) {
-        mPrevBox.left = (int) left;
-        mPrevBox.right = (int) right;
-        mPrevBox.top = (int) top;
-        mPrevBox.bottom = (int) bottom;
-    }
-
-    private void setupTextRect(Rect r) {
-        if (r.bottom <= r.top || r.right <= r.left) {
-            r.bottom = r.top;
-            r.right = r.left;
-            return;
-        }
-
-        if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) {
-            r.top += EVENT_TEXT_TOP_MARGIN;
-            r.bottom -= EVENT_TEXT_BOTTOM_MARGIN;
-        }
-        if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) {
-            r.left += EVENT_TEXT_LEFT_MARGIN;
-            r.right -= EVENT_TEXT_RIGHT_MARGIN;
-        }
-    }
-
-    private void setupAllDayTextRect(Rect r) {
-        if (r.bottom <= r.top || r.right <= r.left) {
-            r.bottom = r.top;
-            r.right = r.left;
-            return;
-        }
-
-        if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) {
-            r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN;
-            r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN;
-        }
-        if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) {
-            r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
-            r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN;
-        }
-    }
-
-    /**
-     * Return the layout for a numbered event. Create it if not already existing
-     */
-    private StaticLayout getEventLayout(StaticLayout[] layouts, int i, Event event, Paint paint,
-            Rect r) {
-        if (i < 0 || i >= layouts.length) {
-            return null;
-        }
-
-        StaticLayout layout = layouts[i];
-        // Check if we have already initialized the StaticLayout and that
-        // the width hasn't changed (due to vertical resizing which causes
-        // re-layout of events at min height)
-        if (layout == null || r.width() != layout.getWidth()) {
-            SpannableStringBuilder bob = new SpannableStringBuilder();
-            if (event.title != null) {
-                // MAX - 1 since we add a space
-                bob.append(drawTextSanitizer(event.title.toString(), MAX_EVENT_TEXT_LEN - 1));
-                bob.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length(), 0);
-                bob.append(' ');
-            }
-            if (event.location != null) {
-                bob.append(drawTextSanitizer(event.location.toString(),
-                        MAX_EVENT_TEXT_LEN - bob.length()));
-            }
-
-            switch (event.selfAttendeeStatus) {
-                case Attendees.ATTENDEE_STATUS_INVITED:
-                    paint.setColor(event.color);
-                    break;
-                case Attendees.ATTENDEE_STATUS_DECLINED:
-                    paint.setColor(mEventTextColor);
-                    paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA);
-                    break;
-                case Attendees.ATTENDEE_STATUS_NONE: // Your own events
-                case Attendees.ATTENDEE_STATUS_ACCEPTED:
-                case Attendees.ATTENDEE_STATUS_TENTATIVE:
-                default:
-                    paint.setColor(mEventTextColor);
-                    break;
-            }
-
-            // Leave a one pixel boundary on the left and right of the rectangle for the event
-            layout = new StaticLayout(bob, 0, bob.length(), new TextPaint(paint), r.width(),
-                    Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width());
-
-            layouts[i] = layout;
-        }
-        layout.getPaint().setAlpha(mEventsAlpha);
-        return layout;
-    }
-
-    private void drawAllDayEvents(int firstDay, int numDays, Canvas canvas, Paint p) {
-
-        p.setTextSize(NORMAL_FONT_SIZE);
-        p.setTextAlign(Paint.Align.LEFT);
-        Paint eventTextPaint = mEventTextPaint;
-
-        final float startY = DAY_HEADER_HEIGHT;
-        final float stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN;
-        float x = 0;
-        int linesIndex = 0;
-
-        // Draw the inner vertical grid lines
-        p.setColor(mCalendarGridLineInnerVerticalColor);
-        x = mHoursWidth;
-        p.setStrokeWidth(GRID_LINE_INNER_WIDTH);
-        // Line bounding the top of the all day area
-        mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN;
-        mLines[linesIndex++] = startY;
-        mLines[linesIndex++] = computeDayLeftPosition(mNumDays);
-        mLines[linesIndex++] = startY;
-
-        for (int day = 0; day <= mNumDays; day++) {
-            x = computeDayLeftPosition(day);
-            mLines[linesIndex++] = x;
-            mLines[linesIndex++] = startY;
-            mLines[linesIndex++] = x;
-            mLines[linesIndex++] = stopY;
-        }
-        p.setAntiAlias(false);
-        canvas.drawLines(mLines, 0, linesIndex, p);
-        p.setStyle(Style.FILL);
-
-        int y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
-        int lastDay = firstDay + numDays - 1;
-        final ArrayList<Event> events = mAllDayEvents;
-        int numEvents = events.size();
-        // Whether or not we should draw the more events text
-        boolean hasMoreEvents = false;
-        // size of the allDay area
-        float drawHeight = mAlldayHeight;
-        // max number of events being drawn in one day of the allday area
-        float numRectangles = mMaxAlldayEvents;
-        // Where to cut off drawn allday events
-        int allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN;
-        // The number of events that weren't drawn in each day
-        mSkippedAlldayEvents = new int[numDays];
-        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount && !mShowAllAllDayEvents &&
-                mAnimateDayHeight == 0) {
-            // We draw one fewer event than will fit so that more events text
-            // can be drawn
-            numRectangles = mMaxUnexpandedAlldayEventCount - 1;
-            // We also clip the events above the more events text
-            allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
-            hasMoreEvents = true;
-        } else if (mAnimateDayHeight != 0) {
-            // clip at the end of the animating space
-            allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN;
-        }
-
-        int alpha = eventTextPaint.getAlpha();
-        eventTextPaint.setAlpha(mEventsAlpha);
-        for (int i = 0; i < numEvents; i++) {
-            Event event = events.get(i);
-            int startDay = event.startDay;
-            int endDay = event.endDay;
-            if (startDay > lastDay || endDay < firstDay) {
-                continue;
-            }
-            if (startDay < firstDay) {
-                startDay = firstDay;
-            }
-            if (endDay > lastDay) {
-                endDay = lastDay;
-            }
-            int startIndex = startDay - firstDay;
-            int endIndex = endDay - firstDay;
-            float height = mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount ? mAnimateDayEventHeight :
-                    drawHeight / numRectangles;
-
-            // Prevent a single event from getting too big
-            if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
-                height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
-            }
-
-            // Leave a one-pixel space between the vertical day lines and the
-            // event rectangle.
-            event.left = computeDayLeftPosition(startIndex);
-            event.right = computeDayLeftPosition(endIndex + 1) - DAY_GAP;
-            event.top = y + height * event.getColumn();
-            event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN;
-            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
-                // check if we should skip this event. We skip if it starts
-                // after the clip bound or ends after the skip bound and we're
-                // not animating.
-                if (event.top >= allDayEventClip) {
-                    incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
-                    continue;
-                } else if (event.bottom > allDayEventClip) {
-                    if (hasMoreEvents) {
-                        incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex);
-                        continue;
-                    }
-                    event.bottom = allDayEventClip;
-                }
-            }
-            Rect r = drawEventRect(event, canvas, p, eventTextPaint, (int) event.top,
-                    (int) event.bottom);
-            setupAllDayTextRect(r);
-            StaticLayout layout = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r);
-            drawEventText(layout, r, canvas, r.top, r.bottom, true);
-
-            // Check if this all-day event intersects the selected day
-            if (mSelectionAllday && mComputeSelectedEvents) {
-                if (startDay <= mSelectionDay && endDay >= mSelectionDay) {
-                    mSelectedEvents.add(event);
-                }
-            }
-        }
-        eventTextPaint.setAlpha(alpha);
-
-        if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) {
-            // If the more allday text should be visible, draw it.
-            alpha = p.getAlpha();
-            p.setAlpha(mEventsAlpha);
-            p.setColor(mMoreAlldayEventsTextAlpha << 24 & mMoreEventsTextColor);
-            for (int i = 0; i < mSkippedAlldayEvents.length; i++) {
-                if (mSkippedAlldayEvents[i] > 0) {
-                    drawMoreAlldayEvents(canvas, mSkippedAlldayEvents[i], i, p);
-                }
-            }
-            p.setAlpha(alpha);
-        }
-
-        if (mSelectionAllday) {
-            // Compute the neighbors for the list of all-day events that
-            // intersect the selected day.
-            computeAllDayNeighbors();
-
-            // Set the selection position to zero so that when we move down
-            // to the normal event area, we will highlight the topmost event.
-            saveSelectionPosition(0f, 0f, 0f, 0f);
-        }
-    }
-
-    // Helper method for counting the number of allday events skipped on each day
-    private void incrementSkipCount(int[] counts, int startIndex, int endIndex) {
-        if (counts == null || startIndex < 0 || endIndex > counts.length) {
-            return;
-        }
-        for (int i = startIndex; i <= endIndex; i++) {
-            counts[i]++;
-        }
-    }
-
-    // Draws the "box +n" text for hidden allday events
-    protected void drawMoreAlldayEvents(Canvas canvas, int remainingEvents, int day, Paint p) {
-        int x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN;
-        int y = (int) (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - .5f
-                * EVENT_SQUARE_WIDTH + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN);
-        Rect r = mRect;
-        r.top = y;
-        r.left = x;
-        r.bottom = y + EVENT_SQUARE_WIDTH;
-        r.right = x + EVENT_SQUARE_WIDTH;
-        p.setColor(mMoreEventsTextColor);
-        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
-        p.setStyle(Style.STROKE);
-        p.setAntiAlias(false);
-        canvas.drawRect(r, p);
-        p.setAntiAlias(true);
-        p.setStyle(Style.FILL);
-        p.setTextSize(EVENT_TEXT_FONT_SIZE);
-        String text = mResources.getQuantityString(R.plurals.month_more_events, remainingEvents);
-        y += EVENT_SQUARE_WIDTH;
-        x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING;
-        canvas.drawText(String.format(text, remainingEvents), x, y, p);
-    }
-
-    private void computeAllDayNeighbors() {
-        int len = mSelectedEvents.size();
-        if (len == 0 || mSelectedEvent != null) {
-            return;
-        }
-
-        // First, clear all the links
-        for (int ii = 0; ii < len; ii++) {
-            Event ev = mSelectedEvents.get(ii);
-            ev.nextUp = null;
-            ev.nextDown = null;
-            ev.nextLeft = null;
-            ev.nextRight = null;
-        }
-
-        // For each event in the selected event list "mSelectedEvents", find
-        // its neighbors in the up and down directions. This could be done
-        // more efficiently by sorting on the Event.getColumn() field, but
-        // the list is expected to be very small.
-
-        // Find the event in the same row as the previously selected all-day
-        // event, if any.
-        int startPosition = -1;
-        if (mPrevSelectedEvent != null && mPrevSelectedEvent.drawAsAllday()) {
-            startPosition = mPrevSelectedEvent.getColumn();
-        }
-        int maxPosition = -1;
-        Event startEvent = null;
-        Event maxPositionEvent = null;
-        for (int ii = 0; ii < len; ii++) {
-            Event ev = mSelectedEvents.get(ii);
-            int position = ev.getColumn();
-            if (position == startPosition) {
-                startEvent = ev;
-            } else if (position > maxPosition) {
-                maxPositionEvent = ev;
-                maxPosition = position;
-            }
-            for (int jj = 0; jj < len; jj++) {
-                if (jj == ii) {
-                    continue;
-                }
-                Event neighbor = mSelectedEvents.get(jj);
-                int neighborPosition = neighbor.getColumn();
-                if (neighborPosition == position - 1) {
-                    ev.nextUp = neighbor;
-                } else if (neighborPosition == position + 1) {
-                    ev.nextDown = neighbor;
-                }
-            }
-        }
-        if (startEvent != null) {
-            setSelectedEvent(startEvent);
-        } else {
-            setSelectedEvent(maxPositionEvent);
-        }
-    }
-
-    private void drawEvents(int date, int dayIndex, int top, Canvas canvas, Paint p) {
-        Paint eventTextPaint = mEventTextPaint;
-        int left = computeDayLeftPosition(dayIndex) + 1;
-        int cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1;
-        int cellHeight = mCellHeight;
-
-        // Use the selected hour as the selection region
-        Rect selectionArea = mSelectionRect;
-        selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP);
-        selectionArea.bottom = selectionArea.top + cellHeight;
-        selectionArea.left = left;
-        selectionArea.right = selectionArea.left + cellWidth;
-
-        final ArrayList<Event> events = mEvents;
-        int numEvents = events.size();
-        EventGeometry geometry = mEventGeometry;
-
-        final int viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight;
-
-        int alpha = eventTextPaint.getAlpha();
-        eventTextPaint.setAlpha(mEventsAlpha);
-        for (int i = 0; i < numEvents; i++) {
-            Event event = events.get(i);
-            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
-                continue;
-            }
-
-            // Don't draw it if it is not visible
-            if (event.bottom < mViewStartY || event.top > viewEndY) {
-                continue;
-            }
-
-            if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents
-                    && geometry.eventIntersectsSelection(event, selectionArea)) {
-                mSelectedEvents.add(event);
-            }
-
-            Rect r = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY);
-            setupTextRect(r);
-
-            // Don't draw text if it is not visible
-            if (r.top > viewEndY || r.bottom < mViewStartY) {
-                continue;
-            }
-            StaticLayout layout = getEventLayout(mLayouts, i, event, eventTextPaint, r);
-            // TODO: not sure why we are 4 pixels off
-            drawEventText(layout, r, canvas, mViewStartY + 4, mViewStartY + mViewHeight
-                    - DAY_HEADER_HEIGHT - mAlldayHeight, false);
-        }
-        eventTextPaint.setAlpha(alpha);
-    }
-
-    private Rect drawEventRect(Event event, Canvas canvas, Paint p, Paint eventTextPaint,
-            int visibleTop, int visibleBot) {
-        // Draw the Event Rect
-        Rect r = mRect;
-        r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN, visibleTop);
-        r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN, visibleBot);
-        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
-        r.right = (int) event.right;
-
-        int color = event.color;
-        switch (event.selfAttendeeStatus) {
-            case Attendees.ATTENDEE_STATUS_INVITED:
-                if (event != mClickedEvent) {
-                    p.setStyle(Style.STROKE);
-                }
-                break;
-            case Attendees.ATTENDEE_STATUS_DECLINED:
-                if (event != mClickedEvent) {
-                    color = Utils.getDeclinedColorFromColor(color);
-                }
-            case Attendees.ATTENDEE_STATUS_NONE: // Your own events
-            case Attendees.ATTENDEE_STATUS_ACCEPTED:
-            case Attendees.ATTENDEE_STATUS_TENTATIVE:
-            default:
-                p.setStyle(Style.FILL_AND_STROKE);
-                break;
-        }
-
-        p.setAntiAlias(false);
-
-        int floorHalfStroke = (int) Math.floor(EVENT_RECT_STROKE_WIDTH / 2.0f);
-        int ceilHalfStroke = (int) Math.ceil(EVENT_RECT_STROKE_WIDTH / 2.0f);
-        r.top = Math.max((int) event.top + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop);
-        r.bottom = Math.min((int) event.bottom - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke,
-                visibleBot);
-        r.left += floorHalfStroke;
-        r.right -= ceilHalfStroke;
-        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH);
-        p.setColor(color);
-        int alpha = p.getAlpha();
-        p.setAlpha(mEventsAlpha);
-        canvas.drawRect(r, p);
-        p.setAlpha(alpha);
-        p.setStyle(Style.FILL);
-
-        // Setup rect for drawEventText which follows
-        r.top = (int) event.top + EVENT_RECT_TOP_MARGIN;
-        r.bottom = (int) event.bottom - EVENT_RECT_BOTTOM_MARGIN;
-        r.left = (int) event.left + EVENT_RECT_LEFT_MARGIN;
-        r.right = (int) event.right - EVENT_RECT_RIGHT_MARGIN;
-        return r;
-    }
-
-    private final Pattern drawTextSanitizerFilter = Pattern.compile("[\t\n],");
-
-    // Sanitize a string before passing it to drawText or else we get little
-    // squares. For newlines and tabs before a comma, delete the character.
-    // Otherwise, just replace them with a space.
-    private String drawTextSanitizer(String string, int maxEventTextLen) {
-        Matcher m = drawTextSanitizerFilter.matcher(string);
-        string = m.replaceAll(",");
-
-        int len = string.length();
-        if (maxEventTextLen <= 0) {
-            string = "";
-            len = 0;
-        } else if (len > maxEventTextLen) {
-            string = string.substring(0, maxEventTextLen);
-            len = maxEventTextLen;
-        }
-
-        return string.replace('\n', ' ');
-    }
-
-    private void drawEventText(StaticLayout eventLayout, Rect rect, Canvas canvas, int top,
-            int bottom, boolean center) {
-        // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging
-
-        int width = rect.right - rect.left;
-        int height = rect.bottom - rect.top;
-
-        // If the rectangle is too small for text, then return
-        if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) {
-            return;
-        }
-
-        int totalLineHeight = 0;
-        int lineCount = eventLayout.getLineCount();
-        for (int i = 0; i < lineCount; i++) {
-            int lineBottom = eventLayout.getLineBottom(i);
-            if (lineBottom <= height) {
-                totalLineHeight = lineBottom;
-            } else {
-                break;
-            }
-        }
-
-        // + 2 is small workaround when the font is slightly bigger then the rect. This will
-        // still allow the text to be shown without overflowing into the other all day rects.
-        if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight + 2 < top) {
-            return;
-        }
-
-        // Use a StaticLayout to format the string.
-        canvas.save();
-      //  canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2));
-        int padding = center? (rect.bottom - rect.top - totalLineHeight) / 2 : 0;
-        canvas.translate(rect.left, rect.top + padding);
-        rect.left = 0;
-        rect.right = width;
-        rect.top = 0;
-        rect.bottom = totalLineHeight;
-
-        // There's a bug somewhere. If this rect is outside of a previous
-        // cliprect, this becomes a no-op. What happens is that the text draw
-        // past the event rect. The current fix is to not draw the staticLayout
-        // at all if it is completely out of bound.
-        canvas.clipRect(rect);
-        eventLayout.draw(canvas);
-        canvas.restore();
-    }
-
-    // The following routines are called from the parent activity when certain
-    // touch events occur.
-    private void doDown(MotionEvent ev) {
-        mTouchMode = TOUCH_MODE_DOWN;
-        mViewStartX = 0;
-        mOnFlingCalled = false;
-        mHandler.removeCallbacks(mContinueScroll);
-        int x = (int) ev.getX();
-        int y = (int) ev.getY();
-
-        // Save selection information: we use setSelectionFromPosition to find the selected event
-        // in order to show the "clicked" color. But since it is also setting the selected info
-        // for new events, we need to restore the old info after calling the function.
-        Event oldSelectedEvent = mSelectedEvent;
-        int oldSelectionDay = mSelectionDay;
-        int oldSelectionHour = mSelectionHour;
-        if (setSelectionFromPosition(x, y, false)) {
-            // If a time was selected (a blue selection box is visible) and the click location
-            // is in the selected time, do not show a click on an event to prevent a situation
-            // of both a selection and an event are clicked when they overlap.
-            boolean pressedSelected = (mSelectionMode != SELECTION_HIDDEN)
-                    && oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour;
-            if (!pressedSelected && mSelectedEvent != null) {
-                mSavedClickedEvent = mSelectedEvent;
-                mDownTouchTime = System.currentTimeMillis();
-                postDelayed (mSetClick,mOnDownDelay);
-            } else {
-                eventClickCleanup();
-            }
-        }
-        mSelectedEvent = oldSelectedEvent;
-        mSelectionDay = oldSelectionDay;
-        mSelectionHour = oldSelectionHour;
-        invalidate();
-    }
-
-    // Kicks off all the animations when the expand allday area is tapped
-    private void doExpandAllDayClick() {
-        mShowAllAllDayEvents = !mShowAllAllDayEvents;
-
-        ObjectAnimator.setFrameDelay(0);
-
-        // Determine the starting height
-        if (mAnimateDayHeight == 0) {
-            mAnimateDayHeight = mShowAllAllDayEvents ?
-                    mAlldayHeight - (int) MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT : mAlldayHeight;
-        }
-        // Cancel current animations
-        mCancellingAnimations = true;
-        if (mAlldayAnimator != null) {
-            mAlldayAnimator.cancel();
-        }
-        if (mAlldayEventAnimator != null) {
-            mAlldayEventAnimator.cancel();
-        }
-        if (mMoreAlldayEventsAnimator != null) {
-            mMoreAlldayEventsAnimator.cancel();
-        }
-        mCancellingAnimations = false;
-        // get new animators
-        mAlldayAnimator = getAllDayAnimator();
-        mAlldayEventAnimator = getAllDayEventAnimator();
-        mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(this,
-                    "moreAllDayEventsTextAlpha",
-                    mShowAllAllDayEvents ? MORE_EVENTS_MAX_ALPHA : 0,
-                    mShowAllAllDayEvents ? 0 : MORE_EVENTS_MAX_ALPHA);
-
-        // Set up delays and start the animators
-        mAlldayAnimator.setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
-        mAlldayAnimator.start();
-        mMoreAlldayEventsAnimator.setStartDelay(mShowAllAllDayEvents ? 0 : ANIMATION_DURATION);
-        mMoreAlldayEventsAnimator.setDuration(ANIMATION_SECONDARY_DURATION);
-        mMoreAlldayEventsAnimator.start();
-        if (mAlldayEventAnimator != null) {
-            // This is the only animator that can return null, so check it
-            mAlldayEventAnimator
-                    .setStartDelay(mShowAllAllDayEvents ? ANIMATION_SECONDARY_DURATION : 0);
-            mAlldayEventAnimator.start();
-        }
-    }
-
-    /**
-     * Figures out the initial heights for allDay events and space when
-     * a view is being set up.
-     */
-    public void initAllDayHeights() {
-        if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) {
-            return;
-        }
-        if (mShowAllAllDayEvents) {
-            int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
-            maxADHeight = Math.min(maxADHeight,
-                    (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
-            mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents;
-        } else {
-            mAnimateDayEventHeight = (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
-        }
-    }
-
-    // Sets up an animator for changing the height of allday events
-    private ObjectAnimator getAllDayEventAnimator() {
-        // First calculate the absolute max height
-        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
-        // Now expand to fit but not beyond the absolute max
-        maxADHeight =
-                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
-        // calculate the height of individual events in order to fit
-        int fitHeight = maxADHeight / mMaxAlldayEvents;
-        int currentHeight = mAnimateDayEventHeight;
-        int desiredHeight =
-                mShowAllAllDayEvents ? fitHeight : (int)MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT;
-        // if there's nothing to animate just return
-        if (currentHeight == desiredHeight) {
-            return null;
-        }
-
-        // Set up the animator with the calculated values
-        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayEventHeight",
-                currentHeight, desiredHeight);
-        animator.setDuration(ANIMATION_DURATION);
-        return animator;
-    }
-
-    // Sets up an animator for changing the height of the allday area
-    private ObjectAnimator getAllDayAnimator() {
-        // Calculate the absolute max height
-        int maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT;
-        // Find the desired height but don't exceed abs max
-        maxADHeight =
-                Math.min(maxADHeight, (int)(mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT));
-        // calculate the current and desired heights
-        int currentHeight = mAnimateDayHeight != 0 ? mAnimateDayHeight : mAlldayHeight;
-        int desiredHeight = mShowAllAllDayEvents ? maxADHeight :
-                (int) (MAX_UNEXPANDED_ALLDAY_HEIGHT - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1);
-
-        // Set up the animator with the calculated values
-        ObjectAnimator animator = ObjectAnimator.ofInt(this, "animateDayHeight",
-                currentHeight, desiredHeight);
-        animator.setDuration(ANIMATION_DURATION);
-
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!mCancellingAnimations) {
-                    // when finished, set this to 0 to signify not animating
-                    mAnimateDayHeight = 0;
-                    mUseExpandIcon = !mShowAllAllDayEvents;
-                }
-                mRemeasure = true;
-                invalidate();
-            }
-        });
-        return animator;
-    }
-
-    // setter for the 'box +n' alpha text used by the animator
-    public void setMoreAllDayEventsTextAlpha(int alpha) {
-        mMoreAlldayEventsTextAlpha = alpha;
-        invalidate();
-    }
-
-    // setter for the height of the allday area used by the animator
-    public void setAnimateDayHeight(int height) {
-        mAnimateDayHeight = height;
-        mRemeasure = true;
-        invalidate();
-    }
-
-    // setter for the height of allday events used by the animator
-    public void setAnimateDayEventHeight(int height) {
-        mAnimateDayEventHeight = height;
-        mRemeasure = true;
-        invalidate();
-    }
-
-    private void doSingleTapUp(MotionEvent ev) {
-        if (!mHandleActionUp || mScrolling) {
-            return;
-        }
-
-        int x = (int) ev.getX();
-        int y = (int) ev.getY();
-        int selectedDay = mSelectionDay;
-        int selectedHour = mSelectionHour;
-
-        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
-            // check if the tap was in the allday expansion area
-            int bottom = mFirstCell;
-            if((x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight)
-                    || (!mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom &&
-                            y >= bottom - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT)) {
-                doExpandAllDayClick();
-                return;
-            }
-        }
-
-        boolean validPosition = setSelectionFromPosition(x, y, false);
-        if (!validPosition) {
-            if (y < DAY_HEADER_HEIGHT) {
-                Time selectedTime = new Time(mBaseDate);
-                selectedTime.setJulianDay(mSelectionDay);
-                selectedTime.hour = mSelectionHour;
-                selectedTime.normalize(true /* ignore isDst */);
-                mController.sendEvent(this, EventType.GO_TO, null, null, selectedTime, -1,
-                        ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null);
-            }
-            return;
-        }
-
-        boolean hasSelection = mSelectionMode != SELECTION_HIDDEN;
-        boolean pressedSelected = (hasSelection || mTouchExplorationEnabled)
-                && selectedDay == mSelectionDay && selectedHour == mSelectionHour;
-
-        if (mSelectedEvent != null) {
-            // If the tap is on an event, launch the "View event" view
-            if (mIsAccessibilityEnabled) {
-                mAccessibilityMgr.interrupt();
-            }
-
-            mSelectionMode = SELECTION_HIDDEN;
-
-            int yLocation =
-                (int)((mSelectedEvent.top + mSelectedEvent.bottom)/2);
-            // Y location is affected by the position of the event in the scrolling
-            // view (mViewStartY) and the presence of all day events (mFirstCell)
-            if (!mSelectedEvent.allDay) {
-                yLocation += (mFirstCell - mViewStartY);
-            }
-            mClickedYLocation = yLocation;
-            long clearDelay = (CLICK_DISPLAY_DURATION + mOnDownDelay) -
-                    (System.currentTimeMillis() - mDownTouchTime);
-            if (clearDelay > 0) {
-                this.postDelayed(mClearClick, clearDelay);
-            } else {
-                this.post(mClearClick);
-            }
-        }
-        invalidate();
-    }
-
-    private void doLongPress(MotionEvent ev) {
-        eventClickCleanup();
-        if (mScrolling) {
-            return;
-        }
-
-        // Scale gesture in progress
-        if (mStartingSpanY != 0) {
-            return;
-        }
-
-        int x = (int) ev.getX();
-        int y = (int) ev.getY();
-
-        boolean validPosition = setSelectionFromPosition(x, y, false);
-        if (!validPosition) {
-            // return if the touch wasn't on an area of concern
-            return;
-        }
-
-        invalidate();
-        performLongClick();
-    }
-
-    private void doScroll(MotionEvent e1, MotionEvent e2, float deltaX, float deltaY) {
-        cancelAnimation();
-        if (mStartingScroll) {
-            mInitialScrollX = 0;
-            mInitialScrollY = 0;
-            mStartingScroll = false;
-        }
-
-        mInitialScrollX += deltaX;
-        mInitialScrollY += deltaY;
-        int distanceX = (int) mInitialScrollX;
-        int distanceY = (int) mInitialScrollY;
-
-        final float focusY = getAverageY(e2);
-        if (mRecalCenterHour) {
-            // Calculate the hour that correspond to the average of the Y touch points
-            mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight)
-                    / (mCellHeight + DAY_GAP);
-            mRecalCenterHour = false;
-        }
-
-        // If we haven't figured out the predominant scroll direction yet,
-        // then do it now.
-        if (mTouchMode == TOUCH_MODE_DOWN) {
-            int absDistanceX = Math.abs(distanceX);
-            int absDistanceY = Math.abs(distanceY);
-            mScrollStartY = mViewStartY;
-            mPreviousDirection = 0;
-
-            if (absDistanceX > absDistanceY) {
-                int slopFactor = mScaleGestureDetector.isInProgress() ? 20 : 2;
-                if (absDistanceX > mScaledPagingTouchSlop * slopFactor) {
-                    mTouchMode = TOUCH_MODE_HSCROLL;
-                    mViewStartX = distanceX;
-                    initNextView(-mViewStartX);
-                }
-            } else {
-                mTouchMode = TOUCH_MODE_VSCROLL;
-            }
-        } else if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
-            // We are already scrolling horizontally, so check if we
-            // changed the direction of scrolling so that the other week
-            // is now visible.
-            mViewStartX = distanceX;
-            if (distanceX != 0) {
-                int direction = (distanceX > 0) ? 1 : -1;
-                if (direction != mPreviousDirection) {
-                    // The user has switched the direction of scrolling
-                    // so re-init the next view
-                    initNextView(-mViewStartX);
-                    mPreviousDirection = direction;
-                }
-            }
-        }
-
-        if ((mTouchMode & TOUCH_MODE_VSCROLL) != 0) {
-            // Calculate the top of the visible region in the calendar grid.
-            // Increasing/decrease this will scroll the calendar grid up/down.
-            mViewStartY = (int) ((mGestureCenterHour * (mCellHeight + DAY_GAP))
-                    - focusY + DAY_HEADER_HEIGHT + mAlldayHeight);
-
-            // If dragging while already at the end, do a glow
-            final int pulledToY = (int) (mScrollStartY + deltaY);
-            if (pulledToY < 0) {
-                mEdgeEffectTop.onPull(deltaY / mViewHeight);
-                if (!mEdgeEffectBottom.isFinished()) {
-                    mEdgeEffectBottom.onRelease();
-                }
-            } else if (pulledToY > mMaxViewStartY) {
-                mEdgeEffectBottom.onPull(deltaY / mViewHeight);
-                if (!mEdgeEffectTop.isFinished()) {
-                    mEdgeEffectTop.onRelease();
-                }
-            }
-
-            if (mViewStartY < 0) {
-                mViewStartY = 0;
-                mRecalCenterHour = true;
-            } else if (mViewStartY > mMaxViewStartY) {
-                mViewStartY = mMaxViewStartY;
-                mRecalCenterHour = true;
-            }
-            if (mRecalCenterHour) {
-                // Calculate the hour that correspond to the average of the Y touch points
-                mGestureCenterHour = (mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight)
-                        / (mCellHeight + DAY_GAP);
-                mRecalCenterHour = false;
-            }
-            computeFirstHour();
-        }
-
-        mScrolling = true;
-
-        mSelectionMode = SELECTION_HIDDEN;
-        invalidate();
-    }
-
-    private float getAverageY(MotionEvent me) {
-        int count = me.getPointerCount();
-        float focusY = 0;
-        for (int i = 0; i < count; i++) {
-            focusY += me.getY(i);
-        }
-        focusY /= count;
-        return focusY;
-    }
-
-    private void cancelAnimation() {
-        Animation in = mViewSwitcher.getInAnimation();
-        if (in != null) {
-            // cancel() doesn't terminate cleanly.
-            in.scaleCurrentDuration(0);
-        }
-        Animation out = mViewSwitcher.getOutAnimation();
-        if (out != null) {
-            // cancel() doesn't terminate cleanly.
-            out.scaleCurrentDuration(0);
-        }
-    }
-
-    private void doFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-        cancelAnimation();
-
-        mSelectionMode = SELECTION_HIDDEN;
-        eventClickCleanup();
-
-        mOnFlingCalled = true;
-
-        if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
-            // Horizontal fling.
-            // initNextView(deltaX);
-            mTouchMode = TOUCH_MODE_INITIAL_STATE;
-            if (DEBUG) Log.d(TAG, "doFling: velocityX " + velocityX);
-            int deltaX = (int) e2.getX() - (int) e1.getX();
-            switchViews(deltaX < 0, mViewStartX, mViewWidth, velocityX);
-            mViewStartX = 0;
-            return;
-        }
-
-        if ((mTouchMode & TOUCH_MODE_VSCROLL) == 0) {
-            if (DEBUG) Log.d(TAG, "doFling: no fling");
-            return;
-        }
-
-        // Vertical fling.
-        mTouchMode = TOUCH_MODE_INITIAL_STATE;
-        mViewStartX = 0;
-
-        if (DEBUG) {
-            Log.d(TAG, "doFling: mViewStartY" + mViewStartY + " velocityY " + velocityY);
-        }
-
-        // Continue scrolling vertically
-        mScrolling = true;
-        mScroller.fling(0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */,
-                (int) -velocityY, 0 /* minX */, 0 /* maxX */, 0 /* minY */,
-                mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE);
-
-        // When flinging down, show a glow when it hits the end only if it
-        // wasn't started at the top
-        if (velocityY > 0 && mViewStartY != 0) {
-            mCallEdgeEffectOnAbsorb = true;
-        }
-        // When flinging up, show a glow when it hits the end only if it wasn't
-        // started at the bottom
-        else if (velocityY < 0 && mViewStartY != mMaxViewStartY) {
-            mCallEdgeEffectOnAbsorb = true;
-        }
-        mHandler.post(mContinueScroll);
-    }
-
-    private boolean initNextView(int deltaX) {
-        // Change the view to the previous day or week
-        DayView view = (DayView) mViewSwitcher.getNextView();
-        Time date = view.mBaseDate;
-        date.set(mBaseDate);
-        boolean switchForward;
-        if (deltaX > 0) {
-            date.monthDay -= mNumDays;
-            view.setSelectedDay(mSelectionDay - mNumDays);
-            switchForward = false;
-        } else {
-            date.monthDay += mNumDays;
-            view.setSelectedDay(mSelectionDay + mNumDays);
-            switchForward = true;
-        }
-        date.normalize(true /* ignore isDst */);
-        initView(view);
-        view.layout(getLeft(), getTop(), getRight(), getBottom());
-        view.reloadEvents();
-        return switchForward;
-    }
-
-    // ScaleGestureDetector.OnScaleGestureListener
-    public boolean onScaleBegin(ScaleGestureDetector detector) {
-        mHandleActionUp = false;
-        float gestureCenterInPixels = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
-        mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP);
-
-        mStartingSpanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY()));
-        mCellHeightBeforeScaleGesture = mCellHeight;
-
-        if (DEBUG_SCALING) {
-            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
-            Log.d(TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour
-                    + "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY
-                    + "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY());
-        }
-
-        return true;
-    }
-
-    // ScaleGestureDetector.OnScaleGestureListener
-    public boolean onScale(ScaleGestureDetector detector) {
-        float spanY = Math.max(MIN_Y_SPAN, Math.abs(detector.getCurrentSpanY()));
-
-        mCellHeight = (int) (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY);
-
-        if (mCellHeight < mMinCellHeight) {
-            // If mStartingSpanY is too small, even a small increase in the
-            // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT
-            mStartingSpanY = spanY;
-            mCellHeight = mMinCellHeight;
-            mCellHeightBeforeScaleGesture = mMinCellHeight;
-        } else if (mCellHeight > MAX_CELL_HEIGHT) {
-            mStartingSpanY = spanY;
-            mCellHeight = MAX_CELL_HEIGHT;
-            mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT;
-        }
-
-        int gestureCenterInPixels = (int) detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight;
-        mViewStartY = (int) (mGestureCenterHour * (mCellHeight + DAY_GAP)) - gestureCenterInPixels;
-        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight;
-
-        if (DEBUG_SCALING) {
-            float ViewStartHour = mViewStartY / (float) (mCellHeight + DAY_GAP);
-            Log.d(TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: "
-                    + ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:"
-                    + mCellHeight + " SpanY:" + detector.getCurrentSpanY());
-        }
-
-        if (mViewStartY < 0) {
-            mViewStartY = 0;
-            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
-                    / (float) (mCellHeight + DAY_GAP);
-        } else if (mViewStartY > mMaxViewStartY) {
-            mViewStartY = mMaxViewStartY;
-            mGestureCenterHour = (mViewStartY + gestureCenterInPixels)
-                    / (float) (mCellHeight + DAY_GAP);
-        }
-        computeFirstHour();
-
-        mRemeasure = true;
-        invalidate();
-        return true;
-    }
-
-    // ScaleGestureDetector.OnScaleGestureListener
-    public void onScaleEnd(ScaleGestureDetector detector) {
-        mScrollStartY = mViewStartY;
-        mInitialScrollY = 0;
-        mInitialScrollX = 0;
-        mStartingSpanY = 0;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent ev) {
-        int action = ev.getAction();
-        if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount());
-
-        if ((ev.getActionMasked() == MotionEvent.ACTION_DOWN) ||
-                (ev.getActionMasked() == MotionEvent.ACTION_UP) ||
-                (ev.getActionMasked() == MotionEvent.ACTION_POINTER_UP) ||
-                (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) {
-            mRecalCenterHour = true;
-        }
-
-        if ((mTouchMode & TOUCH_MODE_HSCROLL) == 0) {
-            mScaleGestureDetector.onTouchEvent(ev);
-        }
-
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mStartingScroll = true;
-                if (DEBUG) {
-                    Log.e(TAG, "ACTION_DOWN ev.getDownTime = " + ev.getDownTime() + " Cnt="
-                            + ev.getPointerCount());
-                }
-
-                int bottom = mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
-                if (ev.getY() < bottom) {
-                    mTouchStartedInAlldayArea = true;
-                } else {
-                    mTouchStartedInAlldayArea = false;
-                }
-                mHandleActionUp = true;
-                mGestureDetector.onTouchEvent(ev);
-                return true;
-
-            case MotionEvent.ACTION_MOVE:
-                if (DEBUG) Log.e(TAG, "ACTION_MOVE Cnt=" + ev.getPointerCount() + DayView.this);
-                mGestureDetector.onTouchEvent(ev);
-                return true;
-
-            case MotionEvent.ACTION_UP:
-                if (DEBUG) Log.e(TAG, "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp);
-                mEdgeEffectTop.onRelease();
-                mEdgeEffectBottom.onRelease();
-                mStartingScroll = false;
-                mGestureDetector.onTouchEvent(ev);
-                if (!mHandleActionUp) {
-                    mHandleActionUp = true;
-                    mViewStartX = 0;
-                    invalidate();
-                    return true;
-                }
-
-                if (mOnFlingCalled) {
-                    return true;
-                }
-
-                // If we were scrolling, then reset the selected hour so that it
-                // is visible.
-                if (mScrolling) {
-                    mScrolling = false;
-                    resetSelectedHour();
-                    invalidate();
-                }
-
-                if ((mTouchMode & TOUCH_MODE_HSCROLL) != 0) {
-                    mTouchMode = TOUCH_MODE_INITIAL_STATE;
-                    if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) {
-                        // The user has gone beyond the threshold so switch views
-                        if (DEBUG) Log.d(TAG, "- horizontal scroll: switch views");
-                        switchViews(mViewStartX > 0, mViewStartX, mViewWidth, 0);
-                        mViewStartX = 0;
-                        return true;
-                    } else {
-                        // Not beyond the threshold so invalidate which will cause
-                        // the view to snap back. Also call recalc() to ensure
-                        // that we have the correct starting date and title.
-                        if (DEBUG) Log.d(TAG, "- horizontal scroll: snap back");
-                        recalc();
-                        invalidate();
-                        mViewStartX = 0;
-                    }
-                }
-
-                return true;
-
-                // This case isn't expected to happen.
-            case MotionEvent.ACTION_CANCEL:
-                if (DEBUG) Log.e(TAG, "ACTION_CANCEL");
-                mGestureDetector.onTouchEvent(ev);
-                mScrolling = false;
-                resetSelectedHour();
-                return true;
-
-            default:
-                if (DEBUG) Log.e(TAG, "Not MotionEvent " + ev.toString());
-                if (mGestureDetector.onTouchEvent(ev)) {
-                    return true;
-                }
-                return super.onTouchEvent(ev);
-        }
-    }
-
-    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
-        MenuItem item;
-
-        // If the trackball is held down, then the context menu pops up and
-        // we never get onKeyUp() for the long-press. So check for it here
-        // and change the selection to the long-press state.
-        if (mSelectionMode != SELECTION_LONGPRESS) {
-            invalidate();
-        }
-
-        final long startMillis = getSelectedTimeInMillis();
-        int flags = DateUtils.FORMAT_SHOW_TIME
-                | DateUtils.FORMAT_CAP_NOON_MIDNIGHT
-                | DateUtils.FORMAT_SHOW_WEEKDAY;
-        final String title = Utils.formatDateRange(mContext, startMillis, startMillis, flags);
-        menu.setHeaderTitle(title);
-
-        mPopup.dismiss();
-    }
-
-    /**
-     * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position.
-     * If the touch position is not within the displayed grid, then this
-     * method returns false.
-     *
-     * @param x the x position of the touch
-     * @param y the y position of the touch
-     * @param keepOldSelection - do not change the selection info (used for invoking accessibility
-     *                           messages)
-     * @return true if the touch position is valid
-     */
-    private boolean setSelectionFromPosition(int x, final int y, boolean keepOldSelection) {
-
-        Event savedEvent = null;
-        int savedDay = 0;
-        int savedHour = 0;
-        boolean savedAllDay = false;
-        if (keepOldSelection) {
-            // Store selection info and restore it at the end. This way, we can invoke the
-            // right accessibility message without affecting the selection.
-            savedEvent = mSelectedEvent;
-            savedDay = mSelectionDay;
-            savedHour = mSelectionHour;
-            savedAllDay = mSelectionAllday;
-        }
-        if (x < mHoursWidth) {
-            x = mHoursWidth;
-        }
-
-        int day = (x - mHoursWidth) / (mCellWidth + DAY_GAP);
-        if (day >= mNumDays) {
-            day = mNumDays - 1;
-        }
-        day += mFirstJulianDay;
-        setSelectedDay(day);
-
-        if (y < DAY_HEADER_HEIGHT) {
-            sendAccessibilityEventAsNeeded(false);
-            return false;
-        }
-
-        setSelectedHour(mFirstHour); /* First fully visible hour */
-
-        if (y < mFirstCell) {
-            mSelectionAllday = true;
-        } else {
-            // y is now offset from top of the scrollable region
-            int adjustedY = y - mFirstCell;
-
-            if (adjustedY < mFirstHourOffset) {
-                setSelectedHour(mSelectionHour - 1); /* In the partially visible hour */
-            } else {
-                setSelectedHour(mSelectionHour +
-                        (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP));
-            }
-
-            mSelectionAllday = false;
-        }
-
-        findSelectedEvent(x, y);
-
-        sendAccessibilityEventAsNeeded(true);
-
-        // Restore old values
-        if (keepOldSelection) {
-            mSelectedEvent = savedEvent;
-            mSelectionDay = savedDay;
-            mSelectionHour = savedHour;
-            mSelectionAllday = savedAllDay;
-        }
-        return true;
-    }
-
-    private void findSelectedEvent(int x, int y) {
-        int date = mSelectionDay;
-        int cellWidth = mCellWidth;
-        ArrayList<Event> events = mEvents;
-        int numEvents = events.size();
-        int left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay);
-        int top = 0;
-        setSelectedEvent(null);
-
-        mSelectedEvents.clear();
-        if (mSelectionAllday) {
-            float yDistance;
-            float minYdistance = 10000.0f; // any large number
-            Event closestEvent = null;
-            float drawHeight = mAlldayHeight;
-            int yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN;
-            int maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount;
-            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
-                // Leave a gap for the 'box +n' text
-                maxUnexpandedColumn--;
-            }
-            events = mAllDayEvents;
-            numEvents = events.size();
-            for (int i = 0; i < numEvents; i++) {
-                Event event = events.get(i);
-                if (!event.drawAsAllday() ||
-                        (!mShowAllAllDayEvents && event.getColumn() >= maxUnexpandedColumn)) {
-                    // Don't check non-allday events or events that aren't shown
-                    continue;
-                }
-
-                if (event.startDay <= mSelectionDay && event.endDay >= mSelectionDay) {
-                    float numRectangles = mShowAllAllDayEvents ? mMaxAlldayEvents
-                            : mMaxUnexpandedAlldayEventCount;
-                    float height = drawHeight / numRectangles;
-                    if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
-                        height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT;
-                    }
-                    float eventTop = yOffset + height * event.getColumn();
-                    float eventBottom = eventTop + height;
-                    if (eventTop < y && eventBottom > y) {
-                        // If the touch is inside the event rectangle, then
-                        // add the event.
-                        mSelectedEvents.add(event);
-                        closestEvent = event;
-                        break;
-                    } else {
-                        // Find the closest event
-                        if (eventTop >= y) {
-                            yDistance = eventTop - y;
-                        } else {
-                            yDistance = y - eventBottom;
-                        }
-                        if (yDistance < minYdistance) {
-                            minYdistance = yDistance;
-                            closestEvent = event;
-                        }
-                    }
-                }
-            }
-            setSelectedEvent(closestEvent);
-            return;
-        }
-
-        // Adjust y for the scrollable bitmap
-        y += mViewStartY - mFirstCell;
-
-        // Use a region around (x,y) for the selection region
-        Rect region = mRect;
-        region.left = x - 10;
-        region.right = x + 10;
-        region.top = y - 10;
-        region.bottom = y + 10;
-
-        EventGeometry geometry = mEventGeometry;
-
-        for (int i = 0; i < numEvents; i++) {
-            Event event = events.get(i);
-            // Compute the event rectangle.
-            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
-                continue;
-            }
-
-            // If the event intersects the selection region, then add it to
-            // mSelectedEvents.
-            if (geometry.eventIntersectsSelection(event, region)) {
-                mSelectedEvents.add(event);
-            }
-        }
-
-        // If there are any events in the selected region, then assign the
-        // closest one to mSelectedEvent.
-        if (mSelectedEvents.size() > 0) {
-            int len = mSelectedEvents.size();
-            Event closestEvent = null;
-            float minDist = mViewWidth + mViewHeight; // some large distance
-            for (int index = 0; index < len; index++) {
-                Event ev = mSelectedEvents.get(index);
-                float dist = geometry.pointToEvent(x, y, ev);
-                if (dist < minDist) {
-                    minDist = dist;
-                    closestEvent = ev;
-                }
-            }
-            setSelectedEvent(closestEvent);
-
-            // Keep the selected hour and day consistent with the selected
-            // event. They could be different if we touched on an empty hour
-            // slot very close to an event in the previous hour slot. In
-            // that case we will select the nearby event.
-            int startDay = mSelectedEvent.startDay;
-            int endDay = mSelectedEvent.endDay;
-            if (mSelectionDay < startDay) {
-                setSelectedDay(startDay);
-            } else if (mSelectionDay > endDay) {
-                setSelectedDay(endDay);
-            }
-
-            int startHour = mSelectedEvent.startTime / 60;
-            int endHour;
-            if (mSelectedEvent.startTime < mSelectedEvent.endTime) {
-                endHour = (mSelectedEvent.endTime - 1) / 60;
-            } else {
-                endHour = mSelectedEvent.endTime / 60;
-            }
-
-            if (mSelectionHour < startHour && mSelectionDay == startDay) {
-                setSelectedHour(startHour);
-            } else if (mSelectionHour > endHour && mSelectionDay == endDay) {
-                setSelectedHour(endHour);
-            }
-        }
-    }
-
-    // Encapsulates the code to continue the scrolling after the
-    // finger is lifted. Instead of stopping the scroll immediately,
-    // the scroll continues to "free spin" and gradually slows down.
-    private class ContinueScroll implements Runnable {
-
-        public void run() {
-            mScrolling = mScrolling && mScroller.computeScrollOffset();
-            if (!mScrolling || mPaused) {
-                resetSelectedHour();
-                invalidate();
-                return;
-            }
-
-            mViewStartY = mScroller.getCurrY();
-
-            if (mCallEdgeEffectOnAbsorb) {
-                if (mViewStartY < 0) {
-                    mEdgeEffectTop.onAbsorb((int) mLastVelocity);
-                    mCallEdgeEffectOnAbsorb = false;
-                } else if (mViewStartY > mMaxViewStartY) {
-                    mEdgeEffectBottom.onAbsorb((int) mLastVelocity);
-                    mCallEdgeEffectOnAbsorb = false;
-                }
-                mLastVelocity = mScroller.getCurrVelocity();
-            }
-
-            if (mScrollStartY == 0 || mScrollStartY == mMaxViewStartY) {
-                // Allow overscroll/springback only on a fling,
-                // not a pull/fling from the end
-                if (mViewStartY < 0) {
-                    mViewStartY = 0;
-                } else if (mViewStartY > mMaxViewStartY) {
-                    mViewStartY = mMaxViewStartY;
-                }
-            }
-
-            computeFirstHour();
-            mHandler.post(this);
-            invalidate();
-        }
-    }
-
-    /**
-     * Cleanup the pop-up and timers.
-     */
-    public void cleanup() {
-        // Protect against null-pointer exceptions
-        if (mPopup != null) {
-            mPopup.dismiss();
-        }
-        mPaused = true;
-        mLastPopupEventID = INVALID_EVENT_ID;
-        if (mHandler != null) {
-            mHandler.removeCallbacks(mDismissPopup);
-            mHandler.removeCallbacks(mUpdateCurrentTime);
-        }
-
-        Utils.setSharedPreference(mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT,
-            mCellHeight);
-        // Clear all click animations
-        eventClickCleanup();
-        // Turn off redraw
-        mRemeasure = false;
-        // Turn off scrolling to make sure the view is in the correct state if we fling back to it
-        mScrolling = false;
-    }
-
-    private void eventClickCleanup() {
-        this.removeCallbacks(mClearClick);
-        this.removeCallbacks(mSetClick);
-        mClickedEvent = null;
-        mSavedClickedEvent = null;
-    }
-
-    private void setSelectedEvent(Event e) {
-        mSelectedEvent = e;
-        mSelectedEventForAccessibility = e;
-    }
-
-    private void setSelectedHour(int h) {
-        mSelectionHour = h;
-        mSelectionHourForAccessibility = h;
-    }
-    private void setSelectedDay(int d) {
-        mSelectionDay = d;
-        mSelectionDayForAccessibility = d;
-    }
-
-    /**
-     * Restart the update timer
-     */
-    public void restartCurrentTimeUpdates() {
-        mPaused = false;
-        if (mHandler != null) {
-            mHandler.removeCallbacks(mUpdateCurrentTime);
-            mHandler.post(mUpdateCurrentTime);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        cleanup();
-        super.onDetachedFromWindow();
-    }
-
-    class DismissPopup implements Runnable {
-
-        public void run() {
-            // Protect against null-pointer exceptions
-            if (mPopup != null) {
-                mPopup.dismiss();
-            }
-        }
-    }
-
-    class UpdateCurrentTime implements Runnable {
-
-        public void run() {
-            long currentTime = System.currentTimeMillis();
-            mCurrentTime.set(currentTime);
-            //% causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.)
-            if (!DayView.this.mPaused) {
-                mHandler.postDelayed(mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY
-                        - (currentTime % UPDATE_CURRENT_TIME_DELAY));
-            }
-            mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime.gmtoff);
-            invalidate();
-        }
-    }
-
-    class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
-        @Override
-        public boolean onSingleTapUp(MotionEvent ev) {
-            if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp");
-            DayView.this.doSingleTapUp(ev);
-            return true;
-        }
-
-        @Override
-        public void onLongPress(MotionEvent ev) {
-            if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress");
-            DayView.this.doLongPress(ev);
-        }
-
-        @Override
-        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-            if (DEBUG) Log.e(TAG, "GestureDetector.onScroll");
-            eventClickCleanup();
-            if (mTouchStartedInAlldayArea) {
-                if (Math.abs(distanceX) < Math.abs(distanceY)) {
-                    // Make sure that click feedback is gone when you scroll from the
-                    // all day area
-                    invalidate();
-                    return false;
-                }
-                // don't scroll vertically if this started in the allday area
-                distanceY = 0;
-            }
-            DayView.this.doScroll(e1, e2, distanceX, distanceY);
-            return true;
-        }
-
-        @Override
-        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-            if (DEBUG) Log.e(TAG, "GestureDetector.onFling");
-
-            if (mTouchStartedInAlldayArea) {
-                if (Math.abs(velocityX) < Math.abs(velocityY)) {
-                    return false;
-                }
-                // don't fling vertically if this started in the allday area
-                velocityY = 0;
-            }
-            DayView.this.doFling(e1, e2, velocityX, velocityY);
-            return true;
-        }
-
-        @Override
-        public boolean onDown(MotionEvent ev) {
-            if (DEBUG) Log.e(TAG, "GestureDetector.onDown");
-            DayView.this.doDown(ev);
-            return true;
-        }
-    }
-
-    @Override
-    public boolean onLongClick(View v) {
-        return true;
-    }
-
-    // The rest of this file was borrowed from Launcher2 - PagedView.java
-    private static final int MINIMUM_SNAP_VELOCITY = 2200;
-
-    private class ScrollInterpolator implements Interpolator {
-        public ScrollInterpolator() {
-        }
-
-        public float getInterpolation(float t) {
-            t -= 1.0f;
-            t = t * t * t * t * t + 1;
-
-            if ((1 - t) * mAnimationDistance < 1) {
-                cancelAnimation();
-            }
-
-            return t;
-        }
-    }
-
-    private long calculateDuration(float delta, float width, float velocity) {
-        /*
-         * Here we compute a "distance" that will be used in the computation of
-         * the overall snap duration. This is a function of the actual distance
-         * that needs to be traveled; we keep this value close to half screen
-         * size in order to reduce the variance in snap duration as a function
-         * of the distance the page needs to travel.
-         */
-        final float halfScreenSize = width / 2;
-        float distanceRatio = delta / width;
-        float distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio);
-        float distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration;
-
-        velocity = Math.abs(velocity);
-        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
-
-        /*
-         * we want the page's snap velocity to approximately match the velocity
-         * at which the user flings, so we scale the duration by a value near to
-         * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to
-         * make it a little slower.
-         */
-        long duration = 6 * Math.round(1000 * Math.abs(distance / velocity));
-        if (DEBUG) {
-            Log.e(TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:"
-                    + distanceRatio + " distance:" + distance + " velocity:" + velocity
-                    + " duration:" + duration + " distanceInfluenceForSnapDuration:"
-                    + distanceInfluenceForSnapDuration);
-        }
-        return duration;
-    }
-
-    /*
-     * We want the duration of the page snap animation to be influenced by the
-     * distance that the screen has to travel, however, we don't want this
-     * duration to be effected in a purely linear fashion. Instead, we use this
-     * method to moderate the effect that the distance of travel has on the
-     * overall snap duration.
-     */
-    private float distanceInfluenceForSnapDuration(float f) {
-        f -= 0.5f; // center the values about 0.
-        f *= 0.3f * Math.PI / 2.0f;
-        return (float) Math.sin(f);
-    }
-}
diff --git a/src/com/android/calendar/DayView.kt b/src/com/android/calendar/DayView.kt
new file mode 100644
index 0000000..58126f2
--- /dev/null
+++ b/src/com/android/calendar/DayView.kt
@@ -0,0 +1,3990 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.calendar
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.app.Service
+import android.content.Context
+import android.content.res.Resources
+import android.content.res.TypedArray
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Paint.Align
+import android.graphics.Paint.Style
+import android.graphics.Rect
+import android.graphics.Typeface
+import android.graphics.drawable.Drawable
+import android.os.Handler
+import android.provider.CalendarContract.Attendees
+import android.provider.CalendarContract.Calendars
+import android.text.Layout.Alignment
+import android.text.SpannableStringBuilder
+import android.text.StaticLayout
+import android.text.TextPaint
+import android.text.format.DateFormat
+import android.text.format.DateUtils
+import android.text.format.Time
+import android.text.style.StyleSpan
+import android.util.Log
+import android.view.ContextMenu
+import android.view.ContextMenu.ContextMenuInfo
+import android.view.GestureDetector
+import android.view.KeyEvent
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.MotionEvent
+import android.view.ScaleGestureDetector
+import android.view.View
+import android.view.ViewConfiguration
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams
+import android.view.WindowManager
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityManager
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.view.animation.Animation
+import android.view.animation.Interpolator
+import android.view.animation.TranslateAnimation
+import android.widget.EdgeEffect
+import android.widget.OverScroller
+import android.widget.PopupWindow
+import android.widget.ViewSwitcher
+import com.android.calendar.CalendarController.EventType
+import com.android.calendar.CalendarController.ViewType
+import java.util.ArrayList
+import java.util.Arrays
+import java.util.Calendar
+import java.util.Formatter
+import java.util.Locale
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+/**
+ * View for multi-day view. So far only 1 and 7 day have been tested.
+ */
+class DayView(
+    context: Context?,
+    controller: CalendarController?,
+    viewSwitcher: ViewSwitcher?,
+    eventLoader: EventLoader?,
+    numDays: Int
+) : View(context), View.OnCreateContextMenuListener, ScaleGestureDetector.OnScaleGestureListener,
+    View.OnClickListener, View.OnLongClickListener {
+    private var mOnFlingCalled = false
+    private var mStartingScroll = false
+    protected var mPaused = true
+    private var mHandler: Handler? = null
+
+    /**
+     * ID of the last event which was displayed with the toast popup.
+     *
+     * This is used to prevent popping up multiple quick views for the same event, especially
+     * during calendar syncs. This becomes valid when an event is selected, either by default
+     * on starting calendar or by scrolling to an event. It becomes invalid when the user
+     * explicitly scrolls to an empty time slot, changes views, or deletes the event.
+     */
+    private var mLastPopupEventID: Long
+    protected var mContext: Context? = null
+    private val mContinueScroll: ContinueScroll = ContinueScroll()
+
+    // Make this visible within the package for more informative debugging
+    var mBaseDate: Time? = null
+    private var mCurrentTime: Time? = null
+    private val mUpdateCurrentTime: UpdateCurrentTime = UpdateCurrentTime()
+    private var mTodayJulianDay = 0
+    private val mBold: Typeface = Typeface.DEFAULT_BOLD
+    private var mFirstJulianDay = 0
+    private var mLoadedFirstJulianDay = -1
+    private var mLastJulianDay = 0
+    private var mMonthLength = 0
+    private var mFirstVisibleDate = 0
+    private var mFirstVisibleDayOfWeek = 0
+    private var mEarliestStartHour: IntArray? = null // indexed by the week day offset
+    private var mHasAllDayEvent: BooleanArray? = null // indexed by the week day offset
+    private var mEventCountTemplate: String? = null
+    private var mClickedEvent: Event? = null // The event the user clicked on
+    private var mSavedClickedEvent: Event? = null
+    private var mClickedYLocation = 0
+    private var mDownTouchTime: Long = 0
+    private var mEventsAlpha = 255
+    private var mEventsCrossFadeAnimation: ObjectAnimator? = null
+    private val mTZUpdater: Runnable = object : Runnable {
+        @Override
+        override fun run() {
+            val tz: String? = Utils.getTimeZone(mContext, this)
+            mBaseDate!!.timezone = tz
+            mBaseDate?.normalize(true)
+            mCurrentTime?.switchTimezone(tz)
+            invalidate()
+        }
+    }
+
+    // Sets the "clicked" color from the clicked event
+    private val mSetClick: Runnable = object : Runnable {
+        @Override
+        override fun run() {
+            mClickedEvent = mSavedClickedEvent
+            mSavedClickedEvent = null
+            this@DayView.invalidate()
+        }
+    }
+
+    // Clears the "clicked" color from the clicked event and launch the event
+    private val mClearClick: Runnable = object : Runnable {
+        @Override
+        override fun run() {
+            if (mClickedEvent != null) {
+                mController?.sendEventRelatedEvent(
+                    this as Object?, EventType.VIEW_EVENT, mClickedEvent!!.id,
+                    mClickedEvent!!.startMillis, mClickedEvent!!.endMillis,
+                    this@DayView.getWidth() / 2, mClickedYLocation,
+                    selectedTimeInMillis
+                )
+            }
+            mClickedEvent = null
+            this@DayView.invalidate()
+        }
+    }
+    private val mTodayAnimatorListener: TodayAnimatorListener = TodayAnimatorListener()
+
+    internal inner class TodayAnimatorListener : AnimatorListenerAdapter() {
+        @Volatile
+        private var mAnimator: Animator? = null
+
+        @Volatile
+        private var mFadingIn = false
+        @Override
+        override fun onAnimationEnd(animation: Animator) {
+            synchronized(this) {
+                if (mAnimator !== animation) {
+                    animation.removeAllListeners()
+                    animation.cancel()
+                    return
+                }
+                if (mFadingIn) {
+                    if (mTodayAnimator != null) {
+                        mTodayAnimator?.removeAllListeners()
+                        mTodayAnimator?.cancel()
+                    }
+                    mTodayAnimator = ObjectAnimator
+                        .ofInt(this@DayView, "animateTodayAlpha", 255, 0)
+                    mAnimator = mTodayAnimator
+                    mFadingIn = false
+                    mTodayAnimator?.addListener(this)
+                    mTodayAnimator?.setDuration(600)
+                    mTodayAnimator?.start()
+                } else {
+                    mAnimateToday = false
+                    mAnimateTodayAlpha = 0
+                    mAnimator?.removeAllListeners()
+                    mAnimator = null
+                    mTodayAnimator = null
+                    invalidate()
+                }
+            }
+        }
+
+        fun setAnimator(animation: Animator?) {
+            mAnimator = animation
+        }
+
+        fun setFadingIn(fadingIn: Boolean) {
+            mFadingIn = fadingIn
+        }
+    }
+
+    var mAnimatorListener: AnimatorListenerAdapter = object : AnimatorListenerAdapter() {
+        @Override
+        override fun onAnimationStart(animation: Animator?) {
+            mScrolling = true
+        }
+
+        @Override
+        override fun onAnimationCancel(animation: Animator?) {
+            mScrolling = false
+        }
+
+        @Override
+        override fun onAnimationEnd(animation: Animator?) {
+            mScrolling = false
+            resetSelectedHour()
+            invalidate()
+        }
+    }
+
+    /**
+     * This variable helps to avoid unnecessarily reloading events by keeping
+     * track of the start millis parameter used for the most recent loading
+     * of events.  If the next reload matches this, then the events are not
+     * reloaded.  To force a reload, set this to zero (this is set to zero
+     * in the method clearCachedEvents()).
+     */
+    private var mLastReloadMillis: Long = 0
+    private var mEvents: ArrayList<Event> = ArrayList<Event>()
+    private var mAllDayEvents: ArrayList<Event>? = ArrayList<Event>()
+    private var mLayouts: Array<StaticLayout?>? = null
+    private var mAllDayLayouts: Array<StaticLayout?>? = null
+    private var mSelectionDay = 0 // Julian day
+    private var mSelectionHour = 0
+    var mSelectionAllday = false
+
+    // Current selection info for accessibility
+    private var mSelectionDayForAccessibility = 0 // Julian day
+    private var mSelectionHourForAccessibility = 0
+    private var mSelectedEventForAccessibility: Event? = null
+
+    // Last selection info for accessibility
+    private var mLastSelectionDayForAccessibility = 0
+    private var mLastSelectionHourForAccessibility = 0
+    private var mLastSelectedEventForAccessibility: Event? = null
+
+    /** Width of a day or non-conflicting event  */
+    private var mCellWidth = 0
+
+    // Pre-allocate these objects and re-use them
+    private val mRect: Rect = Rect()
+    private val mDestRect: Rect = Rect()
+    private val mSelectionRect: Rect = Rect()
+
+    // This encloses the more allDay events icon
+    private val mExpandAllDayRect: Rect = Rect()
+
+    // TODO Clean up paint usage
+    private val mPaint: Paint = Paint()
+    private val mEventTextPaint: Paint = Paint()
+    private val mSelectionPaint: Paint = Paint()
+    private var mLines: FloatArray = emptyArray<Float>().toFloatArray()
+    private var mFirstDayOfWeek = 0 // First day of the week
+    private var mPopup: PopupWindow? = null
+    private var mPopupView: View? = null
+    private val mDismissPopup: DismissPopup = DismissPopup()
+    private var mRemeasure = true
+    private val mEventLoader: EventLoader
+    protected val mEventGeometry: EventGeometry
+    private var mAnimationDistance = 0f
+    private var mViewStartX = 0
+    private var mViewStartY = 0
+    private var mMaxViewStartY = 0
+    private var mViewHeight = 0
+    private var mViewWidth = 0
+    private var mGridAreaHeight = -1
+    private var mScrollStartY = 0
+    private var mPreviousDirection = 0
+
+    /**
+     * Vertical distance or span between the two touch points at the start of a
+     * scaling gesture
+     */
+    private var mStartingSpanY = 0f
+
+    /** Height of 1 hour in pixels at the start of a scaling gesture  */
+    private var mCellHeightBeforeScaleGesture = 0
+
+    /** The hour at the center two touch points  */
+    private var mGestureCenterHour = 0f
+    private var mRecalCenterHour = false
+
+    /**
+     * Flag to decide whether to handle the up event. Cases where up events
+     * should be ignored are 1) right after a scale gesture and 2) finger was
+     * down before app launch
+     */
+    private var mHandleActionUp = true
+    private var mHoursTextHeight = 0
+
+    /**
+     * The height of the area used for allday events
+     */
+    private var mAlldayHeight = 0
+
+    /**
+     * The height of the allday event area used during animation
+     */
+    private var mAnimateDayHeight = 0
+
+    /**
+     * The height of an individual allday event during animation
+     */
+    private var mAnimateDayEventHeight = MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt()
+
+    /**
+     * Max of all day events in a given day in this view.
+     */
+    private var mMaxAlldayEvents = 0
+
+    /**
+     * A count of the number of allday events that were not drawn for each day
+     */
+    private var mSkippedAlldayEvents: IntArray? = null
+
+    /**
+     * The number of allDay events at which point we start hiding allDay events.
+     */
+    private var mMaxUnexpandedAlldayEventCount = 4
+    protected var mNumDays = 7
+    private var mNumHours = 10
+
+    /** Width of the time line (list of hours) to the left.  */
+    private var mHoursWidth = 0
+    private var mDateStrWidth = 0
+
+    /** Top of the scrollable region i.e. below date labels and all day events  */
+    private var mFirstCell = 0
+
+    /** First fully visible hour  */
+    private var mFirstHour = -1
+
+    /** Distance between the mFirstCell and the top of first fully visible hour.  */
+    private var mFirstHourOffset = 0
+    private var mHourStrs: Array<String>? = null
+    private var mDayStrs: Array<String?>? = null
+    private var mDayStrs2Letter: Array<String?>? = null
+    private var mIs24HourFormat = false
+    private val mSelectedEvents: ArrayList<Event> = ArrayList<Event>()
+    private var mComputeSelectedEvents = false
+    private var mUpdateToast = false
+    private var mSelectedEvent: Event? = null
+    private var mPrevSelectedEvent: Event? = null
+    private val mPrevBox: Rect = Rect()
+    protected val mResources: Resources
+    protected val mCurrentTimeLine: Drawable
+    protected val mCurrentTimeAnimateLine: Drawable
+    protected val mTodayHeaderDrawable: Drawable
+    protected val mExpandAlldayDrawable: Drawable
+    protected val mCollapseAlldayDrawable: Drawable
+    protected var mAcceptedOrTentativeEventBoxDrawable: Drawable
+    private var mAmString: String? = null
+    private var mPmString: String? = null
+    var mScaleGestureDetector: ScaleGestureDetector
+    private var mTouchMode = TOUCH_MODE_INITIAL_STATE
+    private var mSelectionMode = SELECTION_HIDDEN
+    private var mScrolling = false
+
+    // Pixels scrolled
+    private var mInitialScrollX = 0f
+    private var mInitialScrollY = 0f
+    private var mAnimateToday = false
+    private var mAnimateTodayAlpha = 0
+
+    // Animates the height of the allday region
+    var mAlldayAnimator: ObjectAnimator? = null
+
+    // Animates the height of events in the allday region
+    var mAlldayEventAnimator: ObjectAnimator? = null
+
+    // Animates the transparency of the more events text
+    var mMoreAlldayEventsAnimator: ObjectAnimator? = null
+
+    // Animates the current time marker when Today is pressed
+    var mTodayAnimator: ObjectAnimator? = null
+
+    // whether or not an event is stopping because it was cancelled
+    private var mCancellingAnimations = false
+
+    // tracks whether a touch originated in the allday area
+    private var mTouchStartedInAlldayArea = false
+    private val mController: CalendarController
+    private val mViewSwitcher: ViewSwitcher
+    private val mGestureDetector: GestureDetector
+    private val mScroller: OverScroller
+    private val mEdgeEffectTop: EdgeEffect
+    private val mEdgeEffectBottom: EdgeEffect
+    private var mCallEdgeEffectOnAbsorb = false
+    private val OVERFLING_DISTANCE: Int
+    private var mLastVelocity = 0f
+    private val mHScrollInterpolator: ScrollInterpolator
+    private var mAccessibilityMgr: AccessibilityManager? = null
+    private var mIsAccessibilityEnabled = false
+    private var mTouchExplorationEnabled = false
+    private val mNewEventHintString: String
+    @Override
+    protected override fun onAttachedToWindow() {
+        if (mHandler == null) {
+            mHandler = getHandler()
+            mHandler?.post(mUpdateCurrentTime)
+        }
+    }
+
+    private fun init(context: Context) {
+        setFocusable(true)
+
+        // Allow focus in touch mode so that we can do keyboard shortcuts
+        // even after we've entered touch mode.
+        setFocusableInTouchMode(true)
+        setClickable(true)
+        setOnCreateContextMenuListener(this)
+        mFirstDayOfWeek = Utils.getFirstDayOfWeek(context)
+        mCurrentTime = Time(Utils.getTimeZone(context, mTZUpdater))
+        val currentTime: Long = System.currentTimeMillis()
+        mCurrentTime?.set(currentTime)
+        mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime!!.gmtoff)
+        mWeek_saturdayColor = mResources.getColor(R.color.week_saturday)
+        mWeek_sundayColor = mResources.getColor(R.color.week_sunday)
+        mCalendarDateBannerTextColor = mResources.getColor(R.color.calendar_date_banner_text_color)
+        mFutureBgColorRes = mResources.getColor(R.color.calendar_future_bg_color)
+        mBgColor = mResources.getColor(R.color.calendar_hour_background)
+        mCalendarAmPmLabel = mResources.getColor(R.color.calendar_ampm_label)
+        mCalendarGridAreaSelected = mResources.getColor(R.color.calendar_grid_area_selected)
+        mCalendarGridLineInnerHorizontalColor = mResources
+            .getColor(R.color.calendar_grid_line_inner_horizontal_color)
+        mCalendarGridLineInnerVerticalColor = mResources
+            .getColor(R.color.calendar_grid_line_inner_vertical_color)
+        mCalendarHourLabelColor = mResources.getColor(R.color.calendar_hour_label)
+        mEventTextColor = mResources.getColor(R.color.calendar_event_text_color)
+        mMoreEventsTextColor = mResources.getColor(R.color.month_event_other_color)
+        mEventTextPaint.setTextSize(EVENT_TEXT_FONT_SIZE)
+        mEventTextPaint.setTextAlign(Paint.Align.LEFT)
+        mEventTextPaint.setAntiAlias(true)
+        val gridLineColor: Int = mResources.getColor(R.color.calendar_grid_line_highlight_color)
+        var p: Paint = mSelectionPaint
+        p.setColor(gridLineColor)
+        p.setStyle(Style.FILL)
+        p.setAntiAlias(false)
+        p = mPaint
+        p.setAntiAlias(true)
+
+        // Allocate space for 2 weeks worth of weekday names so that we can
+        // easily start the week display at any week day.
+        mDayStrs = arrayOfNulls(14)
+
+        // Also create an array of 2-letter abbreviations.
+        mDayStrs2Letter = arrayOfNulls(14)
+        for (i in Calendar.SUNDAY..Calendar.SATURDAY) {
+            val index: Int = i - Calendar.SUNDAY
+            // e.g. Tue for Tuesday
+            mDayStrs!![index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_MEDIUM)
+                .toUpperCase()
+            mDayStrs!![index + 7] = mDayStrs!![index]
+            // e.g. Tu for Tuesday
+            mDayStrs2Letter!![index] = DateUtils.getDayOfWeekString(i, DateUtils.LENGTH_SHORT)
+                .toUpperCase()
+
+            // If we don't have 2-letter day strings, fall back to 1-letter.
+            if (mDayStrs2Letter!![index]!!.equals(mDayStrs!![index])) {
+                mDayStrs2Letter!![index] = DateUtils.getDayOfWeekString(i,
+                DateUtils.LENGTH_SHORTEST)
+            }
+            mDayStrs2Letter!![index + 7] = mDayStrs2Letter!![index]
+        }
+
+        // Figure out how much space we need for the 3-letter abbrev names
+        // in the worst case.
+        p.setTextSize(DATE_HEADER_FONT_SIZE)
+        p.setTypeface(mBold)
+        val dateStrs = arrayOf<String?>(" 28", " 30")
+        mDateStrWidth = computeMaxStringWidth(0, dateStrs, p)
+        p.setTextSize(DAY_HEADER_FONT_SIZE)
+        mDateStrWidth += computeMaxStringWidth(0, mDayStrs as Array<String?>, p)
+        p.setTextSize(HOURS_TEXT_SIZE)
+        p.setTypeface(null)
+        handleOnResume()
+        mAmString = DateUtils.getAMPMString(Calendar.AM).toUpperCase()
+        mPmString = DateUtils.getAMPMString(Calendar.PM).toUpperCase()
+        val ampm = arrayOf(mAmString, mPmString)
+        p.setTextSize(AMPM_TEXT_SIZE)
+        mHoursWidth = Math.max(
+            HOURS_MARGIN, computeMaxStringWidth(mHoursWidth, ampm, p) +
+                HOURS_RIGHT_MARGIN
+        )
+        mHoursWidth = Math.max(MIN_HOURS_WIDTH, mHoursWidth)
+        val inflater: LayoutInflater
+        inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        mPopupView = inflater.inflate(R.layout.bubble_event, null)
+        mPopupView?.setLayoutParams(
+            LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT
+            )
+        )
+        mPopup = PopupWindow(context)
+        mPopup?.setContentView(mPopupView)
+        val dialogTheme: Resources.Theme = getResources().newTheme()
+        dialogTheme.applyStyle(android.R.style.Theme_Dialog, true)
+        val ta: TypedArray = dialogTheme.obtainStyledAttributes(
+            intArrayOf(
+                android.R.attr.windowBackground
+            )
+        )
+        mPopup?.setBackgroundDrawable(ta.getDrawable(0))
+        ta.recycle()
+
+        // Enable touching the popup window
+        mPopupView?.setOnClickListener(this)
+        // Catch long clicks for creating a new event
+        setOnLongClickListener(this)
+        mBaseDate = Time(Utils.getTimeZone(context, mTZUpdater))
+        val millis: Long = System.currentTimeMillis()
+        mBaseDate?.set(millis)
+        mEarliestStartHour = IntArray(mNumDays)
+        mHasAllDayEvent = BooleanArray(mNumDays)
+
+        // mLines is the array of points used with Canvas.drawLines() in
+        // drawGridBackground() and drawAllDayEvents().  Its size depends
+        // on the max number of lines that can ever be drawn by any single
+        // drawLines() call in either of those methods.
+        val maxGridLines = (24 + 1 + // max horizontal lines we might draw
+            (mNumDays + 1)) // max vertical lines we might draw
+        mLines = FloatArray(maxGridLines * 4)
+    }
+
+    /**
+     * This is called when the popup window is pressed.
+     */
+    override fun onClick(v: View) {
+        if (v === mPopupView) {
+            // Pretend it was a trackball click because that will always
+            // jump to the "View event" screen.
+            switchViews(true /* trackball */)
+        }
+    }
+
+    fun handleOnResume() {
+        initAccessibilityVariables()
+        if (Utils.getSharedPreference(mContext, OtherPreferences.KEY_OTHER_1, false)) {
+            mFutureBgColor = 0
+        } else {
+            mFutureBgColor = mFutureBgColorRes
+        }
+        mIs24HourFormat = DateFormat.is24HourFormat(mContext)
+        mHourStrs = if (mIs24HourFormat) CalendarData.s24Hours else CalendarData.s12HoursNoAmPm
+        mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext)
+        mLastSelectionDayForAccessibility = 0
+        mLastSelectionHourForAccessibility = 0
+        mLastSelectedEventForAccessibility = null
+        mSelectionMode = SELECTION_HIDDEN
+    }
+
+    private fun initAccessibilityVariables() {
+        mAccessibilityMgr = mContext
+            ?.getSystemService(Service.ACCESSIBILITY_SERVICE) as AccessibilityManager
+        mIsAccessibilityEnabled = mAccessibilityMgr != null && mAccessibilityMgr!!.isEnabled()
+        mTouchExplorationEnabled = isTouchExplorationEnabled
+    } /* ignore isDst */ // We ignore the "isDst" field because we want normalize() to figure
+    // out the correct DST value and not adjust the selected time based
+    // on the current setting of DST.
+    /**
+     * Returns the start of the selected time in milliseconds since the epoch.
+     *
+     * @return selected time in UTC milliseconds since the epoch.
+     */
+    val selectedTimeInMillis: Long
+        get() {
+            val time = Time(mBaseDate)
+            time.setJulianDay(mSelectionDay)
+            time.hour = mSelectionHour
+
+            // We ignore the "isDst" field because we want normalize() to figure
+            // out the correct DST value and not adjust the selected time based
+            // on the current setting of DST.
+            return time.normalize(true /* ignore isDst */)
+        } /* ignore isDst */
+
+    // We ignore the "isDst" field because we want normalize() to figure
+    // out the correct DST value and not adjust the selected time based
+    // on the current setting of DST.
+    val selectedTime: Time
+        get() {
+            val time = Time(mBaseDate)
+            time.setJulianDay(mSelectionDay)
+            time.hour = mSelectionHour
+
+            // We ignore the "isDst" field because we want normalize() to figure
+            // out the correct DST value and not adjust the selected time based
+            // on the current setting of DST.
+            time.normalize(true /* ignore isDst */)
+            return time
+        } /* ignore isDst */
+
+    // We ignore the "isDst" field because we want normalize() to figure
+    // out the correct DST value and not adjust the selected time based
+    // on the current setting of DST.
+    val selectedTimeForAccessibility: Time
+        get() {
+            val time = Time(mBaseDate)
+            time.setJulianDay(mSelectionDayForAccessibility)
+            time.hour = mSelectionHourForAccessibility
+
+            // We ignore the "isDst" field because we want normalize() to figure
+            // out the correct DST value and not adjust the selected time based
+            // on the current setting of DST.
+            time.normalize(true /* ignore isDst */)
+            return time
+        }
+
+    /**
+     * Returns the start of the selected time in minutes since midnight,
+     * local time.  The derived class must ensure that this is consistent
+     * with the return value from getSelectedTimeInMillis().
+     */
+    val selectedMinutesSinceMidnight: Int
+        get() = mSelectionHour * MINUTES_PER_HOUR
+    var firstVisibleHour: Int
+        get() = mFirstHour
+        set(firstHour) {
+            mFirstHour = firstHour
+            mFirstHourOffset = 0
+        }
+
+    fun setSelected(time: Time?, ignoreTime: Boolean, animateToday: Boolean) {
+        mBaseDate?.set(time)
+        setSelectedHour(mBaseDate!!.hour)
+        setSelectedEvent(null)
+        mPrevSelectedEvent = null
+        val millis: Long = mBaseDate!!.toMillis(false /* use isDst */)
+        setSelectedDay(Time.getJulianDay(millis, mBaseDate!!.gmtoff))
+        mSelectedEvents.clear()
+        mComputeSelectedEvents = true
+        var gotoY: Int = Integer.MIN_VALUE
+        if (!ignoreTime && mGridAreaHeight != -1) {
+            var lastHour = 0
+            if (mBaseDate!!.hour < mFirstHour) {
+                // Above visible region
+                gotoY = mBaseDate!!.hour * (mCellHeight + HOUR_GAP)
+            } else {
+                lastHour = ((mGridAreaHeight - mFirstHourOffset) / (mCellHeight + HOUR_GAP) +
+                    mFirstHour)
+                if (mBaseDate!!.hour >= lastHour) {
+                    // Below visible region
+
+                    // target hour + 1 (to give it room to see the event) -
+                    // grid height (to get the y of the top of the visible
+                    // region)
+                    gotoY = ((mBaseDate!!.hour + 1 + mBaseDate!!.minute / 60.0f) *
+                        (mCellHeight + HOUR_GAP) - mGridAreaHeight).toInt()
+                }
+            }
+            if (DEBUG) {
+                Log.e(
+                    TAG, "Go " + gotoY + " 1st " + mFirstHour + ":" + mFirstHourOffset + "CH " +
+                        (mCellHeight + HOUR_GAP) + " lh " + lastHour + " gh " + mGridAreaHeight +
+                        " ymax " + mMaxViewStartY
+                )
+            }
+            if (gotoY > mMaxViewStartY) {
+                gotoY = mMaxViewStartY
+            } else if (gotoY < 0 && gotoY != Integer.MIN_VALUE) {
+                gotoY = 0
+            }
+        }
+        recalc()
+        mRemeasure = true
+        invalidate()
+        var delayAnimateToday = false
+        if (gotoY != Integer.MIN_VALUE) {
+            val scrollAnim: ValueAnimator =
+                ObjectAnimator.ofInt(this, "viewStartY", mViewStartY, gotoY)
+            scrollAnim.setDuration(GOTO_SCROLL_DURATION.toLong())
+            scrollAnim.setInterpolator(AccelerateDecelerateInterpolator())
+            scrollAnim.addListener(mAnimatorListener)
+            scrollAnim.start()
+            delayAnimateToday = true
+        }
+        if (animateToday) {
+            synchronized(mTodayAnimatorListener) {
+                if (mTodayAnimator != null) {
+                    mTodayAnimator?.removeAllListeners()
+                    mTodayAnimator?.cancel()
+                }
+                mTodayAnimator = ObjectAnimator.ofInt(
+                    this, "animateTodayAlpha",
+                    mAnimateTodayAlpha, 255
+                )
+                mAnimateToday = true
+                mTodayAnimatorListener.setFadingIn(true)
+                mTodayAnimatorListener.setAnimator(mTodayAnimator)
+                mTodayAnimator?.addListener(mTodayAnimatorListener)
+                mTodayAnimator?.setDuration(150)
+                if (delayAnimateToday) {
+                    mTodayAnimator?.setStartDelay(GOTO_SCROLL_DURATION.toLong())
+                }
+                mTodayAnimator?.start()
+            }
+        }
+        sendAccessibilityEventAsNeeded(false)
+    }
+
+    // Called from animation framework via reflection. Do not remove
+    fun setViewStartY(viewStartY: Int) {
+        var viewStartY = viewStartY
+        if (viewStartY > mMaxViewStartY) {
+            viewStartY = mMaxViewStartY
+        }
+        mViewStartY = viewStartY
+        computeFirstHour()
+        invalidate()
+    }
+
+    fun setAnimateTodayAlpha(todayAlpha: Int) {
+        mAnimateTodayAlpha = todayAlpha
+        invalidate()
+    } /* ignore isDst */
+
+    fun getSelectedDay(): Time {
+        val time = Time(mBaseDate)
+        time.setJulianDay(mSelectionDay)
+        time.hour = mSelectionHour
+
+        // We ignore the "isDst" field because we want normalize() to figure
+        // out the correct DST value and not adjust the selected time based
+        // on the current setting of DST.
+        time.normalize(true /* ignore isDst */)
+        return time
+    }
+
+    fun updateTitle() {
+        val start = Time(mBaseDate)
+        start.normalize(true)
+        val end = Time(start)
+        end.monthDay += mNumDays - 1
+        // Move it forward one minute so the formatter doesn't lose a day
+        end.minute += 1
+        end.normalize(true)
+        var formatFlags: Long = DateUtils.FORMAT_SHOW_DATE.toLong() or
+            DateUtils.FORMAT_SHOW_YEAR.toLong()
+        if (mNumDays != 1) {
+            // Don't show day of the month if for multi-day view
+            formatFlags = formatFlags or DateUtils.FORMAT_NO_MONTH_DAY.toLong()
+
+            // Abbreviate the month if showing multiple months
+            if (start.month !== end.month) {
+                formatFlags = formatFlags or DateUtils.FORMAT_ABBREV_MONTH.toLong()
+            }
+        }
+        mController.sendEvent(
+            this as Object?, EventType.UPDATE_TITLE, start, end, null, -1, ViewType.CURRENT,
+            formatFlags, null, null
+        )
+    }
+
+    /**
+     * return a negative number if "time" is comes before the visible time
+     * range, a positive number if "time" is after the visible time range, and 0
+     * if it is in the visible time range.
+     */
+    fun compareToVisibleTimeRange(time: Time): Int {
+        val savedHour: Int = mBaseDate!!.hour
+        val savedMinute: Int = mBaseDate!!.minute
+        val savedSec: Int = mBaseDate!!.second
+        mBaseDate!!.hour = 0
+        mBaseDate!!.minute = 0
+        mBaseDate!!.second = 0
+        if (DEBUG) {
+            Log.d(TAG, "Begin " + mBaseDate.toString())
+            Log.d(TAG, "Diff  " + time.toString())
+        }
+
+        // Compare beginning of range
+        var diff: Int = Time.compare(time, mBaseDate)
+        if (diff > 0) {
+            // Compare end of range
+            mBaseDate!!.monthDay += mNumDays
+            mBaseDate?.normalize(true)
+            diff = Time.compare(time, mBaseDate)
+            if (DEBUG) Log.d(TAG, "End   " + mBaseDate.toString())
+            mBaseDate!!.monthDay -= mNumDays
+            mBaseDate?.normalize(true)
+            if (diff < 0) {
+                // in visible time
+                diff = 0
+            } else if (diff == 0) {
+                // Midnight of following day
+                diff = 1
+            }
+        }
+        if (DEBUG) Log.d(TAG, "Diff: $diff")
+        mBaseDate!!.hour = savedHour
+        mBaseDate!!.minute = savedMinute
+        mBaseDate!!.second = savedSec
+        return diff
+    }
+
+    private fun recalc() {
+        // Set the base date to the beginning of the week if we are displaying
+        // 7 days at a time.
+        if (mNumDays == 7) {
+            adjustToBeginningOfWeek(mBaseDate)
+        }
+        val start: Long = mBaseDate!!.toMillis(false /* use isDst */)
+        mFirstJulianDay = Time.getJulianDay(start, mBaseDate!!.gmtoff)
+        mLastJulianDay = mFirstJulianDay + mNumDays - 1
+        mMonthLength = mBaseDate!!.getActualMaximum(Time.MONTH_DAY)
+        mFirstVisibleDate = mBaseDate!!.monthDay
+        mFirstVisibleDayOfWeek = mBaseDate!!.weekDay
+    }
+
+    private fun adjustToBeginningOfWeek(time: Time?) {
+        val dayOfWeek: Int = time!!.weekDay
+        var diff = dayOfWeek - mFirstDayOfWeek
+        if (diff != 0) {
+            if (diff < 0) {
+                diff += 7
+            }
+            time!!.monthDay -= diff
+            time?.normalize(true /* ignore isDst */)
+        }
+    }
+
+    @Override
+    protected override fun onSizeChanged(width: Int, height: Int, oldw: Int, oldh: Int) {
+        mViewWidth = width
+        mViewHeight = height
+        mEdgeEffectTop.setSize(mViewWidth, mViewHeight)
+        mEdgeEffectBottom.setSize(mViewWidth, mViewHeight)
+        val gridAreaWidth = width - mHoursWidth
+        mCellWidth = (gridAreaWidth - mNumDays * DAY_GAP) / mNumDays
+
+        // This would be about 1 day worth in a 7 day view
+        mHorizontalSnapBackThreshold = width / 7
+        val p = Paint()
+        p.setTextSize(HOURS_TEXT_SIZE)
+        mHoursTextHeight = Math.abs(p.ascent()).toInt()
+        remeasure(width, height)
+    }
+
+    /**
+     * Measures the space needed for various parts of the view after
+     * loading new events.  This can change if there are all-day events.
+     */
+    private fun remeasure(width: Int, height: Int) {
+        // Shrink to fit available space but make sure we can display at least two events
+        MAX_UNEXPANDED_ALLDAY_HEIGHT = (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4).toInt()
+        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.min(MAX_UNEXPANDED_ALLDAY_HEIGHT, height / 6)
+        MAX_UNEXPANDED_ALLDAY_HEIGHT = Math.max(
+            MAX_UNEXPANDED_ALLDAY_HEIGHT,
+            MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt() * 2
+        )
+        mMaxUnexpandedAlldayEventCount =
+            (MAX_UNEXPANDED_ALLDAY_HEIGHT / MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt()
+
+        // First, clear the array of earliest start times, and the array
+        // indicating presence of an all-day event.
+        for (day in 0 until mNumDays) {
+            mEarliestStartHour!![day] = 25 // some big number
+            mHasAllDayEvent!![day] = false
+        }
+        val maxAllDayEvents = mMaxAlldayEvents
+
+        // The min is where 24 hours cover the entire visible area
+        mMinCellHeight = Math.max((height - DAY_HEADER_HEIGHT) / 24, MIN_EVENT_HEIGHT.toInt())
+        if (mCellHeight < mMinCellHeight) {
+            mCellHeight = mMinCellHeight
+        }
+
+        // Calculate mAllDayHeight
+        mFirstCell = DAY_HEADER_HEIGHT
+        var allDayHeight = 0
+        if (maxAllDayEvents > 0) {
+            val maxAllAllDayHeight = height - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT
+            // If there is at most one all-day event per day, then use less
+            // space (but more than the space for a single event).
+            if (maxAllDayEvents == 1) {
+                allDayHeight = SINGLE_ALLDAY_HEIGHT
+            } else if (maxAllDayEvents <= mMaxUnexpandedAlldayEventCount) {
+                // Allow the all-day area to grow in height depending on the
+                // number of all-day events we need to show, up to a limit.
+                allDayHeight = maxAllDayEvents * MAX_HEIGHT_OF_ONE_ALLDAY_EVENT
+                if (allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
+                    allDayHeight = MAX_UNEXPANDED_ALLDAY_HEIGHT
+                }
+            } else {
+                // if we have more than the magic number, check if we're animating
+                // and if not adjust the sizes appropriately
+                if (mAnimateDayHeight != 0) {
+                    // Don't shrink the space past the final allDay space. The animation
+                    // continues to hide the last event so the more events text can
+                    // fade in.
+                    allDayHeight = Math.max(mAnimateDayHeight, MAX_UNEXPANDED_ALLDAY_HEIGHT)
+                } else {
+                    // Try to fit all the events in
+                    allDayHeight = (maxAllDayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt()
+                    // But clip the area depending on which mode we're in
+                    if (!mShowAllAllDayEvents && allDayHeight > MAX_UNEXPANDED_ALLDAY_HEIGHT) {
+                        allDayHeight = (mMaxUnexpandedAlldayEventCount *
+                            MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt()
+                    } else if (allDayHeight > maxAllAllDayHeight) {
+                        allDayHeight = maxAllAllDayHeight
+                    }
+                }
+            }
+            mFirstCell = DAY_HEADER_HEIGHT + allDayHeight + ALLDAY_TOP_MARGIN
+        } else {
+            mSelectionAllday = false
+        }
+        mAlldayHeight = allDayHeight
+        mGridAreaHeight = height - mFirstCell
+
+        // Set up the expand icon position
+        val allDayIconWidth: Int = mExpandAlldayDrawable.getIntrinsicWidth()
+        mExpandAllDayRect.left = Math.max(
+            (mHoursWidth - allDayIconWidth) / 2,
+            EVENT_ALL_DAY_TEXT_LEFT_MARGIN
+        )
+        mExpandAllDayRect.right = Math.min(
+            mExpandAllDayRect.left + allDayIconWidth, mHoursWidth -
+                EVENT_ALL_DAY_TEXT_RIGHT_MARGIN
+        )
+        mExpandAllDayRect.bottom = mFirstCell - EXPAND_ALL_DAY_BOTTOM_MARGIN
+        mExpandAllDayRect.top = (mExpandAllDayRect.bottom -
+            mExpandAlldayDrawable.getIntrinsicHeight())
+        mNumHours = mGridAreaHeight / (mCellHeight + HOUR_GAP)
+        mEventGeometry.setHourHeight(mCellHeight.toFloat())
+        val minimumDurationMillis =
+            (MIN_EVENT_HEIGHT * DateUtils.MINUTE_IN_MILLIS / (mCellHeight / 60.0f)).toLong()
+        Event.computePositions(mEvents, minimumDurationMillis)
+
+        // Compute the top of our reachable view
+        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight
+        if (DEBUG) {
+            Log.e(TAG, "mViewStartY: $mViewStartY")
+            Log.e(TAG, "mMaxViewStartY: $mMaxViewStartY")
+        }
+        if (mViewStartY > mMaxViewStartY) {
+            mViewStartY = mMaxViewStartY
+            computeFirstHour()
+        }
+        if (mFirstHour == -1) {
+            initFirstHour()
+            mFirstHourOffset = 0
+        }
+
+        // When we change the base date, the number of all-day events may
+        // change and that changes the cell height.  When we switch dates,
+        // we use the mFirstHourOffset from the previous view, but that may
+        // be too large for the new view if the cell height is smaller.
+        if (mFirstHourOffset >= mCellHeight + HOUR_GAP) {
+            mFirstHourOffset = mCellHeight + HOUR_GAP - 1
+        }
+        mViewStartY = mFirstHour * (mCellHeight + HOUR_GAP) - mFirstHourOffset
+        val eventAreaWidth = mNumDays * (mCellWidth + DAY_GAP)
+        // When we get new events we don't want to dismiss the popup unless the event changes
+        if (mSelectedEvent != null && mLastPopupEventID != mSelectedEvent!!.id) {
+            mPopup?.dismiss()
+        }
+        mPopup?.setWidth(eventAreaWidth - 20)
+        mPopup?.setHeight(WindowManager.LayoutParams.WRAP_CONTENT)
+    }
+
+    /**
+     * Initialize the state for another view.  The given view is one that has
+     * its own bitmap and will use an animation to replace the current view.
+     * The current view and new view are either both Week views or both Day
+     * views.  They differ in their base date.
+     *
+     * @param view the view to initialize.
+     */
+    private fun initView(view: DayView) {
+        view.setSelectedHour(mSelectionHour)
+        view.mSelectedEvents.clear()
+        view.mComputeSelectedEvents = true
+        view.mFirstHour = mFirstHour
+        view.mFirstHourOffset = mFirstHourOffset
+        view.remeasure(getWidth(), getHeight())
+        view.initAllDayHeights()
+        view.setSelectedEvent(null)
+        view.mPrevSelectedEvent = null
+        view.mFirstDayOfWeek = mFirstDayOfWeek
+        if (view.mEvents.size > 0) {
+            view.mSelectionAllday = mSelectionAllday
+        } else {
+            view.mSelectionAllday = false
+        }
+
+        // Redraw the screen so that the selection box will be redrawn.  We may
+        // have scrolled to a different part of the day in some other view
+        // so the selection box in this view may no longer be visible.
+        view.recalc()
+    }
+
+    /**
+     * Switch to another view based on what was selected (an event or a free
+     * slot) and how it was selected (by touch or by trackball).
+     *
+     * @param trackBallSelection true if the selection was made using the
+     * trackball.
+     */
+    private fun switchViews(trackBallSelection: Boolean) {
+        val selectedEvent: Event? = mSelectedEvent
+        mPopup?.dismiss()
+        mLastPopupEventID = INVALID_EVENT_ID
+        if (mNumDays > 1) {
+            // This is the Week view.
+            // With touch, we always switch to Day/Agenda View
+            // With track ball, if we selected a free slot, then create an event.
+            // If we selected a specific event, switch to EventInfo view.
+            if (trackBallSelection) {
+                if (selectedEvent != null) {
+                    if (mIsAccessibilityEnabled) {
+                        mAccessibilityMgr?.interrupt()
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
+        mScrolling = false
+        return super.onKeyUp(keyCode, event)
+    }
+
+    @Override
+    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
+        return super.onKeyDown(keyCode, event)
+    }
+
+    @Override
+    override fun onHoverEvent(event: MotionEvent?): Boolean {
+        return true
+    }
+
+    private val isTouchExplorationEnabled: Boolean
+        private get() = mIsAccessibilityEnabled && mAccessibilityMgr!!.isTouchExplorationEnabled()
+
+    private fun sendAccessibilityEventAsNeeded(speakEvents: Boolean) {
+        if (!mIsAccessibilityEnabled) {
+            return
+        }
+        val dayChanged = mLastSelectionDayForAccessibility != mSelectionDayForAccessibility
+        val hourChanged = mLastSelectionHourForAccessibility != mSelectionHourForAccessibility
+        if (dayChanged || hourChanged || mLastSelectedEventForAccessibility !==
+            mSelectedEventForAccessibility) {
+            mLastSelectionDayForAccessibility = mSelectionDayForAccessibility
+            mLastSelectionHourForAccessibility = mSelectionHourForAccessibility
+            mLastSelectedEventForAccessibility = mSelectedEventForAccessibility
+            val b = StringBuilder()
+
+            // Announce only the changes i.e. day or hour or both
+            if (dayChanged) {
+                b.append(selectedTimeForAccessibility.format("%A "))
+            }
+            if (hourChanged) {
+                b.append(selectedTimeForAccessibility.format(if (mIs24HourFormat) "%k" else "%l%p"))
+            }
+            if (dayChanged || hourChanged) {
+                b.append(PERIOD_SPACE)
+            }
+            if (speakEvents) {
+                if (mEventCountTemplate == null) {
+                    mEventCountTemplate = mContext?.getString(R.string.template_announce_item_index)
+                }
+
+                // Read out the relevant event(s)
+                val numEvents: Int = mSelectedEvents.size
+                if (numEvents > 0) {
+                    if (mSelectedEventForAccessibility == null) {
+                        // Read out all the events
+                        var i = 1
+                        for (calEvent in mSelectedEvents) {
+                            if (numEvents > 1) {
+                                // Read out x of numEvents if there are more than one event
+                                mStringBuilder.setLength(0)
+                                b.append(mFormatter.format(mEventCountTemplate, i++, numEvents))
+                                b.append(" ")
+                            }
+                            appendEventAccessibilityString(b, calEvent)
+                        }
+                    } else {
+                        if (numEvents > 1) {
+                            // Read out x of numEvents if there are more than one event
+                            mStringBuilder.setLength(0)
+                            b.append(
+                                mFormatter.format(
+                                    mEventCountTemplate, mSelectedEvents
+                                        .indexOf(mSelectedEventForAccessibility) + 1, numEvents
+                                )
+                            )
+                            b.append(" ")
+                        }
+                        appendEventAccessibilityString(b, mSelectedEventForAccessibility)
+                    }
+                }
+            }
+            if (dayChanged || hourChanged || speakEvents) {
+                val event: AccessibilityEvent = AccessibilityEvent
+                    .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED)
+                val msg: CharSequence = b.toString()
+                event.getText().add(msg)
+                event.setAddedCount(msg.length)
+                sendAccessibilityEventUnchecked(event)
+            }
+        }
+    }
+
+    /**
+     * @param b
+     * @param calEvent
+     */
+    private fun appendEventAccessibilityString(b: StringBuilder, calEvent: Event?) {
+        b.append(calEvent!!.titleAndLocation)
+        b.append(PERIOD_SPACE)
+        val `when`: String?
+        var flags: Int = DateUtils.FORMAT_SHOW_DATE
+        if (calEvent!!.allDay) {
+            flags = flags or (DateUtils.FORMAT_UTC or DateUtils.FORMAT_SHOW_WEEKDAY)
+        } else {
+            flags = flags or DateUtils.FORMAT_SHOW_TIME
+            if (DateFormat.is24HourFormat(mContext)) {
+                flags = flags or DateUtils.FORMAT_24HOUR
+            }
+        }
+        `when` = Utils.formatDateRange(mContext, calEvent!!.startMillis, calEvent!!.endMillis,
+            flags)
+        b.append(`when`)
+        b.append(PERIOD_SPACE)
+    }
+
+    private inner class GotoBroadcaster(start: Time, end: Time) : Animation.AnimationListener {
+        private val mCounter: Int
+        private val mStart: Time
+        private val mEnd: Time
+        @Override
+        override fun onAnimationEnd(animation: Animation?) {
+            var view = mViewSwitcher.getCurrentView() as DayView
+            view.mViewStartX = 0
+            view = mViewSwitcher.getNextView() as DayView
+            view.mViewStartX = 0
+            if (mCounter == sCounter) {
+                mController.sendEvent(
+                    this as Object?, EventType.GO_TO, mStart, mEnd, null, -1,
+                    ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null
+                )
+            }
+        }
+
+        @Override
+        override fun onAnimationRepeat(animation: Animation?) {
+        }
+
+        @Override
+        override fun onAnimationStart(animation: Animation?) {
+        }
+
+        init {
+            mCounter = ++sCounter
+            mStart = start
+            mEnd = end
+        }
+    }
+
+    private fun switchViews(forward: Boolean, xOffSet: Float, width: Float, velocity: Float): View {
+        mAnimationDistance = width - xOffSet
+        if (DEBUG) {
+            Log.d(TAG, "switchViews($forward) O:$xOffSet Dist:$mAnimationDistance")
+        }
+        var progress: Float = Math.abs(xOffSet) / width
+        if (progress > 1.0f) {
+            progress = 1.0f
+        }
+        val inFromXValue: Float
+        val inToXValue: Float
+        val outFromXValue: Float
+        val outToXValue: Float
+        if (forward) {
+            inFromXValue = 1.0f - progress
+            inToXValue = 0.0f
+            outFromXValue = -progress
+            outToXValue = -1.0f
+        } else {
+            inFromXValue = progress - 1.0f
+            inToXValue = 0.0f
+            outFromXValue = progress
+            outToXValue = 1.0f
+        }
+        val start = Time(mBaseDate!!.timezone)
+        start.set(mController.time as Long)
+        if (forward) {
+            start.monthDay += mNumDays
+        } else {
+            start.monthDay -= mNumDays
+        }
+        mController.time = start.normalize(true)
+        var newSelected: Time? = start
+        if (mNumDays == 7) {
+            newSelected = Time(start)
+            adjustToBeginningOfWeek(start)
+        }
+        val end = Time(start)
+        end.monthDay += mNumDays - 1
+
+        // We have to allocate these animation objects each time we switch views
+        // because that is the only way to set the animation parameters.
+        val inAnimation = TranslateAnimation(
+            Animation.RELATIVE_TO_SELF, inFromXValue,
+            Animation.RELATIVE_TO_SELF, inToXValue,
+            Animation.ABSOLUTE, 0.0f,
+            Animation.ABSOLUTE, 0.0f
+        )
+        val outAnimation = TranslateAnimation(
+            Animation.RELATIVE_TO_SELF, outFromXValue,
+            Animation.RELATIVE_TO_SELF, outToXValue,
+            Animation.ABSOLUTE, 0.0f,
+            Animation.ABSOLUTE, 0.0f
+        )
+        val duration = calculateDuration(width - Math.abs(xOffSet), width, velocity)
+        inAnimation.setDuration(duration)
+        inAnimation.setInterpolator(mHScrollInterpolator)
+        outAnimation.setInterpolator(mHScrollInterpolator)
+        outAnimation.setDuration(duration)
+        outAnimation.setAnimationListener(GotoBroadcaster(start, end))
+        mViewSwitcher.setInAnimation(inAnimation)
+        mViewSwitcher.setOutAnimation(outAnimation)
+        var view = mViewSwitcher.getCurrentView() as DayView
+        view.cleanup()
+        mViewSwitcher.showNext()
+        view = mViewSwitcher.getCurrentView() as DayView
+        view.setSelected(newSelected, true, false)
+        view.requestFocus()
+        view.reloadEvents()
+        view.updateTitle()
+        view.restartCurrentTimeUpdates()
+        return view
+    }
+
+    // This is called after scrolling stops to move the selected hour
+    // to the visible part of the screen.
+    private fun resetSelectedHour() {
+        if (mSelectionHour < mFirstHour + 1) {
+            setSelectedHour(mFirstHour + 1)
+            setSelectedEvent(null)
+            mSelectedEvents.clear()
+            mComputeSelectedEvents = true
+        } else if (mSelectionHour > mFirstHour + mNumHours - 3) {
+            setSelectedHour(mFirstHour + mNumHours - 3)
+            setSelectedEvent(null)
+            mSelectedEvents.clear()
+            mComputeSelectedEvents = true
+        }
+    }
+
+    private fun initFirstHour() {
+        mFirstHour = mSelectionHour - mNumHours / 5
+        if (mFirstHour < 0) {
+            mFirstHour = 0
+        } else if (mFirstHour + mNumHours > 24) {
+            mFirstHour = 24 - mNumHours
+        }
+    }
+
+    /**
+     * Recomputes the first full hour that is visible on screen after the
+     * screen is scrolled.
+     */
+    private fun computeFirstHour() {
+        // Compute the first full hour that is visible on screen
+        mFirstHour = (mViewStartY + mCellHeight + HOUR_GAP - 1) / (mCellHeight + HOUR_GAP)
+        mFirstHourOffset = mFirstHour * (mCellHeight + HOUR_GAP) - mViewStartY
+    }
+
+    private fun adjustHourSelection() {
+        if (mSelectionHour < 0) {
+            setSelectedHour(0)
+            if (mMaxAlldayEvents > 0) {
+                mPrevSelectedEvent = null
+                mSelectionAllday = true
+            }
+        }
+        if (mSelectionHour > 23) {
+            setSelectedHour(23)
+        }
+
+        // If the selected hour is at least 2 time slots from the top and
+        // bottom of the screen, then don't scroll the view.
+        if (mSelectionHour < mFirstHour + 1) {
+            // If there are all-days events for the selected day but there
+            // are no more normal events earlier in the day, then jump to
+            // the all-day event area.
+            // Exception 1: allow the user to scroll to 8am with the trackball
+            // before jumping to the all-day event area.
+            // Exception 2: if 12am is on screen, then allow the user to select
+            // 12am before going up to the all-day event area.
+            val daynum = mSelectionDay - mFirstJulianDay
+            if (daynum < mEarliestStartHour!!.size && daynum >= 0 && mMaxAlldayEvents > 0 &&
+                mEarliestStartHour!![daynum] > mSelectionHour &&
+                mFirstHour > 0 && mFirstHour < 8) {
+                mPrevSelectedEvent = null
+                mSelectionAllday = true
+                setSelectedHour(mFirstHour + 1)
+                return
+            }
+            if (mFirstHour > 0) {
+                mFirstHour -= 1
+                mViewStartY -= mCellHeight + HOUR_GAP
+                if (mViewStartY < 0) {
+                    mViewStartY = 0
+                }
+                return
+            }
+        }
+        if (mSelectionHour > mFirstHour + mNumHours - 3) {
+            if (mFirstHour < 24 - mNumHours) {
+                mFirstHour += 1
+                mViewStartY += mCellHeight + HOUR_GAP
+                if (mViewStartY > mMaxViewStartY) {
+                    mViewStartY = mMaxViewStartY
+                }
+                return
+            } else if (mFirstHour == 24 - mNumHours && mFirstHourOffset > 0) {
+                mViewStartY = mMaxViewStartY
+            }
+        }
+    }
+
+    fun clearCachedEvents() {
+        mLastReloadMillis = 0
+    }
+
+    private val mCancelCallback: Runnable = object : Runnable {
+        override fun run() {
+            clearCachedEvents()
+        }
+    }
+
+    /* package */
+    fun reloadEvents() {
+        // Protect against this being called before this view has been
+        // initialized.
+//        if (mContext == null) {
+//            return;
+//        }
+
+        // Make sure our time zones are up to date
+        mTZUpdater.run()
+        setSelectedEvent(null)
+        mPrevSelectedEvent = null
+        mSelectedEvents.clear()
+
+        // The start date is the beginning of the week at 12am
+        val weekStart = Time(Utils.getTimeZone(mContext, mTZUpdater))
+        weekStart.set(mBaseDate)
+        weekStart.hour = 0
+        weekStart.minute = 0
+        weekStart.second = 0
+        val millis: Long = weekStart.normalize(true /* ignore isDst */)
+
+        // Avoid reloading events unnecessarily.
+        if (millis == mLastReloadMillis) {
+            return
+        }
+        mLastReloadMillis = millis
+
+        // load events in the background
+        // mContext.startProgressSpinner();
+        val events: ArrayList<Event> = ArrayList<Event>()
+        mEventLoader.loadEventsInBackground(mNumDays, events as ArrayList<Event?>, mFirstJulianDay,
+            object : Runnable {
+            override fun run() {
+                val fadeinEvents = mFirstJulianDay != mLoadedFirstJulianDay
+                mEvents = events
+                mLoadedFirstJulianDay = mFirstJulianDay
+                if (mAllDayEvents == null) {
+                    mAllDayEvents = ArrayList<Event>()
+                } else {
+                    mAllDayEvents?.clear()
+                }
+
+                // Create a shorter array for all day events
+                for (e in events) {
+                    if (e.drawAsAllday()) {
+                        mAllDayEvents?.add(e)
+                    }
+                }
+
+                // New events, new layouts
+                if (mLayouts == null || mLayouts!!.size < events.size) {
+                    mLayouts = arrayOfNulls<StaticLayout>(events.size)
+                } else {
+                    Arrays.fill(mLayouts, null)
+                }
+                if (mAllDayLayouts == null || mAllDayLayouts!!.size < mAllDayEvents!!.size) {
+                    mAllDayLayouts = arrayOfNulls<StaticLayout>(events.size)
+                } else {
+                    Arrays.fill(mAllDayLayouts, null)
+                }
+                computeEventRelations()
+                mRemeasure = true
+                mComputeSelectedEvents = true
+                recalc()
+
+                // Start animation to cross fade the events
+                if (fadeinEvents) {
+                    if (mEventsCrossFadeAnimation == null) {
+                        mEventsCrossFadeAnimation =
+                            ObjectAnimator.ofInt(this@DayView, "EventsAlpha", 0, 255)
+                        mEventsCrossFadeAnimation?.setDuration(EVENTS_CROSS_FADE_DURATION.toLong())
+                    }
+                    mEventsCrossFadeAnimation?.start()
+                } else {
+                    invalidate()
+                }
+            }
+        }, mCancelCallback)
+    }
+
+    var eventsAlpha: Int
+        get() = mEventsAlpha
+        set(alpha) {
+            mEventsAlpha = alpha
+            invalidate()
+        }
+
+    fun stopEventsAnimation() {
+        if (mEventsCrossFadeAnimation != null) {
+            mEventsCrossFadeAnimation?.cancel()
+        }
+        mEventsAlpha = 255
+    }
+
+    private fun computeEventRelations() {
+        // Compute the layout relation between each event before measuring cell
+        // width, as the cell width should be adjusted along with the relation.
+        //
+        // Examples: A (1:00pm - 1:01pm), B (1:02pm - 2:00pm)
+        // We should mark them as "overwapped". Though they are not overwapped logically, but
+        // minimum cell height implicitly expands the cell height of A and it should look like
+        // (1:00pm - 1:15pm) after the cell height adjustment.
+
+        // Compute the space needed for the all-day events, if any.
+        // Make a pass over all the events, and keep track of the maximum
+        // number of all-day events in any one day.  Also, keep track of
+        // the earliest event in each day.
+        var maxAllDayEvents = 0
+        val events: ArrayList<Event> = mEvents
+        val len: Int = events.size
+        // Num of all-day-events on each day.
+        val eventsCount = IntArray(mLastJulianDay - mFirstJulianDay + 1)
+        Arrays.fill(eventsCount, 0)
+        for (ii in 0 until len) {
+            val event: Event = events.get(ii)
+            if (event.startDay > mLastJulianDay || event.endDay < mFirstJulianDay) {
+                continue
+            }
+            if (event.drawAsAllday()) {
+                // Count all the events being drawn as allDay events
+                val firstDay: Int = Math.max(event.startDay, mFirstJulianDay)
+                val lastDay: Int = Math.min(event.endDay, mLastJulianDay)
+                for (day in firstDay..lastDay) {
+                    val count = ++eventsCount[day - mFirstJulianDay]
+                    if (maxAllDayEvents < count) {
+                        maxAllDayEvents = count
+                    }
+                }
+                var daynum: Int = event.startDay - mFirstJulianDay
+                var durationDays: Int = event.endDay - event.startDay + 1
+                if (daynum < 0) {
+                    durationDays += daynum
+                    daynum = 0
+                }
+                if (daynum + durationDays > mNumDays) {
+                    durationDays = mNumDays - daynum
+                }
+                var day = daynum
+                while (durationDays > 0) {
+                    mHasAllDayEvent!![day] = true
+                    day++
+                    durationDays--
+                }
+            } else {
+                var daynum: Int = event.startDay - mFirstJulianDay
+                var hour: Int = event.startTime / 60
+                if (daynum >= 0 && hour < mEarliestStartHour!![daynum]) {
+                    mEarliestStartHour!![daynum] = hour
+                }
+
+                // Also check the end hour in case the event spans more than
+                // one day.
+                daynum = event.endDay - mFirstJulianDay
+                hour = event.endTime / 60
+                if (daynum < mNumDays && hour < mEarliestStartHour!![daynum]) {
+                    mEarliestStartHour!![daynum] = hour
+                }
+            }
+        }
+        mMaxAlldayEvents = maxAllDayEvents
+        initAllDayHeights()
+    }
+
+    @Override
+    protected override fun onDraw(canvas: Canvas) {
+        if (mRemeasure) {
+            remeasure(getWidth(), getHeight())
+            mRemeasure = false
+        }
+        canvas.save()
+        val yTranslate = (-mViewStartY + DAY_HEADER_HEIGHT + mAlldayHeight).toFloat()
+        // offset canvas by the current drag and header position
+        canvas.translate(-mViewStartX.toFloat(), yTranslate)
+        // clip to everything below the allDay area
+        val dest: Rect = mDestRect
+        dest.top = (mFirstCell - yTranslate).toInt()
+        dest.bottom = (mViewHeight - yTranslate).toInt()
+        dest.left = 0
+        dest.right = mViewWidth
+        canvas.save()
+        canvas.clipRect(dest)
+        // Draw the movable part of the view
+        doDraw(canvas)
+        // restore to having no clip
+        canvas.restore()
+        if (mTouchMode and TOUCH_MODE_HSCROLL != 0) {
+            val xTranslate: Float
+            xTranslate = if (mViewStartX > 0) {
+                mViewWidth.toFloat()
+            } else {
+                -mViewWidth.toFloat()
+            }
+            // Move the canvas around to prep it for the next view
+            // specifically, shift it by a screen and undo the
+            // yTranslation which will be redone in the nextView's onDraw().
+            canvas.translate(xTranslate, -yTranslate)
+            val nextView = mViewSwitcher.getNextView() as DayView
+
+            // Prevent infinite recursive calls to onDraw().
+            nextView.mTouchMode = TOUCH_MODE_INITIAL_STATE
+            nextView.onDraw(canvas)
+            // Move it back for this view
+            canvas.translate(-xTranslate, 0f)
+        } else {
+            // If we drew another view we already translated it back
+            // If we didn't draw another view we should be at the edge of the
+            // screen
+            canvas.translate(mViewStartX.toFloat(), -yTranslate)
+        }
+
+        // Draw the fixed areas (that don't scroll) directly to the canvas.
+        drawAfterScroll(canvas)
+        if (mComputeSelectedEvents && mUpdateToast) {
+            mUpdateToast = false
+        }
+        mComputeSelectedEvents = false
+
+        // Draw overscroll glow
+        if (!mEdgeEffectTop.isFinished()) {
+            if (DAY_HEADER_HEIGHT != 0) {
+                canvas.translate(0f, DAY_HEADER_HEIGHT.toFloat())
+            }
+            if (mEdgeEffectTop.draw(canvas)) {
+                invalidate()
+            }
+            if (DAY_HEADER_HEIGHT != 0) {
+                canvas.translate(0f, -DAY_HEADER_HEIGHT.toFloat())
+            }
+        }
+        if (!mEdgeEffectBottom.isFinished()) {
+            canvas.rotate(180f, mViewWidth.toFloat() / 2f, mViewHeight.toFloat() / 2f)
+            if (mEdgeEffectBottom.draw(canvas)) {
+                invalidate()
+            }
+        }
+        canvas.restore()
+    }
+
+    private fun drawAfterScroll(canvas: Canvas) {
+        val p: Paint = mPaint
+        val r: Rect = mRect
+        drawAllDayHighlights(r, canvas, p)
+        if (mMaxAlldayEvents != 0) {
+            drawAllDayEvents(mFirstJulianDay, mNumDays, canvas, p)
+            drawUpperLeftCorner(r, canvas, p)
+        }
+        drawScrollLine(r, canvas, p)
+        drawDayHeaderLoop(r, canvas, p)
+
+        // Draw the AM and PM indicators if we're in 12 hour mode
+        if (!mIs24HourFormat) {
+            drawAmPm(canvas, p)
+        }
+    }
+
+    // This isn't really the upper-left corner. It's the square area just
+    // below the upper-left corner, above the hours and to the left of the
+    // all-day area.
+    private fun drawUpperLeftCorner(r: Rect, canvas: Canvas, p: Paint) {
+        setupHourTextPaint(p)
+        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
+            // Draw the allDay expand/collapse icon
+            if (mUseExpandIcon) {
+                mExpandAlldayDrawable.setBounds(mExpandAllDayRect)
+                mExpandAlldayDrawable.draw(canvas)
+            } else {
+                mCollapseAlldayDrawable.setBounds(mExpandAllDayRect)
+                mCollapseAlldayDrawable.draw(canvas)
+            }
+        }
+    }
+
+    private fun drawScrollLine(r: Rect, canvas: Canvas, p: Paint) {
+        val right = computeDayLeftPosition(mNumDays)
+        val y = mFirstCell - 1
+        p.setAntiAlias(false)
+        p.setStyle(Style.FILL)
+        p.setColor(mCalendarGridLineInnerHorizontalColor)
+        p.setStrokeWidth(GRID_LINE_INNER_WIDTH)
+        canvas.drawLine(GRID_LINE_LEFT_MARGIN, y.toFloat(), right.toFloat(), y.toFloat(), p)
+        p.setAntiAlias(true)
+    }
+
+    // Computes the x position for the left side of the given day (base 0)
+    private fun computeDayLeftPosition(day: Int): Int {
+        val effectiveWidth = mViewWidth - mHoursWidth
+        return day * effectiveWidth / mNumDays + mHoursWidth
+    }
+
+    private fun drawAllDayHighlights(r: Rect, canvas: Canvas, p: Paint) {
+        if (mFutureBgColor != 0) {
+            // First, color the labels area light gray
+            r.top = 0
+            r.bottom = DAY_HEADER_HEIGHT
+            r.left = 0
+            r.right = mViewWidth
+            p.setColor(mBgColor)
+            p.setStyle(Style.FILL)
+            canvas.drawRect(r, p)
+            // and the area that says All day
+            r.top = DAY_HEADER_HEIGHT
+            r.bottom = mFirstCell - 1
+            r.left = 0
+            r.right = mHoursWidth
+            canvas.drawRect(r, p)
+            var startIndex = -1
+            val todayIndex = mTodayJulianDay - mFirstJulianDay
+            if (todayIndex < 0) {
+                // Future
+                startIndex = 0
+            } else if (todayIndex >= 1 && todayIndex + 1 < mNumDays) {
+                // Multiday - tomorrow is visible.
+                startIndex = todayIndex + 1
+            }
+            if (startIndex >= 0) {
+                // Draw the future highlight
+                r.top = 0
+                r.bottom = mFirstCell - 1
+                r.left = computeDayLeftPosition(startIndex) + 1
+                r.right = computeDayLeftPosition(mNumDays)
+                p.setColor(mFutureBgColor)
+                p.setStyle(Style.FILL)
+                canvas.drawRect(r, p)
+            }
+        }
+    }
+
+    private fun drawDayHeaderLoop(r: Rect, canvas: Canvas, p: Paint) {
+        // Draw the horizontal day background banner
+        // p.setColor(mCalendarDateBannerBackground);
+        // r.top = 0;
+        // r.bottom = DAY_HEADER_HEIGHT;
+        // r.left = 0;
+        // r.right = mHoursWidth + mNumDays * (mCellWidth + DAY_GAP);
+        // canvas.drawRect(r, p);
+        //
+        // Fill the extra space on the right side with the default background
+        // r.left = r.right;
+        // r.right = mViewWidth;
+        // p.setColor(mCalendarGridAreaBackground);
+        // canvas.drawRect(r, p);
+        if (mNumDays == 1 && ONE_DAY_HEADER_HEIGHT == 0) {
+            return
+        }
+        p.setTypeface(mBold)
+        p.setTextAlign(Paint.Align.RIGHT)
+        var cell = mFirstJulianDay
+        val dayNames: Array<String?>?
+        dayNames = if (mDateStrWidth < mCellWidth) {
+            mDayStrs
+        } else {
+            mDayStrs2Letter
+        }
+        p.setAntiAlias(true)
+        var day = 0
+        while (day < mNumDays) {
+            var dayOfWeek = day + mFirstVisibleDayOfWeek
+            if (dayOfWeek >= 14) {
+                dayOfWeek -= 14
+            }
+            var color = mCalendarDateBannerTextColor
+            if (mNumDays == 1) {
+                if (dayOfWeek == Time.SATURDAY) {
+                    color = mWeek_saturdayColor
+                } else if (dayOfWeek == Time.SUNDAY) {
+                    color = mWeek_sundayColor
+                }
+            } else {
+                val column = day % 7
+                if (Utils.isSaturday(column, mFirstDayOfWeek)) {
+                    color = mWeek_saturdayColor
+                } else if (Utils.isSunday(column, mFirstDayOfWeek)) {
+                    color = mWeek_sundayColor
+                }
+            }
+            p.setColor(color)
+            drawDayHeader(dayNames!![dayOfWeek], day, cell, canvas, p)
+            day++
+            cell++
+        }
+        p.setTypeface(null)
+    }
+
+    private fun drawAmPm(canvas: Canvas, p: Paint) {
+        p.setColor(mCalendarAmPmLabel)
+        p.setTextSize(AMPM_TEXT_SIZE)
+        p.setTypeface(mBold)
+        p.setAntiAlias(true)
+        p.setTextAlign(Paint.Align.RIGHT)
+        var text = mAmString
+        if (mFirstHour >= 12) {
+            text = mPmString
+        }
+        var y = mFirstCell + mFirstHourOffset + 2 * mHoursTextHeight + HOUR_GAP
+        canvas.drawText(text as String, HOURS_LEFT_MARGIN.toFloat(), y.toFloat(), p)
+        if (mFirstHour < 12 && mFirstHour + mNumHours > 12) {
+            // Also draw the "PM"
+            text = mPmString
+            y =
+                mFirstCell + mFirstHourOffset + (12 - mFirstHour) * (mCellHeight + HOUR_GAP) +
+                    2 * mHoursTextHeight + HOUR_GAP
+            canvas.drawText(text as String, HOURS_LEFT_MARGIN.toFloat(), y.toFloat(), p)
+        }
+    }
+
+    private fun drawCurrentTimeLine(
+        r: Rect,
+        day: Int,
+        top: Int,
+        canvas: Canvas,
+        p: Paint
+    ) {
+        r.left = computeDayLeftPosition(day) - CURRENT_TIME_LINE_SIDE_BUFFER + 1
+        r.right = computeDayLeftPosition(day + 1) + CURRENT_TIME_LINE_SIDE_BUFFER + 1
+        r.top = top - CURRENT_TIME_LINE_TOP_OFFSET
+        r.bottom = r.top + mCurrentTimeLine.getIntrinsicHeight()
+        mCurrentTimeLine.setBounds(r)
+        mCurrentTimeLine.draw(canvas)
+        if (mAnimateToday) {
+            mCurrentTimeAnimateLine.setBounds(r)
+            mCurrentTimeAnimateLine.setAlpha(mAnimateTodayAlpha)
+            mCurrentTimeAnimateLine.draw(canvas)
+        }
+    }
+
+    private fun doDraw(canvas: Canvas) {
+        val p: Paint = mPaint
+        val r: Rect = mRect
+        if (mFutureBgColor != 0) {
+            drawBgColors(r, canvas, p)
+        }
+        drawGridBackground(r, canvas, p)
+        drawHours(r, canvas, p)
+
+        // Draw each day
+        var cell = mFirstJulianDay
+        p.setAntiAlias(false)
+        val alpha: Int = p.getAlpha()
+        p.setAlpha(mEventsAlpha)
+        var day = 0
+        while (day < mNumDays) {
+
+            // TODO Wow, this needs cleanup. drawEvents loop through all the
+            // events on every call.
+            drawEvents(cell, day, HOUR_GAP, canvas, p)
+            // If this is today
+            if (cell == mTodayJulianDay) {
+                val lineY: Int =
+                    mCurrentTime!!.hour * (mCellHeight + HOUR_GAP) + mCurrentTime!!.minute *
+                        mCellHeight / 60 + 1
+
+                // And the current time shows up somewhere on the screen
+                if (lineY >= mViewStartY && lineY < mViewStartY + mViewHeight - 2) {
+                    drawCurrentTimeLine(r, day, lineY, canvas, p)
+                }
+            }
+            day++
+            cell++
+        }
+        p.setAntiAlias(true)
+        p.setAlpha(alpha)
+    }
+
+    private fun drawHours(r: Rect, canvas: Canvas, p: Paint) {
+        setupHourTextPaint(p)
+        var y = HOUR_GAP + mHoursTextHeight + HOURS_TOP_MARGIN
+        for (i in 0..23) {
+            val time = mHourStrs!![i]
+            canvas.drawText(time, HOURS_LEFT_MARGIN.toFloat(), y.toFloat(), p)
+            y += mCellHeight + HOUR_GAP
+        }
+    }
+
+    private fun setupHourTextPaint(p: Paint) {
+        p.setColor(mCalendarHourLabelColor)
+        p.setTextSize(HOURS_TEXT_SIZE)
+        p.setTypeface(Typeface.DEFAULT)
+        p.setTextAlign(Paint.Align.RIGHT)
+        p.setAntiAlias(true)
+    }
+
+    private fun drawDayHeader(dayStr: String?, day: Int, cell: Int, canvas: Canvas, p: Paint) {
+        var dateNum = mFirstVisibleDate + day
+        var x: Int
+        if (dateNum > mMonthLength) {
+            dateNum -= mMonthLength
+        }
+        p.setAntiAlias(true)
+        val todayIndex = mTodayJulianDay - mFirstJulianDay
+        // Draw day of the month
+        val dateNumStr: String = dateNum.toString()
+        if (mNumDays > 1) {
+            val y = (DAY_HEADER_HEIGHT - DAY_HEADER_BOTTOM_MARGIN).toFloat()
+
+            // Draw day of the month
+            x = computeDayLeftPosition(day + 1) - DAY_HEADER_RIGHT_MARGIN
+            p.setTextAlign(Align.RIGHT)
+            p.setTextSize(DATE_HEADER_FONT_SIZE)
+            p.setTypeface(if (todayIndex == day) mBold else Typeface.DEFAULT)
+            canvas.drawText(dateNumStr as String, x.toFloat(), y, p)
+
+            // Draw day of the week
+            x -= (p.measureText(" $dateNumStr")).toInt()
+            p.setTextSize(DAY_HEADER_FONT_SIZE)
+            p.setTypeface(Typeface.DEFAULT)
+            canvas.drawText(dayStr as String, x.toFloat(), y, p)
+        } else {
+            val y = (ONE_DAY_HEADER_HEIGHT - DAY_HEADER_ONE_DAY_BOTTOM_MARGIN).toFloat()
+            p.setTextAlign(Align.LEFT)
+
+            // Draw day of the week
+            x = computeDayLeftPosition(day) + DAY_HEADER_ONE_DAY_LEFT_MARGIN
+            p.setTextSize(DAY_HEADER_FONT_SIZE)
+            p.setTypeface(Typeface.DEFAULT)
+            canvas.drawText(dayStr as String, x.toFloat(), y, p)
+
+            // Draw day of the month
+            x += (p.measureText(dayStr) + DAY_HEADER_ONE_DAY_RIGHT_MARGIN).toInt()
+            p.setTextSize(DATE_HEADER_FONT_SIZE)
+            p.setTypeface(if (todayIndex == day) mBold else Typeface.DEFAULT)
+            canvas.drawText(dateNumStr, x.toFloat(), y, p)
+        }
+    }
+
+    private fun drawGridBackground(r: Rect, canvas: Canvas, p: Paint) {
+        val savedStyle: Style = p.getStyle()
+        val stopX = computeDayLeftPosition(mNumDays).toFloat()
+        var y = 0f
+        val deltaY = (mCellHeight + HOUR_GAP).toFloat()
+        var linesIndex = 0
+        val startY = 0f
+        val stopY = (HOUR_GAP + 24 * (mCellHeight + HOUR_GAP)).toFloat()
+        var x = mHoursWidth.toFloat()
+
+        // Draw the inner horizontal grid lines
+        p.setColor(mCalendarGridLineInnerHorizontalColor)
+        p.setStrokeWidth(GRID_LINE_INNER_WIDTH)
+        p.setAntiAlias(false)
+        y = 0f
+        linesIndex = 0
+        for (hour in 0..24) {
+            mLines[linesIndex++] = GRID_LINE_LEFT_MARGIN
+            mLines[linesIndex++] = y
+            mLines[linesIndex++] = stopX
+            mLines[linesIndex++] = y
+            y += deltaY
+        }
+        if (mCalendarGridLineInnerVerticalColor != mCalendarGridLineInnerHorizontalColor) {
+            canvas.drawLines(mLines, 0, linesIndex, p)
+            linesIndex = 0
+            p.setColor(mCalendarGridLineInnerVerticalColor)
+        }
+
+        // Draw the inner vertical grid lines
+        for (day in 0..mNumDays) {
+            x = computeDayLeftPosition(day).toFloat()
+            mLines[linesIndex++] = x
+            mLines[linesIndex++] = startY
+            mLines[linesIndex++] = x
+            mLines[linesIndex++] = stopY
+        }
+        canvas.drawLines(mLines, 0, linesIndex, p)
+
+        // Restore the saved style.
+        p.setStyle(savedStyle)
+        p.setAntiAlias(true)
+    }
+
+    /**
+     * @param r
+     * @param canvas
+     * @param p
+     */
+    private fun drawBgColors(r: Rect, canvas: Canvas, p: Paint) {
+        val todayIndex = mTodayJulianDay - mFirstJulianDay
+        // Draw the hours background color
+        r.top = mDestRect.top
+        r.bottom = mDestRect.bottom
+        r.left = 0
+        r.right = mHoursWidth
+        p.setColor(mBgColor)
+        p.setStyle(Style.FILL)
+        p.setAntiAlias(false)
+        canvas.drawRect(r, p)
+
+        // Draw background for grid area
+        if (mNumDays == 1 && todayIndex == 0) {
+            // Draw a white background for the time later than current time
+            var lineY: Int =
+                mCurrentTime!!.hour * (mCellHeight + HOUR_GAP) + mCurrentTime!!.minute *
+                    mCellHeight / 60 + 1
+            if (lineY < mViewStartY + mViewHeight) {
+                lineY = Math.max(lineY, mViewStartY)
+                r.left = mHoursWidth
+                r.right = mViewWidth
+                r.top = lineY
+                r.bottom = mViewStartY + mViewHeight
+                p.setColor(mFutureBgColor)
+                canvas.drawRect(r, p)
+            }
+        } else if (todayIndex >= 0 && todayIndex < mNumDays) {
+            // Draw today with a white background for the time later than current time
+            var lineY: Int =
+                mCurrentTime!!.hour * (mCellHeight + HOUR_GAP) + mCurrentTime!!.minute *
+                    mCellHeight / 60 + 1
+            if (lineY < mViewStartY + mViewHeight) {
+                lineY = Math.max(lineY, mViewStartY)
+                r.left = computeDayLeftPosition(todayIndex) + 1
+                r.right = computeDayLeftPosition(todayIndex + 1)
+                r.top = lineY
+                r.bottom = mViewStartY + mViewHeight
+                p.setColor(mFutureBgColor)
+                canvas.drawRect(r, p)
+            }
+
+            // Paint Tomorrow and later days with future color
+            if (todayIndex + 1 < mNumDays) {
+                r.left = computeDayLeftPosition(todayIndex + 1) + 1
+                r.right = computeDayLeftPosition(mNumDays)
+                r.top = mDestRect.top
+                r.bottom = mDestRect.bottom
+                p.setColor(mFutureBgColor)
+                canvas.drawRect(r, p)
+            }
+        } else if (todayIndex < 0) {
+            // Future
+            r.left = computeDayLeftPosition(0) + 1
+            r.right = computeDayLeftPosition(mNumDays)
+            r.top = mDestRect.top
+            r.bottom = mDestRect.bottom
+            p.setColor(mFutureBgColor)
+            canvas.drawRect(r, p)
+        }
+        p.setAntiAlias(true)
+    }
+
+    private fun computeMaxStringWidth(currentMax: Int, strings: Array<String?>, p: Paint): Int {
+        var maxWidthF = 0.0f
+        val len = strings.size
+        for (i in 0 until len) {
+            val width: Float = p.measureText(strings[i])
+            maxWidthF = Math.max(width, maxWidthF)
+        }
+        var maxWidth = (maxWidthF + 0.5).toInt()
+        if (maxWidth < currentMax) {
+            maxWidth = currentMax
+        }
+        return maxWidth
+    }
+
+    private fun saveSelectionPosition(left: Float, top: Float, right: Float, bottom: Float) {
+        mPrevBox.left = left.toInt()
+        mPrevBox.right = right.toInt()
+        mPrevBox.top = top.toInt()
+        mPrevBox.bottom = bottom.toInt()
+    }
+
+    private fun setupTextRect(r: Rect) {
+        if (r.bottom <= r.top || r.right <= r.left) {
+            r.bottom = r.top
+            r.right = r.left
+            return
+        }
+        if (r.bottom - r.top > EVENT_TEXT_TOP_MARGIN + EVENT_TEXT_BOTTOM_MARGIN) {
+            r.top += EVENT_TEXT_TOP_MARGIN
+            r.bottom -= EVENT_TEXT_BOTTOM_MARGIN
+        }
+        if (r.right - r.left > EVENT_TEXT_LEFT_MARGIN + EVENT_TEXT_RIGHT_MARGIN) {
+            r.left += EVENT_TEXT_LEFT_MARGIN
+            r.right -= EVENT_TEXT_RIGHT_MARGIN
+        }
+    }
+
+    private fun setupAllDayTextRect(r: Rect) {
+        if (r.bottom <= r.top || r.right <= r.left) {
+            r.bottom = r.top
+            r.right = r.left
+            return
+        }
+        if (r.bottom - r.top > EVENT_ALL_DAY_TEXT_TOP_MARGIN + EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN) {
+            r.top += EVENT_ALL_DAY_TEXT_TOP_MARGIN
+            r.bottom -= EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN
+        }
+        if (r.right - r.left > EVENT_ALL_DAY_TEXT_LEFT_MARGIN + EVENT_ALL_DAY_TEXT_RIGHT_MARGIN) {
+            r.left += EVENT_ALL_DAY_TEXT_LEFT_MARGIN
+            r.right -= EVENT_ALL_DAY_TEXT_RIGHT_MARGIN
+        }
+    }
+
+    /**
+     * Return the layout for a numbered event. Create it if not already existing
+     */
+    private fun getEventLayout(
+        layouts: Array<StaticLayout?>?,
+        i: Int,
+        event: Event,
+        paint: Paint,
+        r: Rect
+    ): StaticLayout? {
+        if (i < 0 || i >= layouts!!.size) {
+            return null
+        }
+        var layout: StaticLayout? = layouts!![i]
+        // Check if we have already initialized the StaticLayout and that
+        // the width hasn't changed (due to vertical resizing which causes
+        // re-layout of events at min height)
+        if (layout == null || r.width() !== layout.getWidth()) {
+            val bob = SpannableStringBuilder()
+            if (event.title != null) {
+                // MAX - 1 since we add a space
+                bob.append(drawTextSanitizer(event.title.toString(),
+                    MAX_EVENT_TEXT_LEN - 1))
+                bob.setSpan(StyleSpan(android.graphics.Typeface.BOLD), 0, bob.length, 0)
+                bob.append(' ')
+            }
+            if (event.location != null) {
+                bob.append(
+                    drawTextSanitizer(
+                        event.location.toString(),
+                        MAX_EVENT_TEXT_LEN - bob.length
+                    )
+                )
+            }
+            when (event.selfAttendeeStatus) {
+                Attendees.ATTENDEE_STATUS_INVITED -> paint.setColor(event.color)
+                Attendees.ATTENDEE_STATUS_DECLINED -> {
+                    paint.setColor(mEventTextColor)
+                    paint.setAlpha(Utils.DECLINED_EVENT_TEXT_ALPHA)
+                }
+                Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED,
+                    Attendees.ATTENDEE_STATUS_TENTATIVE -> paint.setColor(
+                    mEventTextColor
+                )
+                else -> paint.setColor(mEventTextColor)
+            }
+
+            // Leave a one pixel boundary on the left and right of the rectangle for the event
+            layout = StaticLayout(
+                bob, 0, bob.length, TextPaint(paint), r.width(),
+                Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true, null, r.width()
+            )
+            layouts[i] = layout
+        }
+        layout.getPaint().setAlpha(mEventsAlpha)
+        return layout
+    }
+
+    private fun drawAllDayEvents(firstDay: Int, numDays: Int, canvas: Canvas, p: Paint) {
+        p.setTextSize(NORMAL_FONT_SIZE)
+        p.setTextAlign(Paint.Align.LEFT)
+        val eventTextPaint: Paint = mEventTextPaint
+        val startY = DAY_HEADER_HEIGHT.toFloat()
+        val stopY = startY + mAlldayHeight + ALLDAY_TOP_MARGIN
+        var x = 0f
+        var linesIndex = 0
+
+        // Draw the inner vertical grid lines
+        p.setColor(mCalendarGridLineInnerVerticalColor)
+        x = mHoursWidth.toFloat()
+        p.setStrokeWidth(GRID_LINE_INNER_WIDTH)
+        // Line bounding the top of the all day area
+        mLines!![linesIndex++] = GRID_LINE_LEFT_MARGIN
+        mLines!![linesIndex++] = startY
+        mLines!![linesIndex++] = computeDayLeftPosition(mNumDays).toFloat()
+        mLines!![linesIndex++] = startY
+        for (day in 0..mNumDays) {
+            x = computeDayLeftPosition(day).toFloat()
+            mLines!![linesIndex++] = x
+            mLines!![linesIndex++] = startY
+            mLines!![linesIndex++] = x
+            mLines!![linesIndex++] = stopY
+        }
+        p.setAntiAlias(false)
+        canvas.drawLines(mLines, 0, linesIndex, p)
+        p.setStyle(Style.FILL)
+        val y = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN
+        val lastDay = firstDay + numDays - 1
+        val events: ArrayList<Event>? = mAllDayEvents
+        val numEvents: Int = events!!.size
+        // Whether or not we should draw the more events text
+        var hasMoreEvents = false
+        // size of the allDay area
+        val drawHeight = mAlldayHeight.toFloat()
+        // max number of events being drawn in one day of the allday area
+        var numRectangles = mMaxAlldayEvents.toFloat()
+        // Where to cut off drawn allday events
+        var allDayEventClip = DAY_HEADER_HEIGHT + mAlldayHeight + ALLDAY_TOP_MARGIN
+        // The number of events that weren't drawn in each day
+        mSkippedAlldayEvents = IntArray(numDays)
+        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount &&
+            !mShowAllAllDayEvents && mAnimateDayHeight == 0) {
+            // We draw one fewer event than will fit so that more events text
+            // can be drawn
+            numRectangles = (mMaxUnexpandedAlldayEventCount - 1).toFloat()
+            // We also clip the events above the more events text
+            allDayEventClip -= MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt()
+            hasMoreEvents = true
+        } else if (mAnimateDayHeight != 0) {
+            // clip at the end of the animating space
+            allDayEventClip = DAY_HEADER_HEIGHT + mAnimateDayHeight + ALLDAY_TOP_MARGIN
+        }
+        var alpha: Int = eventTextPaint.getAlpha()
+        eventTextPaint.setAlpha(mEventsAlpha)
+        for (i in 0 until numEvents) {
+            val event: Event = events!!.get(i)
+            var startDay: Int = event.startDay
+            var endDay: Int = event.endDay
+            if (startDay > lastDay || endDay < firstDay) {
+                continue
+            }
+            if (startDay < firstDay) {
+                startDay = firstDay
+            }
+            if (endDay > lastDay) {
+                endDay = lastDay
+            }
+            val startIndex = startDay - firstDay
+            val endIndex = endDay - firstDay
+            var height =
+                if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount)
+                    mAnimateDayEventHeight.toFloat() else drawHeight / numRectangles
+
+            // Prevent a single event from getting too big
+            if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
+                height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT.toFloat()
+            }
+
+            // Leave a one-pixel space between the vertical day lines and the
+            // event rectangle.
+            event.left = computeDayLeftPosition(startIndex).toFloat()
+            event.right = computeDayLeftPosition(endIndex + 1).toFloat() - DAY_GAP
+            event.top = y + height * event.getColumn()
+            event.bottom = event.top + height - ALL_DAY_EVENT_RECT_BOTTOM_MARGIN
+            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
+                // check if we should skip this event. We skip if it starts
+                // after the clip bound or ends after the skip bound and we're
+                // not animating.
+                if (event.top >= allDayEventClip) {
+                    incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex)
+                    continue
+                } else if (event.bottom > allDayEventClip) {
+                    if (hasMoreEvents) {
+                        incrementSkipCount(mSkippedAlldayEvents, startIndex, endIndex)
+                        continue
+                    }
+                    event.bottom = allDayEventClip.toFloat()
+                }
+            }
+            val r: Rect = drawEventRect(
+                event, canvas, p, eventTextPaint, event.top.toInt(),
+                event.bottom.toInt()
+            )
+            setupAllDayTextRect(r)
+            val layout: StaticLayout? = getEventLayout(mAllDayLayouts, i, event, eventTextPaint, r)
+            drawEventText(layout, r, canvas, r.top, r.bottom, true)
+
+            // Check if this all-day event intersects the selected day
+            if (mSelectionAllday && mComputeSelectedEvents) {
+                if (startDay <= mSelectionDay && endDay >= mSelectionDay) {
+                    mSelectedEvents.add(event)
+                }
+            }
+        }
+        eventTextPaint.setAlpha(alpha)
+        if (mMoreAlldayEventsTextAlpha != 0 && mSkippedAlldayEvents != null) {
+            // If the more allday text should be visible, draw it.
+            alpha = p.getAlpha()
+            p.setAlpha(mEventsAlpha)
+            p.setColor(mMoreAlldayEventsTextAlpha shl 24 and mMoreEventsTextColor)
+            for (i in mSkippedAlldayEvents!!.indices) {
+                if (mSkippedAlldayEvents!![i] > 0) {
+                    drawMoreAlldayEvents(canvas, mSkippedAlldayEvents!![i], i, p)
+                }
+            }
+            p.setAlpha(alpha)
+        }
+        if (mSelectionAllday) {
+            // Compute the neighbors for the list of all-day events that
+            // intersect the selected day.
+            computeAllDayNeighbors()
+
+            // Set the selection position to zero so that when we move down
+            // to the normal event area, we will highlight the topmost event.
+            saveSelectionPosition(0f, 0f, 0f, 0f)
+        }
+    }
+
+    // Helper method for counting the number of allday events skipped on each day
+    private fun incrementSkipCount(counts: IntArray?, startIndex: Int, endIndex: Int) {
+        if (counts == null || startIndex < 0 || endIndex > counts.size) {
+            return
+        }
+        for (i in startIndex..endIndex) {
+            counts[i]++
+        }
+    }
+
+    // Draws the "box +n" text for hidden allday events
+    protected fun drawMoreAlldayEvents(canvas: Canvas, remainingEvents: Int, day: Int, p: Paint) {
+        var x = computeDayLeftPosition(day) + EVENT_ALL_DAY_TEXT_LEFT_MARGIN
+        var y = (mAlldayHeight - .5f * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - (.5f *
+            EVENT_SQUARE_WIDTH) + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN).toInt()
+        val r: Rect = mRect
+        r.top = y
+        r.left = x
+        r.bottom = y + EVENT_SQUARE_WIDTH
+        r.right = x + EVENT_SQUARE_WIDTH
+        p.setColor(mMoreEventsTextColor)
+        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH.toFloat())
+        p.setStyle(Style.STROKE)
+        p.setAntiAlias(false)
+        canvas.drawRect(r, p)
+        p.setAntiAlias(true)
+        p.setStyle(Style.FILL)
+        p.setTextSize(EVENT_TEXT_FONT_SIZE)
+        val text: String =
+            mResources.getQuantityString(R.plurals.month_more_events, remainingEvents)
+        y += EVENT_SQUARE_WIDTH
+        x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING
+        canvas.drawText(String.format(text, remainingEvents), x.toFloat(), y.toFloat(), p)
+    }
+
+    private fun computeAllDayNeighbors() {
+        val len: Int = mSelectedEvents.size
+        if (len == 0 || mSelectedEvent != null) {
+            return
+        }
+
+        // First, clear all the links
+        for (ii in 0 until len) {
+            val ev: Event = mSelectedEvents.get(ii)
+            ev.nextUp = null
+            ev.nextDown = null
+            ev.nextLeft = null
+            ev.nextRight = null
+        }
+
+        // For each event in the selected event list "mSelectedEvents", find
+        // its neighbors in the up and down directions. This could be done
+        // more efficiently by sorting on the Event.getColumn() field, but
+        // the list is expected to be very small.
+
+        // Find the event in the same row as the previously selected all-day
+        // event, if any.
+        var startPosition = -1
+        if (mPrevSelectedEvent != null && mPrevSelectedEvent!!.drawAsAllday()) {
+            startPosition = mPrevSelectedEvent?.getColumn() as Int
+        }
+        var maxPosition = -1
+        var startEvent: Event? = null
+        var maxPositionEvent: Event? = null
+        for (ii in 0 until len) {
+            val ev: Event = mSelectedEvents.get(ii)
+            val position: Int = ev.getColumn()
+            if (position == startPosition) {
+                startEvent = ev
+            } else if (position > maxPosition) {
+                maxPositionEvent = ev
+                maxPosition = position
+            }
+            for (jj in 0 until len) {
+                if (jj == ii) {
+                    continue
+                }
+                val neighbor: Event = mSelectedEvents.get(jj)
+                val neighborPosition: Int = neighbor.getColumn()
+                if (neighborPosition == position - 1) {
+                    ev.nextUp = neighbor
+                } else if (neighborPosition == position + 1) {
+                    ev.nextDown = neighbor
+                }
+            }
+        }
+        if (startEvent != null) {
+            setSelectedEvent(startEvent)
+        } else {
+            setSelectedEvent(maxPositionEvent)
+        }
+    }
+
+    private fun drawEvents(date: Int, dayIndex: Int, top: Int, canvas: Canvas, p: Paint) {
+        val eventTextPaint: Paint = mEventTextPaint
+        val left = computeDayLeftPosition(dayIndex) + 1
+        val cellWidth = computeDayLeftPosition(dayIndex + 1) - left + 1
+        val cellHeight = mCellHeight
+
+        // Use the selected hour as the selection region
+        val selectionArea: Rect = mSelectionRect
+        selectionArea.top = top + mSelectionHour * (cellHeight + HOUR_GAP)
+        selectionArea.bottom = selectionArea.top + cellHeight
+        selectionArea.left = left
+        selectionArea.right = selectionArea.left + cellWidth
+        val events: ArrayList<Event> = mEvents
+        val numEvents: Int = events.size
+        val geometry: EventGeometry = mEventGeometry
+        val viewEndY = mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight
+        val alpha: Int = eventTextPaint.getAlpha()
+        eventTextPaint.setAlpha(mEventsAlpha)
+        for (i in 0 until numEvents) {
+            val event: Event = events.get(i)
+            if (!geometry.computeEventRect(date, left, top, cellWidth, event)) {
+                continue
+            }
+
+            // Don't draw it if it is not visible
+            if (event.bottom < mViewStartY || event.top > viewEndY) {
+                continue
+            }
+            if (date == mSelectionDay && !mSelectionAllday && mComputeSelectedEvents &&
+                geometry.eventIntersectsSelection(event, selectionArea)
+            ) {
+                mSelectedEvents.add(event)
+            }
+            val r: Rect = drawEventRect(event, canvas, p, eventTextPaint, mViewStartY, viewEndY)
+            setupTextRect(r)
+
+            // Don't draw text if it is not visible
+            if (r.top > viewEndY || r.bottom < mViewStartY) {
+                continue
+            }
+            val layout: StaticLayout? = getEventLayout(mLayouts, i, event, eventTextPaint, r)
+            // TODO: not sure why we are 4 pixels off
+            drawEventText(
+                layout,
+                r,
+                canvas,
+                mViewStartY + 4,
+                mViewStartY + mViewHeight - DAY_HEADER_HEIGHT - mAlldayHeight,
+                false
+            )
+        }
+        eventTextPaint.setAlpha(alpha)
+    }
+
+    private fun drawEventRect(
+        event: Event,
+        canvas: Canvas,
+        p: Paint,
+        eventTextPaint: Paint,
+        visibleTop: Int,
+        visibleBot: Int
+    ): Rect {
+        // Draw the Event Rect
+        val r: Rect = mRect
+        r.top = Math.max(event.top.toInt() + EVENT_RECT_TOP_MARGIN, visibleTop)
+        r.bottom = Math.min(event.bottom.toInt() - EVENT_RECT_BOTTOM_MARGIN, visibleBot)
+        r.left = event.left.toInt() + EVENT_RECT_LEFT_MARGIN
+        r.right = event.right.toInt()
+        var color: Int = event.color
+        when (event.selfAttendeeStatus) {
+            Attendees.ATTENDEE_STATUS_INVITED -> if (event !== mClickedEvent) {
+                p.setStyle(Style.STROKE)
+            }
+            Attendees.ATTENDEE_STATUS_DECLINED -> {
+                if (event !== mClickedEvent) {
+                    color = Utils.getDeclinedColorFromColor(color)
+                }
+                p.setStyle(Style.FILL_AND_STROKE)
+            }
+            Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED,
+                Attendees.ATTENDEE_STATUS_TENTATIVE -> p.setStyle(
+                Style.FILL_AND_STROKE
+            )
+            else -> p.setStyle(Style.FILL_AND_STROKE)
+        }
+        p.setAntiAlias(false)
+        val floorHalfStroke = Math.floor(EVENT_RECT_STROKE_WIDTH.toDouble() / 2.0).toInt()
+        val ceilHalfStroke = Math.ceil(EVENT_RECT_STROKE_WIDTH.toDouble() / 2.0).toInt()
+        r.top = Math.max(event.top.toInt() + EVENT_RECT_TOP_MARGIN + floorHalfStroke, visibleTop)
+        r.bottom = Math.min(
+            event.bottom.toInt() - EVENT_RECT_BOTTOM_MARGIN - ceilHalfStroke,
+            visibleBot
+        )
+        r.left += floorHalfStroke
+        r.right -= ceilHalfStroke
+        p.setStrokeWidth(EVENT_RECT_STROKE_WIDTH.toFloat())
+        p.setColor(color)
+        val alpha: Int = p.getAlpha()
+        p.setAlpha(mEventsAlpha)
+        canvas.drawRect(r, p)
+        p.setAlpha(alpha)
+        p.setStyle(Style.FILL)
+
+        // Setup rect for drawEventText which follows
+        r.top = event.top.toInt() + EVENT_RECT_TOP_MARGIN
+        r.bottom = event.bottom.toInt() - EVENT_RECT_BOTTOM_MARGIN
+        r.left = event.left.toInt() + EVENT_RECT_LEFT_MARGIN
+        r.right = event.right.toInt() - EVENT_RECT_RIGHT_MARGIN
+        return r
+    }
+
+    private val drawTextSanitizerFilter: Pattern = Pattern.compile("[\t\n],")
+
+    // Sanitize a string before passing it to drawText or else we get little
+    // squares. For newlines and tabs before a comma, delete the character.
+    // Otherwise, just replace them with a space.
+    private fun drawTextSanitizer(string: String, maxEventTextLen: Int): String {
+        var string = string
+        val m: Matcher = drawTextSanitizerFilter.matcher(string)
+        string = m.replaceAll(",")
+        var len: Int = string.length
+        if (maxEventTextLen <= 0) {
+            string = ""
+            len = 0
+        } else if (len > maxEventTextLen) {
+            string = string.substring(0, maxEventTextLen)
+            len = maxEventTextLen
+        }
+        return string.replace('\n', ' ')
+    }
+
+    private fun drawEventText(
+        eventLayout: StaticLayout?,
+        rect: Rect,
+        canvas: Canvas,
+        top: Int,
+        bottom: Int,
+        center: Boolean
+    ) {
+        // drawEmptyRect(canvas, rect, 0xFFFF00FF); // for debugging
+        val width: Int = rect.right - rect.left
+        val height: Int = rect.bottom - rect.top
+
+        // If the rectangle is too small for text, then return
+        if (eventLayout == null || width < MIN_CELL_WIDTH_FOR_TEXT) {
+            return
+        }
+        var totalLineHeight = 0
+        val lineCount: Int = eventLayout.getLineCount()
+        for (i in 0 until lineCount) {
+            val lineBottom: Int = eventLayout.getLineBottom(i)
+            totalLineHeight = if (lineBottom <= height) {
+                lineBottom
+            } else {
+                break
+            }
+        }
+
+        // + 2 is small workaround when the font is slightly bigger than the rect. This will
+        // still allow the text to be shown without overflowing into the other all day rects.
+        if (totalLineHeight == 0 || rect.top > bottom || rect.top + totalLineHeight + 2 < top) {
+            return
+        }
+
+        // Use a StaticLayout to format the string.
+        canvas.save()
+        //  canvas.translate(rect.left, rect.top + (rect.bottom - rect.top / 2));
+        val padding = if (center) (rect.bottom - rect.top - totalLineHeight) / 2 else 0
+        canvas.translate(rect.left.toFloat(), rect.top.toFloat() + padding)
+        rect.left = 0
+        rect.right = width
+        rect.top = 0
+        rect.bottom = totalLineHeight
+
+        // There's a bug somewhere. If this rect is outside of a previous
+        // cliprect, this becomes a no-op. What happens is that the text draw
+        // past the event rect. The current fix is to not draw the staticLayout
+        // at all if it is completely out of bound.
+        canvas.clipRect(rect)
+        eventLayout.draw(canvas)
+        canvas.restore()
+    }
+
+    // The following routines are called from the parent activity when certain
+    // touch events occur.
+    private fun doDown(ev: MotionEvent) {
+        mTouchMode = TOUCH_MODE_DOWN
+        mViewStartX = 0
+        mOnFlingCalled = false
+        mHandler?.removeCallbacks(mContinueScroll)
+        val x = ev.getX().toInt()
+        val y = ev.getY().toInt()
+
+        // Save selection information: we use setSelectionFromPosition to find the selected event
+        // in order to show the "clicked" color. But since it is also setting the selected info
+        // for new events, we need to restore the old info after calling the function.
+        val oldSelectedEvent: Event? = mSelectedEvent
+        val oldSelectionDay = mSelectionDay
+        val oldSelectionHour = mSelectionHour
+        if (setSelectionFromPosition(x, y, false)) {
+            // If a time was selected (a blue selection box is visible) and the click location
+            // is in the selected time, do not show a click on an event to prevent a situation
+            // of both a selection and an event are clicked when they overlap.
+            val pressedSelected = (mSelectionMode != SELECTION_HIDDEN &&
+                oldSelectionDay == mSelectionDay && oldSelectionHour == mSelectionHour)
+            if (!pressedSelected && mSelectedEvent != null) {
+                mSavedClickedEvent = mSelectedEvent
+                mDownTouchTime = System.currentTimeMillis()
+                postDelayed(mSetClick, mOnDownDelay.toLong())
+            } else {
+                eventClickCleanup()
+            }
+        }
+        mSelectedEvent = oldSelectedEvent
+        mSelectionDay = oldSelectionDay
+        mSelectionHour = oldSelectionHour
+        invalidate()
+    }
+
+    // Kicks off all the animations when the expand allday area is tapped
+    private fun doExpandAllDayClick() {
+        mShowAllAllDayEvents = !mShowAllAllDayEvents
+        ObjectAnimator.setFrameDelay(0)
+
+        // Determine the starting height
+        if (mAnimateDayHeight == 0) {
+            mAnimateDayHeight =
+                if (mShowAllAllDayEvents) mAlldayHeight - MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt()
+                else mAlldayHeight
+        }
+        // Cancel current animations
+        mCancellingAnimations = true
+        if (mAlldayAnimator != null) {
+            mAlldayAnimator?.cancel()
+        }
+        if (mAlldayEventAnimator != null) {
+            mAlldayEventAnimator?.cancel()
+        }
+        if (mMoreAlldayEventsAnimator != null) {
+            mMoreAlldayEventsAnimator?.cancel()
+        }
+        mCancellingAnimations = false
+        // get new animators
+        mAlldayAnimator = allDayAnimator
+        mAlldayEventAnimator = allDayEventAnimator
+        mMoreAlldayEventsAnimator = ObjectAnimator.ofInt(
+            this,
+            "moreAllDayEventsTextAlpha",
+            if (mShowAllAllDayEvents) MORE_EVENTS_MAX_ALPHA else 0,
+            if (mShowAllAllDayEvents) 0 else MORE_EVENTS_MAX_ALPHA
+        )
+
+        // Set up delays and start the animators
+        mAlldayAnimator?.setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION
+            else 0)
+        mAlldayAnimator?.start()
+        mMoreAlldayEventsAnimator?.setStartDelay(if (mShowAllAllDayEvents) 0
+            else ANIMATION_DURATION)
+        mMoreAlldayEventsAnimator?.setDuration(ANIMATION_SECONDARY_DURATION)
+        mMoreAlldayEventsAnimator?.start()
+        if (mAlldayEventAnimator != null) {
+            // This is the only animator that can return null, so check it
+            mAlldayEventAnimator
+                ?.setStartDelay(if (mShowAllAllDayEvents) ANIMATION_SECONDARY_DURATION else 0)
+            mAlldayEventAnimator?.start()
+        }
+    }
+
+    /**
+     * Figures out the initial heights for allDay events and space when
+     * a view is being set up.
+     */
+    fun initAllDayHeights() {
+        if (mMaxAlldayEvents <= mMaxUnexpandedAlldayEventCount) {
+            return
+        }
+        if (mShowAllAllDayEvents) {
+            var maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT
+            maxADHeight = Math.min(
+                maxADHeight,
+                (mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt()
+            )
+            mAnimateDayEventHeight = maxADHeight / mMaxAlldayEvents
+        } else {
+            mAnimateDayEventHeight = MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt()
+        }
+    } // First calculate the absolute max height
+    // Now expand to fit but not beyond the absolute max
+    // calculate the height of individual events in order to fit
+    // if there's nothing to animate just return
+
+    // Set up the animator with the calculated values
+    // Sets up an animator for changing the height of allday events
+    private val allDayEventAnimator: ObjectAnimator?
+        private get() {
+            // First calculate the absolute max height
+            var maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT
+            // Now expand to fit but not beyond the absolute max
+            maxADHeight = Math.min(
+                maxADHeight,
+                (mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt()
+            )
+            // calculate the height of individual events in order to fit
+            val fitHeight = maxADHeight / mMaxAlldayEvents
+            val currentHeight = mAnimateDayEventHeight
+            val desiredHeight =
+                if (mShowAllAllDayEvents) fitHeight else MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT.toInt()
+            // if there's nothing to animate just return
+            if (currentHeight == desiredHeight) {
+                return null
+            }
+
+            // Set up the animator with the calculated values
+            val animator: ObjectAnimator = ObjectAnimator.ofInt(
+                this, "animateDayEventHeight",
+                currentHeight, desiredHeight
+            )
+            animator.setDuration(ANIMATION_DURATION)
+            return animator
+        }
+
+    // Set up the animator with the calculated values
+    // Sets up an animator for changing the height of the allday area
+    private val allDayAnimator: ObjectAnimator
+        private get() {
+            // Calculate the absolute max height
+            var maxADHeight = mViewHeight - DAY_HEADER_HEIGHT - MIN_HOURS_HEIGHT
+            // Find the desired height but don't exceed abs max
+            maxADHeight = Math.min(
+                maxADHeight,
+                (mMaxAlldayEvents * MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT).toInt()
+            )
+            // calculate the current and desired heights
+            val currentHeight = if (mAnimateDayHeight != 0) mAnimateDayHeight else mAlldayHeight
+            val desiredHeight =
+                if (mShowAllAllDayEvents) maxADHeight else (MAX_UNEXPANDED_ALLDAY_HEIGHT -
+                    MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT - 1).toInt()
+
+            // Set up the animator with the calculated values
+            val animator: ObjectAnimator = ObjectAnimator.ofInt(
+                this, "animateDayHeight",
+                currentHeight, desiredHeight
+            )
+            animator.setDuration(ANIMATION_DURATION)
+            animator.addListener(object : AnimatorListenerAdapter() {
+                @Override
+                override fun onAnimationEnd(animation: Animator?) {
+                    if (!mCancellingAnimations) {
+                        // when finished, set this to 0 to signify not animating
+                        mAnimateDayHeight = 0
+                        mUseExpandIcon = !mShowAllAllDayEvents
+                    }
+                    mRemeasure = true
+                    invalidate()
+                }
+            })
+            return animator
+        }
+
+    // setter for the 'box +n' alpha text used by the animator
+    fun setMoreAllDayEventsTextAlpha(alpha: Int) {
+        mMoreAlldayEventsTextAlpha = alpha
+        invalidate()
+    }
+
+    // setter for the height of the allday area used by the animator
+    fun setAnimateDayHeight(height: Int) {
+        mAnimateDayHeight = height
+        mRemeasure = true
+        invalidate()
+    }
+
+    // setter for the height of allday events used by the animator
+    fun setAnimateDayEventHeight(height: Int) {
+        mAnimateDayEventHeight = height
+        mRemeasure = true
+        invalidate()
+    }
+
+    private fun doSingleTapUp(ev: MotionEvent) {
+        if (!mHandleActionUp || mScrolling) {
+            return
+        }
+        val x = ev.getX().toInt()
+        val y = ev.getY().toInt()
+        val selectedDay = mSelectionDay
+        val selectedHour = mSelectionHour
+        if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
+            // check if the tap was in the allday expansion area
+            val bottom = mFirstCell
+            if (x < mHoursWidth && y > DAY_HEADER_HEIGHT && y < DAY_HEADER_HEIGHT + mAlldayHeight ||
+                !mShowAllAllDayEvents && mAnimateDayHeight == 0 && y < bottom && y >= bottom -
+                MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT
+            ) {
+                doExpandAllDayClick()
+                return
+            }
+        }
+        val validPosition = setSelectionFromPosition(x, y, false)
+        if (!validPosition) {
+            if (y < DAY_HEADER_HEIGHT) {
+                val selectedTime = Time(mBaseDate)
+                selectedTime.setJulianDay(mSelectionDay)
+                selectedTime.hour = mSelectionHour
+                selectedTime.normalize(true /* ignore isDst */)
+                mController.sendEvent(
+                    this as? Object, EventType.GO_TO, null, null, selectedTime, -1,
+                    ViewType.DAY, CalendarController.EXTRA_GOTO_DATE, null, null
+                )
+            }
+            return
+        }
+        val hasSelection = mSelectionMode != SELECTION_HIDDEN
+        val pressedSelected = ((hasSelection || mTouchExplorationEnabled) &&
+            selectedDay == mSelectionDay && selectedHour == mSelectionHour)
+        if (mSelectedEvent != null) {
+            // If the tap is on an event, launch the "View event" view
+            if (mIsAccessibilityEnabled) {
+                mAccessibilityMgr?.interrupt()
+            }
+            mSelectionMode = SELECTION_HIDDEN
+            var yLocation = ((mSelectedEvent!!.top + mSelectedEvent!!.bottom) / 2) as Int
+            // Y location is affected by the position of the event in the scrolling
+            // view (mViewStartY) and the presence of all day events (mFirstCell)
+            if (!mSelectedEvent!!.allDay) {
+                yLocation += mFirstCell - mViewStartY
+            }
+            mClickedYLocation = yLocation
+            val clearDelay: Long = CLICK_DISPLAY_DURATION + mOnDownDelay -
+                (System.currentTimeMillis() - mDownTouchTime)
+            if (clearDelay > 0) {
+                this.postDelayed(mClearClick, clearDelay)
+            } else {
+                this.post(mClearClick)
+            }
+        }
+        invalidate()
+    }
+
+    private fun doLongPress(ev: MotionEvent) {
+        eventClickCleanup()
+        if (mScrolling) {
+            return
+        }
+
+        // Scale gesture in progress
+        if (mStartingSpanY != 0f) {
+            return
+        }
+        val x = ev.getX().toInt()
+        val y = ev.getY().toInt()
+        val validPosition = setSelectionFromPosition(x, y, false)
+        if (!validPosition) {
+            // return if the touch wasn't on an area of concern
+            return
+        }
+        invalidate()
+        performLongClick()
+    }
+
+    private fun doScroll(e1: MotionEvent, e2: MotionEvent, deltaX: Float, deltaY: Float) {
+        cancelAnimation()
+        if (mStartingScroll) {
+            mInitialScrollX = 0f
+            mInitialScrollY = 0f
+            mStartingScroll = false
+        }
+        mInitialScrollX += deltaX
+        mInitialScrollY += deltaY
+        val distanceX = mInitialScrollX.toInt()
+        val distanceY = mInitialScrollY.toInt()
+        val focusY = getAverageY(e2)
+        if (mRecalCenterHour) {
+            // Calculate the hour that correspond to the average of the Y touch points
+            mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) /
+                (mCellHeight + DAY_GAP))
+            mRecalCenterHour = false
+        }
+
+        // If we haven't figured out the predominant scroll direction yet,
+        // then do it now.
+        if (mTouchMode == TOUCH_MODE_DOWN) {
+            val absDistanceX: Int = Math.abs(distanceX)
+            val absDistanceY: Int = Math.abs(distanceY)
+            mScrollStartY = mViewStartY
+            mPreviousDirection = 0
+            if (absDistanceX > absDistanceY) {
+                val slopFactor = if (mScaleGestureDetector.isInProgress()) 20 else 2
+                if (absDistanceX > mScaledPagingTouchSlop * slopFactor) {
+                    mTouchMode = TOUCH_MODE_HSCROLL
+                    mViewStartX = distanceX
+                    initNextView(-mViewStartX)
+                }
+            } else {
+                mTouchMode = TOUCH_MODE_VSCROLL
+            }
+        } else if (mTouchMode and TOUCH_MODE_HSCROLL != 0) {
+            // We are already scrolling horizontally, so check if we
+            // changed the direction of scrolling so that the other week
+            // is now visible.
+            mViewStartX = distanceX
+            if (distanceX != 0) {
+                val direction = if (distanceX > 0) 1 else -1
+                if (direction != mPreviousDirection) {
+                    // The user has switched the direction of scrolling
+                    // so re-init the next view
+                    initNextView(-mViewStartX)
+                    mPreviousDirection = direction
+                }
+            }
+        }
+        if (mTouchMode and TOUCH_MODE_VSCROLL != 0) {
+            // Calculate the top of the visible region in the calendar grid.
+            // Increasing/decrease this will scroll the calendar grid up/down.
+            mViewStartY = ((mGestureCenterHour * (mCellHeight + DAY_GAP) -
+                focusY) + DAY_HEADER_HEIGHT + mAlldayHeight).toInt()
+
+            // If dragging while already at the end, do a glow
+            val pulledToY = (mScrollStartY + deltaY).toInt()
+            if (pulledToY < 0) {
+                mEdgeEffectTop.onPull(deltaY / mViewHeight)
+                if (!mEdgeEffectBottom.isFinished()) {
+                    mEdgeEffectBottom.onRelease()
+                }
+            } else if (pulledToY > mMaxViewStartY) {
+                mEdgeEffectBottom.onPull(deltaY / mViewHeight)
+                if (!mEdgeEffectTop.isFinished()) {
+                    mEdgeEffectTop.onRelease()
+                }
+            }
+            if (mViewStartY < 0) {
+                mViewStartY = 0
+                mRecalCenterHour = true
+            } else if (mViewStartY > mMaxViewStartY) {
+                mViewStartY = mMaxViewStartY
+                mRecalCenterHour = true
+            }
+            if (mRecalCenterHour) {
+                // Calculate the hour that correspond to the average of the Y touch points
+                mGestureCenterHour = ((mViewStartY + focusY - DAY_HEADER_HEIGHT - mAlldayHeight) /
+                    (mCellHeight + DAY_GAP))
+                mRecalCenterHour = false
+            }
+            computeFirstHour()
+        }
+        mScrolling = true
+        mSelectionMode = SELECTION_HIDDEN
+        invalidate()
+    }
+
+    private fun getAverageY(me: MotionEvent): Float {
+        val count: Int = me.getPointerCount()
+        var focusY = 0f
+        for (i in 0 until count) {
+            focusY += me.getY(i)
+        }
+        focusY /= count.toFloat()
+        return focusY
+    }
+
+    private fun cancelAnimation() {
+        val `in`: Animation? = mViewSwitcher?.getInAnimation()
+        if (`in` != null) {
+            // cancel() doesn't terminate cleanly.
+            `in`?.scaleCurrentDuration(0f)
+        }
+        val out: Animation? = mViewSwitcher?.getOutAnimation()
+        if (out != null) {
+            // cancel() doesn't terminate cleanly.
+            out?.scaleCurrentDuration(0f)
+        }
+    }
+
+    private fun doFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float) {
+        cancelAnimation()
+        mSelectionMode = SELECTION_HIDDEN
+        eventClickCleanup()
+        mOnFlingCalled = true
+        if (mTouchMode and TOUCH_MODE_HSCROLL != 0) {
+            // Horizontal fling.
+            // initNextView(deltaX);
+            mTouchMode = TOUCH_MODE_INITIAL_STATE
+            if (DEBUG) Log.d(TAG, "doFling: velocityX $velocityX")
+            val deltaX = e2.getX().toInt() - e1.getX().toInt()
+            switchViews(deltaX < 0, mViewStartX.toFloat(), mViewWidth.toFloat(), velocityX)
+            mViewStartX = 0
+            return
+        }
+        if (mTouchMode and TOUCH_MODE_VSCROLL == 0) {
+            if (DEBUG) Log.d(TAG, "doFling: no fling")
+            return
+        }
+
+        // Vertical fling.
+        mTouchMode = TOUCH_MODE_INITIAL_STATE
+        mViewStartX = 0
+        if (DEBUG) {
+            Log.d(TAG, "doFling: mViewStartY$mViewStartY velocityY $velocityY")
+        }
+
+        // Continue scrolling vertically
+        mScrolling = true
+        mScroller.fling(
+            0 /* startX */, mViewStartY /* startY */, 0 /* velocityX */,
+            (-velocityY).toInt(), 0 /* minX */, 0 /* maxX */, 0 /* minY */,
+            mMaxViewStartY /* maxY */, OVERFLING_DISTANCE, OVERFLING_DISTANCE
+        )
+
+        // When flinging down, show a glow when it hits the end only if it
+        // wasn't started at the top
+        if (velocityY > 0 && mViewStartY != 0) {
+            mCallEdgeEffectOnAbsorb = true
+        } else if (velocityY < 0 && mViewStartY != mMaxViewStartY) {
+            mCallEdgeEffectOnAbsorb = true
+        }
+        mHandler?.post(mContinueScroll)
+    }
+
+    private fun initNextView(deltaX: Int): Boolean {
+        // Change the view to the previous day or week
+        val view = mViewSwitcher.getNextView() as DayView
+        val date: Time? = view.mBaseDate
+        date?.set(mBaseDate)
+        val switchForward: Boolean
+        if (deltaX > 0) {
+            date!!.monthDay -= mNumDays
+            view.setSelectedDay(mSelectionDay - mNumDays)
+            switchForward = false
+        } else {
+            date!!.monthDay += mNumDays
+            view.setSelectedDay(mSelectionDay + mNumDays)
+            switchForward = true
+        }
+        date?.normalize(true /* ignore isDst */)
+        initView(view)
+        view.layout(getLeft(), getTop(), getRight(), getBottom())
+        view.reloadEvents()
+        return switchForward
+    }
+
+    // ScaleGestureDetector.OnScaleGestureListener
+    override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
+        mHandleActionUp = false
+        val gestureCenterInPixels: Float = detector.getFocusY() - DAY_HEADER_HEIGHT - mAlldayHeight
+        mGestureCenterHour = (mViewStartY + gestureCenterInPixels) / (mCellHeight + DAY_GAP)
+        mStartingSpanY = Math.max(MIN_Y_SPAN.toFloat(),
+            Math.abs(detector.getCurrentSpanY().toFloat()))
+        mCellHeightBeforeScaleGesture = mCellHeight
+        if (DEBUG_SCALING) {
+            val ViewStartHour = mViewStartY / (mCellHeight + DAY_GAP).toFloat()
+            Log.d(
+                TAG, "onScaleBegin: mGestureCenterHour:" + mGestureCenterHour +
+                    "\tViewStartHour: " + ViewStartHour + "\tmViewStartY:" + mViewStartY +
+                    "\tmCellHeight:" + mCellHeight + " SpanY:" + detector.getCurrentSpanY()
+            )
+        }
+        return true
+    }
+
+    // ScaleGestureDetector.OnScaleGestureListener
+    override fun onScale(detector: ScaleGestureDetector): Boolean {
+        val spanY: Float = Math.max(MIN_Y_SPAN.toFloat(),
+            Math.abs(detector.getCurrentSpanY().toFloat()))
+        mCellHeight = (mCellHeightBeforeScaleGesture * spanY / mStartingSpanY).toInt()
+        if (mCellHeight < mMinCellHeight) {
+            // If mStartingSpanY is too small, even a small increase in the
+            // gesture can bump the mCellHeight beyond MAX_CELL_HEIGHT
+            mStartingSpanY = spanY
+            mCellHeight = mMinCellHeight
+            mCellHeightBeforeScaleGesture = mMinCellHeight
+        } else if (mCellHeight > MAX_CELL_HEIGHT) {
+            mStartingSpanY = spanY
+            mCellHeight = MAX_CELL_HEIGHT
+            mCellHeightBeforeScaleGesture = MAX_CELL_HEIGHT
+        }
+        val gestureCenterInPixels = detector.getFocusY().toInt() - DAY_HEADER_HEIGHT - mAlldayHeight
+        mViewStartY = (mGestureCenterHour * (mCellHeight + DAY_GAP)).toInt() - gestureCenterInPixels
+        mMaxViewStartY = HOUR_GAP + 24 * (mCellHeight + HOUR_GAP) - mGridAreaHeight
+        if (DEBUG_SCALING) {
+            val ViewStartHour = mViewStartY / (mCellHeight + DAY_GAP).toFloat()
+            Log.d(
+                TAG, "onScale: mGestureCenterHour:" + mGestureCenterHour + "\tViewStartHour: " +
+                    ViewStartHour + "\tmViewStartY:" + mViewStartY + "\tmCellHeight:" +
+                    mCellHeight + " SpanY:" + detector.getCurrentSpanY()
+            )
+        }
+        if (mViewStartY < 0) {
+            mViewStartY = 0
+            mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) /
+                (mCellHeight + DAY_GAP).toFloat())
+        } else if (mViewStartY > mMaxViewStartY) {
+            mViewStartY = mMaxViewStartY
+            mGestureCenterHour = ((mViewStartY + gestureCenterInPixels) /
+                (mCellHeight + DAY_GAP).toFloat())
+        }
+        computeFirstHour()
+        mRemeasure = true
+        invalidate()
+        return true
+    }
+
+    // ScaleGestureDetector.OnScaleGestureListener
+    override fun onScaleEnd(detector: ScaleGestureDetector?) {
+        mScrollStartY = mViewStartY
+        mInitialScrollY = 0f
+        mInitialScrollX = 0f
+        mStartingSpanY = 0f
+    }
+
+    @Override
+    override fun onTouchEvent(ev: MotionEvent): Boolean {
+        val action: Int = ev.getAction()
+        if (DEBUG) Log.e(TAG, "" + action + " ev.getPointerCount() = " + ev.getPointerCount())
+        if (ev.getActionMasked() === MotionEvent.ACTION_DOWN ||
+            ev.getActionMasked() === MotionEvent.ACTION_UP ||
+            ev.getActionMasked() === MotionEvent.ACTION_POINTER_UP ||
+            ev.getActionMasked() === MotionEvent.ACTION_POINTER_DOWN
+        ) {
+            mRecalCenterHour = true
+        }
+        if (mTouchMode and TOUCH_MODE_HSCROLL == 0) {
+            mScaleGestureDetector.onTouchEvent(ev)
+        }
+        return when (action) {
+            MotionEvent.ACTION_DOWN -> {
+                mStartingScroll = true
+                if (DEBUG) {
+                    Log.e(
+                        TAG,
+                        "ACTION_DOWN ev.getDownTime = " + ev.getDownTime().toString() + " Cnt=" +
+                            ev.getPointerCount()
+                    )
+                }
+                val bottom =
+                    mAlldayHeight + DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN
+                mTouchStartedInAlldayArea = if (ev.getY() < bottom) {
+                    true
+                } else {
+                    false
+                }
+                mHandleActionUp = true
+                mGestureDetector.onTouchEvent(ev)
+                true
+            }
+            MotionEvent.ACTION_MOVE -> {
+                if (DEBUG) Log.e(
+                    TAG,
+                    "ACTION_MOVE Cnt=" + ev.getPointerCount() + this@DayView
+                )
+                mGestureDetector.onTouchEvent(ev)
+                true
+            }
+            MotionEvent.ACTION_UP -> {
+                if (DEBUG) Log.e(
+                    TAG,
+                    "ACTION_UP Cnt=" + ev.getPointerCount() + mHandleActionUp
+                )
+                mEdgeEffectTop.onRelease()
+                mEdgeEffectBottom.onRelease()
+                mStartingScroll = false
+                mGestureDetector.onTouchEvent(ev)
+                if (!mHandleActionUp) {
+                    mHandleActionUp = true
+                    mViewStartX = 0
+                    invalidate()
+                    return true
+                }
+                if (mOnFlingCalled) {
+                    return true
+                }
+
+                // If we were scrolling, then reset the selected hour so that it
+                // is visible.
+                if (mScrolling) {
+                    mScrolling = false
+                    resetSelectedHour()
+                    invalidate()
+                }
+                if (mTouchMode and TOUCH_MODE_HSCROLL != 0) {
+                    mTouchMode = TOUCH_MODE_INITIAL_STATE
+                    if (Math.abs(mViewStartX) > mHorizontalSnapBackThreshold) {
+                        // The user has gone beyond the threshold so switch views
+                        if (DEBUG) Log.d(
+                            TAG,
+                            "- horizontal scroll: switch views"
+                        )
+                        switchViews(
+                            mViewStartX > 0,
+                            mViewStartX.toFloat(),
+                            mViewWidth.toFloat(),
+                            0f
+                        )
+                        mViewStartX = 0
+                        return true
+                    } else {
+                        // Not beyond the threshold so invalidate which will cause
+                        // the view to snap back. Also call recalc() to ensure
+                        // that we have the correct starting date and title.
+                        if (DEBUG) Log.d(
+                            TAG,
+                            "- horizontal scroll: snap back"
+                        )
+                        recalc()
+                        invalidate()
+                        mViewStartX = 0
+                    }
+                }
+                true
+            }
+            MotionEvent.ACTION_CANCEL -> {
+                if (DEBUG) Log.e(
+                    TAG,
+                    "ACTION_CANCEL"
+                )
+                mGestureDetector.onTouchEvent(ev)
+                mScrolling = false
+                resetSelectedHour()
+                true
+            }
+            else -> {
+                if (DEBUG) Log.e(
+                    TAG,
+                    "Not MotionEvent " + ev.toString()
+                )
+                if (mGestureDetector.onTouchEvent(ev)) {
+                    true
+                } else super.onTouchEvent(ev)
+            }
+        }
+    }
+
+    override fun onCreateContextMenu(menu: ContextMenu, view: View?, menuInfo: ContextMenuInfo?) {
+        var item: MenuItem
+
+        // If the trackball is held down, then the context menu pops up and
+        // we never get onKeyUp() for the long-press. So check for it here
+        // and change the selection to the long-press state.
+        if (mSelectionMode != SELECTION_LONGPRESS) {
+            invalidate()
+        }
+        val startMillis = selectedTimeInMillis
+        val flags: Int = (DateUtils.FORMAT_SHOW_TIME
+            or DateUtils.FORMAT_CAP_NOON_MIDNIGHT
+            or DateUtils.FORMAT_SHOW_WEEKDAY)
+        val title: String? = Utils.formatDateRange(mContext, startMillis, startMillis, flags)
+        menu.setHeaderTitle(title)
+        mPopup?.dismiss()
+    }
+
+    /**
+     * Sets mSelectionDay and mSelectionHour based on the (x,y) touch position.
+     * If the touch position is not within the displayed grid, then this
+     * method returns false.
+     *
+     * @param x the x position of the touch
+     * @param y the y position of the touch
+     * @param keepOldSelection - do not change the selection info (used for invoking accessibility
+     * messages)
+     * @return true if the touch position is valid
+     */
+    private fun setSelectionFromPosition(x: Int, y: Int, keepOldSelection: Boolean): Boolean {
+        var x = x
+        var savedEvent: Event? = null
+        var savedDay = 0
+        var savedHour = 0
+        var savedAllDay = false
+        if (keepOldSelection) {
+            // Store selection info and restore it at the end. This way, we can invoke the
+            // right accessibility message without affecting the selection.
+            savedEvent = mSelectedEvent
+            savedDay = mSelectionDay
+            savedHour = mSelectionHour
+            savedAllDay = mSelectionAllday
+        }
+        if (x < mHoursWidth) {
+            x = mHoursWidth
+        }
+        var day = (x - mHoursWidth) / (mCellWidth + DAY_GAP)
+        if (day >= mNumDays) {
+            day = mNumDays - 1
+        }
+        day += mFirstJulianDay
+        setSelectedDay(day)
+        if (y < DAY_HEADER_HEIGHT) {
+            sendAccessibilityEventAsNeeded(false)
+            return false
+        }
+        setSelectedHour(mFirstHour) /* First fully visible hour */
+        mSelectionAllday = if (y < mFirstCell) {
+            true
+        } else {
+            // y is now offset from top of the scrollable region
+            val adjustedY = y - mFirstCell
+            if (adjustedY < mFirstHourOffset) {
+                setSelectedHour(mSelectionHour - 1) /* In the partially visible hour */
+            } else {
+                setSelectedHour(
+                    mSelectionHour +
+                        (adjustedY - mFirstHourOffset) / (mCellHeight + HOUR_GAP)
+                )
+            }
+            false
+        }
+        findSelectedEvent(x, y)
+        sendAccessibilityEventAsNeeded(true)
+
+        // Restore old values
+        if (keepOldSelection) {
+            mSelectedEvent = savedEvent
+            mSelectionDay = savedDay
+            mSelectionHour = savedHour
+            mSelectionAllday = savedAllDay
+        }
+        return true
+    }
+
+    private fun findSelectedEvent(x: Int, y: Int) {
+        var y = y
+        val date = mSelectionDay
+        val cellWidth = mCellWidth
+        var events: ArrayList<Event>? = mEvents
+        var numEvents: Int = events!!.size
+        val left = computeDayLeftPosition(mSelectionDay - mFirstJulianDay)
+        val top = 0
+        setSelectedEvent(null)
+        mSelectedEvents.clear()
+        if (mSelectionAllday) {
+            var yDistance: Float
+            var minYdistance = 10000.0f // any large number
+            var closestEvent: Event? = null
+            val drawHeight = mAlldayHeight.toFloat()
+            val yOffset = DAY_HEADER_HEIGHT + ALLDAY_TOP_MARGIN
+            var maxUnexpandedColumn = mMaxUnexpandedAlldayEventCount
+            if (mMaxAlldayEvents > mMaxUnexpandedAlldayEventCount) {
+                // Leave a gap for the 'box +n' text
+                maxUnexpandedColumn--
+            }
+            events = mAllDayEvents
+            numEvents = events!!.size
+            for (i in 0 until numEvents) {
+                val event: Event? = events?.get(i)
+                if (!event!!.drawAsAllday() ||
+                    !mShowAllAllDayEvents && event!!.getColumn() >= maxUnexpandedColumn
+                ) {
+                    // Don't check non-allday events or events that aren't shown
+                    continue
+                }
+                if (event!!.startDay <= mSelectionDay && event!!.endDay >= mSelectionDay) {
+                    val numRectangles =
+                        if (mShowAllAllDayEvents) mMaxAlldayEvents.toFloat()
+                        else mMaxUnexpandedAlldayEventCount.toFloat()
+                    var height = drawHeight / numRectangles
+                    if (height > MAX_HEIGHT_OF_ONE_ALLDAY_EVENT) {
+                        height = MAX_HEIGHT_OF_ONE_ALLDAY_EVENT.toFloat()
+                    }
+                    val eventTop: Float = yOffset + height * event?.getColumn()
+                    val eventBottom = eventTop + height
+                    if (eventTop < y && eventBottom > y) {
+                        // If the touch is inside the event rectangle, then
+                        // add the event.
+                        mSelectedEvents.add(event)
+                        closestEvent = event
+                        break
+                    } else {
+                        // Find the closest event
+                        yDistance = if (eventTop >= y) {
+                            eventTop - y
+                        } else {
+                            y - eventBottom
+                        }
+                        if (yDistance < minYdistance) {
+                            minYdistance = yDistance
+                            closestEvent = event
+                        }
+                    }
+                }
+            }
+            setSelectedEvent(closestEvent)
+            return
+        }
+
+        // Adjust y for the scrollable bitmap
+        y += mViewStartY - mFirstCell
+
+        // Use a region around (x,y) for the selection region
+        val region: Rect = mRect
+        region.left = x - 10
+        region.right = x + 10
+        region.top = y - 10
+        region.bottom = y + 10
+        val geometry: EventGeometry = mEventGeometry
+        for (i in 0 until numEvents) {
+            val event: Event? = events?.get(i)
+            // Compute the event rectangle.
+            if (!geometry.computeEventRect(date, left, top, cellWidth, event as Event)) {
+                continue
+            }
+
+            // If the event intersects the selection region, then add it to
+            // mSelectedEvents.
+            if (geometry.eventIntersectsSelection(event as Event, region)) {
+                mSelectedEvents.add(event as Event)
+            }
+        }
+
+        // If there are any events in the selected region, then assign the
+        // closest one to mSelectedEvent.
+        if (mSelectedEvents.size > 0) {
+            val len: Int = mSelectedEvents.size
+            var closestEvent: Event? = null
+            var minDist = (mViewWidth + mViewHeight).toFloat() // some large distance
+            for (index in 0 until len) {
+                val ev: Event? = mSelectedEvents?.get(index)
+                val dist: Float = geometry.pointToEvent(x.toFloat(), y.toFloat(), ev as Event)
+                if (dist < minDist) {
+                    minDist = dist
+                    closestEvent = ev
+                }
+            }
+            setSelectedEvent(closestEvent)
+
+            // Keep the selected hour and day consistent with the selected
+            // event. They could be different if we touched on an empty hour
+            // slot very close to an event in the previous hour slot. In
+            // that case we will select the nearby event.
+            val startDay: Int = mSelectedEvent!!.startDay
+            val endDay: Int = mSelectedEvent!!.endDay
+            if (mSelectionDay < startDay) {
+                setSelectedDay(startDay)
+            } else if (mSelectionDay > endDay) {
+                setSelectedDay(endDay)
+            }
+            val startHour: Int = mSelectedEvent!!.startTime / 60
+            val endHour: Int
+            endHour = if (mSelectedEvent!!.startTime < mSelectedEvent!!.endTime) {
+                (mSelectedEvent!!.endTime - 1) / 60
+            } else {
+                mSelectedEvent!!.endTime / 60
+            }
+            if (mSelectionHour < startHour && mSelectionDay == startDay) {
+                setSelectedHour(startHour)
+            } else if (mSelectionHour > endHour && mSelectionDay == endDay) {
+                setSelectedHour(endHour)
+            }
+        }
+    }
+
+    // Encapsulates the code to continue the scrolling after the
+    // finger is lifted. Instead of stopping the scroll immediately,
+    // the scroll continues to "free spin" and gradually slows down.
+    private inner class ContinueScroll : Runnable {
+        override fun run() {
+            mScrolling = mScrolling && mScroller.computeScrollOffset()
+            if (!mScrolling || mPaused) {
+                resetSelectedHour()
+                invalidate()
+                return
+            }
+            mViewStartY = mScroller.getCurrY()
+            if (mCallEdgeEffectOnAbsorb) {
+                if (mViewStartY < 0) {
+                    mEdgeEffectTop.onAbsorb(mLastVelocity.toInt())
+                    mCallEdgeEffectOnAbsorb = false
+                } else if (mViewStartY > mMaxViewStartY) {
+                    mEdgeEffectBottom.onAbsorb(mLastVelocity.toInt())
+                    mCallEdgeEffectOnAbsorb = false
+                }
+                mLastVelocity = mScroller.getCurrVelocity()
+            }
+            if (mScrollStartY == 0 || mScrollStartY == mMaxViewStartY) {
+                // Allow overscroll/springback only on a fling,
+                // not a pull/fling from the end
+                if (mViewStartY < 0) {
+                    mViewStartY = 0
+                } else if (mViewStartY > mMaxViewStartY) {
+                    mViewStartY = mMaxViewStartY
+                }
+            }
+            computeFirstHour()
+            mHandler?.post(this)
+            invalidate()
+        }
+    }
+
+    /**
+     * Cleanup the pop-up and timers.
+     */
+    fun cleanup() {
+        // Protect against null-pointer exceptions
+        if (mPopup != null) {
+            mPopup?.dismiss()
+        }
+        mPaused = true
+        mLastPopupEventID = INVALID_EVENT_ID
+        if (mHandler != null) {
+            mHandler?.removeCallbacks(mDismissPopup)
+            mHandler?.removeCallbacks(mUpdateCurrentTime)
+        }
+        Utils.setSharedPreference(
+            mContext, GeneralPreferences.KEY_DEFAULT_CELL_HEIGHT,
+            mCellHeight
+        )
+        // Clear all click animations
+        eventClickCleanup()
+        // Turn off redraw
+        mRemeasure = false
+        // Turn off scrolling to make sure the view is in the correct state if we fling back to it
+        mScrolling = false
+    }
+
+    private fun eventClickCleanup() {
+        this.removeCallbacks(mClearClick)
+        this.removeCallbacks(mSetClick)
+        mClickedEvent = null
+        mSavedClickedEvent = null
+    }
+
+    private fun setSelectedEvent(e: Event?) {
+        mSelectedEvent = e
+        mSelectedEventForAccessibility = e
+    }
+
+    private fun setSelectedHour(h: Int) {
+        mSelectionHour = h
+        mSelectionHourForAccessibility = h
+    }
+
+    private fun setSelectedDay(d: Int) {
+        mSelectionDay = d
+        mSelectionDayForAccessibility = d
+    }
+
+    /**
+     * Restart the update timer
+     */
+    fun restartCurrentTimeUpdates() {
+        mPaused = false
+        if (mHandler != null) {
+            mHandler?.removeCallbacks(mUpdateCurrentTime)
+            mHandler?.post(mUpdateCurrentTime)
+        }
+    }
+
+    @Override
+    protected override fun onDetachedFromWindow() {
+        cleanup()
+        super.onDetachedFromWindow()
+    }
+
+    internal inner class DismissPopup : Runnable {
+        override fun run() {
+            // Protect against null-pointer exceptions
+            if (mPopup != null) {
+                mPopup?.dismiss()
+            }
+        }
+    }
+
+    internal inner class UpdateCurrentTime : Runnable {
+        override fun run() {
+            val currentTime: Long = System.currentTimeMillis()
+            mCurrentTime?.set(currentTime)
+            // % causes update to occur on 5 minute marks (11:10, 11:15, 11:20, etc.)
+            if (!mPaused) {
+                mHandler?.postDelayed(
+                    mUpdateCurrentTime, UPDATE_CURRENT_TIME_DELAY -
+                        currentTime % UPDATE_CURRENT_TIME_DELAY
+                )
+            }
+            mTodayJulianDay = Time.getJulianDay(currentTime, mCurrentTime!!.gmtoff)
+            invalidate()
+        }
+    }
+
+    internal inner class CalendarGestureListener : GestureDetector.SimpleOnGestureListener() {
+        @Override
+        override fun onSingleTapUp(ev: MotionEvent): Boolean {
+            if (DEBUG) Log.e(TAG, "GestureDetector.onSingleTapUp")
+            doSingleTapUp(ev)
+            return true
+        }
+
+        @Override
+        override fun onLongPress(ev: MotionEvent) {
+            if (DEBUG) Log.e(TAG, "GestureDetector.onLongPress")
+            doLongPress(ev)
+        }
+
+        @Override
+        override fun onScroll(
+            e1: MotionEvent,
+            e2: MotionEvent,
+            distanceX: Float,
+            distanceY: Float
+        ): Boolean {
+            var distanceY = distanceY
+            if (DEBUG) Log.e(TAG, "GestureDetector.onScroll")
+            eventClickCleanup()
+            if (mTouchStartedInAlldayArea) {
+                if (Math.abs(distanceX) < Math.abs(distanceY)) {
+                    // Make sure that click feedback is gone when you scroll from the
+                    // all day area
+                    invalidate()
+                    return false
+                }
+                // don't scroll vertically if this started in the allday area
+                distanceY = 0f
+            }
+            doScroll(e1, e2, distanceX, distanceY)
+            return true
+        }
+
+        @Override
+        override fun onFling(
+            e1: MotionEvent,
+            e2: MotionEvent,
+            velocityX: Float,
+            velocityY: Float
+        ): Boolean {
+            var velocityY = velocityY
+            if (DEBUG) Log.e(TAG, "GestureDetector.onFling")
+            if (mTouchStartedInAlldayArea) {
+                if (Math.abs(velocityX) < Math.abs(velocityY)) {
+                    return false
+                }
+                // don't fling vertically if this started in the allday area
+                velocityY = 0f
+            }
+            doFling(e1, e2, velocityX, velocityY)
+            return true
+        }
+
+        @Override
+        override fun onDown(ev: MotionEvent): Boolean {
+            if (DEBUG) Log.e(TAG, "GestureDetector.onDown")
+            doDown(ev)
+            return true
+        }
+    }
+
+    @Override
+    override fun onLongClick(v: View?): Boolean {
+        return true
+    }
+
+    private inner class ScrollInterpolator : Interpolator {
+        override fun getInterpolation(t: Float): Float {
+            var t = t
+            t -= 1.0f
+            t = t * t * t * t * t + 1
+            if ((1 - t) * mAnimationDistance < 1) {
+                cancelAnimation()
+            }
+            return t
+        }
+    }
+
+    private fun calculateDuration(delta: Float, width: Float, velocity: Float): Long {
+        /*
+         * Here we compute a "distance" that will be used in the computation of
+         * the overall snap duration. This is a function of the actual distance
+         * that needs to be traveled; we keep this value close to half screen
+         * size in order to reduce the variance in snap duration as a function
+         * of the distance the page needs to travel.
+         */
+        var velocity = velocity
+        val halfScreenSize = width / 2
+        val distanceRatio = delta / width
+        val distanceInfluenceForSnapDuration = distanceInfluenceForSnapDuration(distanceRatio)
+        val distance = halfScreenSize + halfScreenSize * distanceInfluenceForSnapDuration
+        velocity = Math.abs(velocity)
+        velocity = Math.max(MINIMUM_SNAP_VELOCITY.toFloat(), velocity)
+
+        /*
+         * we want the page's snap velocity to approximately match the velocity
+         * at which the user flings, so we scale the duration by a value near to
+         * the derivative of the scroll interpolator at zero, ie. 5. We use 6 to
+         * make it a little slower.
+         */
+        val duration: Long = 6L * Math.round(1000 * Math.abs(distance / velocity))
+        if (DEBUG) {
+            Log.e(
+                TAG, "halfScreenSize:" + halfScreenSize + " delta:" + delta + " distanceRatio:" +
+                    distanceRatio + " distance:" + distance + " velocity:" + velocity +
+                    " duration:" + duration + " distanceInfluenceForSnapDuration:" +
+                    distanceInfluenceForSnapDuration
+            )
+        }
+        return duration
+    }
+
+    /*
+     * We want the duration of the page snap animation to be influenced by the
+     * distance that the screen has to travel, however, we don't want this
+     * duration to be effected in a purely linear fashion. Instead, we use this
+     * method to moderate the effect that the distance of travel has on the
+     * overall snap duration.
+     */
+    private fun distanceInfluenceForSnapDuration(f: Float): Float {
+        var f = f
+        f -= 0.5f // center the values about 0.
+        f *= (0.3f * Math.PI / 2.0f).toFloat()
+        return Math.sin(f.toDouble()).toFloat()
+    }
+
+    companion object {
+        private const val TAG = "DayView"
+        private const val DEBUG = false
+        private const val DEBUG_SCALING = false
+        private const val PERIOD_SPACE = ". "
+        private var mScale = 0f // Used for supporting different screen densities
+        private const val INVALID_EVENT_ID: Long = -1 // This is used for remembering a null event
+
+        // Duration of the allday expansion
+        private const val ANIMATION_DURATION: Long = 400
+
+        // duration of the more allday event text fade
+        private const val ANIMATION_SECONDARY_DURATION: Long = 200
+
+        // duration of the scroll to go to a specified time
+        private const val GOTO_SCROLL_DURATION = 200
+
+        // duration for events' cross-fade animation
+        private const val EVENTS_CROSS_FADE_DURATION = 400
+
+        // duration to show the event clicked
+        private const val CLICK_DISPLAY_DURATION = 50
+        private const val MENU_DAY = 3
+        private const val MENU_EVENT_VIEW = 5
+        private const val MENU_EVENT_CREATE = 6
+        private const val MENU_EVENT_EDIT = 7
+        private const val MENU_EVENT_DELETE = 8
+        private var DEFAULT_CELL_HEIGHT = 64
+        private var MAX_CELL_HEIGHT = 150
+        private var MIN_Y_SPAN = 100
+        private val CALENDARS_PROJECTION = arrayOf<String>(
+            Calendars._ID, // 0
+            Calendars.CALENDAR_ACCESS_LEVEL, // 1
+            Calendars.OWNER_ACCOUNT
+        )
+        private const val CALENDARS_INDEX_ACCESS_LEVEL = 1
+        private const val CALENDARS_INDEX_OWNER_ACCOUNT = 2
+        private val CALENDARS_WHERE: String = Calendars._ID.toString() + "=%d"
+        private const val FROM_NONE = 0
+        private const val FROM_ABOVE = 1
+        private const val FROM_BELOW = 2
+        private const val FROM_LEFT = 4
+        private const val FROM_RIGHT = 8
+        private const val ACCESS_LEVEL_NONE = 0
+        private const val ACCESS_LEVEL_DELETE = 1
+        private const val ACCESS_LEVEL_EDIT = 2
+        private var mHorizontalSnapBackThreshold = 128
+
+        // Update the current time line every five minutes if the window is left open that long
+        private const val UPDATE_CURRENT_TIME_DELAY = 300000
+        private var mOnDownDelay = 0
+        protected var mStringBuilder: StringBuilder = StringBuilder(50)
+
+        // TODO recreate formatter when locale changes
+        protected var mFormatter: Formatter = Formatter(mStringBuilder, Locale.getDefault())
+
+        // The number of milliseconds to show the popup window
+        private const val POPUP_DISMISS_DELAY = 3000
+        private var GRID_LINE_LEFT_MARGIN = 0f
+        private const val GRID_LINE_INNER_WIDTH = 1f
+        private const val DAY_GAP = 1
+        private const val HOUR_GAP = 1
+
+        // This is the standard height of an allday event with no restrictions
+        private var SINGLE_ALLDAY_HEIGHT = 34
+
+        /**
+         * This is the minimum desired height of a allday event.
+         * When unexpanded, allday events will use this height.
+         * When expanded allDay events will attempt to grow to fit all
+         * events at this height.
+         */
+        private var MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT = 28.0f // in pixels
+
+        /**
+         * This is how big the unexpanded allday height is allowed to be.
+         * It will get adjusted based on screen size
+         */
+        private var MAX_UNEXPANDED_ALLDAY_HEIGHT = (MIN_UNEXPANDED_ALLDAY_EVENT_HEIGHT * 4).toInt()
+
+        /**
+         * This is the minimum size reserved for displaying regular events.
+         * The expanded allDay region can't expand into this.
+         */
+        private const val MIN_HOURS_HEIGHT = 180
+        private var ALLDAY_TOP_MARGIN = 1
+
+        // The largest a single allDay event will become.
+        private var MAX_HEIGHT_OF_ONE_ALLDAY_EVENT = 34
+        private var HOURS_TOP_MARGIN = 2
+        private var HOURS_LEFT_MARGIN = 2
+        private var HOURS_RIGHT_MARGIN = 4
+        private var HOURS_MARGIN = HOURS_LEFT_MARGIN + HOURS_RIGHT_MARGIN
+        private var NEW_EVENT_MARGIN = 4
+        private var NEW_EVENT_WIDTH = 2
+        private var NEW_EVENT_MAX_LENGTH = 16
+        private var CURRENT_TIME_LINE_SIDE_BUFFER = 4
+        private var CURRENT_TIME_LINE_TOP_OFFSET = 2
+
+        /* package */
+        const val MINUTES_PER_HOUR = 60
+
+        /* package */
+        const val MINUTES_PER_DAY = MINUTES_PER_HOUR * 24
+
+        /* package */
+        const val MILLIS_PER_MINUTE = 60 * 1000
+
+        /* package */
+        const val MILLIS_PER_HOUR = 3600 * 1000
+
+        /* package */
+        const val MILLIS_PER_DAY = MILLIS_PER_HOUR * 24
+
+        // More events text will transition between invisible and this alpha
+        private const val MORE_EVENTS_MAX_ALPHA = 0x4C
+        private var DAY_HEADER_ONE_DAY_LEFT_MARGIN = 0
+        private var DAY_HEADER_ONE_DAY_RIGHT_MARGIN = 5
+        private var DAY_HEADER_ONE_DAY_BOTTOM_MARGIN = 6
+        private var DAY_HEADER_RIGHT_MARGIN = 4
+        private var DAY_HEADER_BOTTOM_MARGIN = 3
+        private var DAY_HEADER_FONT_SIZE = 14f
+        private var DATE_HEADER_FONT_SIZE = 32f
+        private var NORMAL_FONT_SIZE = 12f
+        private var EVENT_TEXT_FONT_SIZE = 12f
+        private var HOURS_TEXT_SIZE = 12f
+        private var AMPM_TEXT_SIZE = 9f
+        private var MIN_HOURS_WIDTH = 96
+        private var MIN_CELL_WIDTH_FOR_TEXT = 20
+        private const val MAX_EVENT_TEXT_LEN = 500
+
+        // smallest height to draw an event with
+        private var MIN_EVENT_HEIGHT = 24.0f // in pixels
+        private var CALENDAR_COLOR_SQUARE_SIZE = 10
+        private var EVENT_RECT_TOP_MARGIN = 1
+        private var EVENT_RECT_BOTTOM_MARGIN = 0
+        private var EVENT_RECT_LEFT_MARGIN = 1
+        private var EVENT_RECT_RIGHT_MARGIN = 0
+        private var EVENT_RECT_STROKE_WIDTH = 2
+        private var EVENT_TEXT_TOP_MARGIN = 2
+        private var EVENT_TEXT_BOTTOM_MARGIN = 2
+        private var EVENT_TEXT_LEFT_MARGIN = 6
+        private var EVENT_TEXT_RIGHT_MARGIN = 6
+        private var ALL_DAY_EVENT_RECT_BOTTOM_MARGIN = 1
+        private var EVENT_ALL_DAY_TEXT_TOP_MARGIN = EVENT_TEXT_TOP_MARGIN
+        private var EVENT_ALL_DAY_TEXT_BOTTOM_MARGIN = EVENT_TEXT_BOTTOM_MARGIN
+        private var EVENT_ALL_DAY_TEXT_LEFT_MARGIN = EVENT_TEXT_LEFT_MARGIN
+        private var EVENT_ALL_DAY_TEXT_RIGHT_MARGIN = EVENT_TEXT_RIGHT_MARGIN
+
+        // margins and sizing for the expand allday icon
+        private var EXPAND_ALL_DAY_BOTTOM_MARGIN = 10
+
+        // sizing for "box +n" in allDay events
+        private var EVENT_SQUARE_WIDTH = 10
+        private var EVENT_LINE_PADDING = 4
+        private var NEW_EVENT_HINT_FONT_SIZE = 12
+        private var mEventTextColor = 0
+        private var mMoreEventsTextColor = 0
+        private var