diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2021-07-14 12:57:07 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-07-14 12:57:07 +0000 |
commit | 6231b6d8967e49a20ede71ac7f573725cbf7997e (patch) | |
tree | 7196e80787af5d50f5212746d863e3c1f67cb549 | |
parent | 20aec4e7b54a1e6f179f11d82645024b9e522815 (diff) | |
parent | 18bfb47118da473facc146c839fb774b3eaa28b2 (diff) | |
download | Calendar-6231b6d8967e49a20ede71ac7f573725cbf7997e.tar.gz |
Merge "AOSP/Calendar - .java files deleted" am: b75e6bd5a4 am: df1e123b8a am: 18bfb47118
Original change: https://android-review.googlesource.com/c/platform/packages/apps/Calendar/+/1764567
Change-Id: I85e044016407d2ab5d31ab830bdfaa46d6e0adf8
43 files changed, 0 insertions, 18007 deletions
diff --git a/src/com/android/calendar/AllInOneActivity.java b/src/com/android/calendar/AllInOneActivity.java deleted file mode 100644 index cec6a40f..00000000 --- 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/AsyncQueryServiceHelper.java b/src/com/android/calendar/AsyncQueryServiceHelper.java deleted file mode 100644 index c6e0a2bc..00000000 --- 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/CalendarApplication.java b/src/com/android/calendar/CalendarApplication.java deleted file mode 100644 index d0ca4698..00000000 --- a/src/com/android/calendar/CalendarApplication.java +++ /dev/null @@ -1,32 +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.app.Application; - -public class CalendarApplication extends Application { - @Override - public void onCreate() { - super.onCreate(); - - /* - * Ensure the default values are set for any receiver, activity, - * service, etc. of Calendar - */ - GeneralPreferences.setDefaultValues(this); - } -} diff --git a/src/com/android/calendar/CalendarBackupAgent.java b/src/com/android/calendar/CalendarBackupAgent.java deleted file mode 100644 index 02456fdc..00000000 --- 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/CalendarController.java b/src/com/android/calendar/CalendarController.java deleted file mode 100644 index 37286f2e..00000000 --- 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/CalendarData.java b/src/com/android/calendar/CalendarData.java deleted file mode 100644 index 5c8456fa..00000000 --- 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/CalendarUtils.java b/src/com/android/calendar/CalendarUtils.java deleted file mode 100644 index 0238c321..00000000 --- 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/CalendarViewAdapter.java b/src/com/android/calendar/CalendarViewAdapter.java deleted file mode 100644 index 524268fc..00000000 --- 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/DayFragment.java b/src/com/android/calendar/DayFragment.java deleted file mode 100644 index a9fb39ed..00000000 --- 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/DayOfMonthDrawable.java b/src/com/android/calendar/DayOfMonthDrawable.java deleted file mode 100644 index 461ab317..00000000 --- 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/DayView.java b/src/com/android/calendar/DayView.java deleted file mode 100644 index 2fc00b3c..00000000 --- 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/Event.java b/src/com/android/calendar/Event.java deleted file mode 100644 index 095e43e7..00000000 --- a/src/com/android/calendar/Event.java +++ /dev/null @@ -1,642 +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.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.Debug; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Instances; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicInteger; - -// TODO: should Event be Parcelable so it can be passed via Intents? -public class Event implements Cloneable { - - private static final String TAG = "CalEvent"; - private static final boolean PROFILE = false; - - /** - * The sort order is: - * 1) events with an earlier start (begin for normal events, startday for allday) - * 2) events with a later end (end for normal events, endday for allday) - * 3) the title (unnecessary, but nice) - * - * The start and end day is sorted first so that all day events are - * sorted correctly with respect to events that are >24 hours (and - * therefore show up in the allday area). - */ - private static final String SORT_EVENTS_BY = - "begin ASC, end DESC, title ASC"; - private static final String SORT_ALLDAY_BY = - "startDay ASC, endDay DESC, title ASC"; - private static final String DISPLAY_AS_ALLDAY = "dispAllday"; - - private static final String EVENTS_WHERE = DISPLAY_AS_ALLDAY + "=0"; - private static final String ALLDAY_WHERE = DISPLAY_AS_ALLDAY + "=1"; - - // The projection to use when querying instances to build a list of events - public static final String[] EVENT_PROJECTION = new String[] { - Instances.TITLE, // 0 - Instances.EVENT_LOCATION, // 1 - Instances.ALL_DAY, // 2 - Instances.DISPLAY_COLOR, // 3 If SDK < 16, set to Instances.CALENDAR_COLOR. - Instances.EVENT_TIMEZONE, // 4 - Instances.EVENT_ID, // 5 - Instances.BEGIN, // 6 - Instances.END, // 7 - Instances._ID, // 8 - Instances.START_DAY, // 9 - Instances.END_DAY, // 10 - Instances.START_MINUTE, // 11 - Instances.END_MINUTE, // 12 - Instances.HAS_ALARM, // 13 - Instances.RRULE, // 14 - Instances.RDATE, // 15 - Instances.SELF_ATTENDEE_STATUS, // 16 - Events.ORGANIZER, // 17 - Events.GUESTS_CAN_MODIFY, // 18 - Instances.ALL_DAY + "=1 OR (" + Instances.END + "-" + Instances.BEGIN + ")>=" - + DateUtils.DAY_IN_MILLIS + " AS " + DISPLAY_AS_ALLDAY, // 19 - }; - - // The indices for the projection array above. - private static final int PROJECTION_TITLE_INDEX = 0; - private static final int PROJECTION_LOCATION_INDEX = 1; - private static final int PROJECTION_ALL_DAY_INDEX = 2; - private static final int PROJECTION_COLOR_INDEX = 3; - private static final int PROJECTION_TIMEZONE_INDEX = 4; - private static final int PROJECTION_EVENT_ID_INDEX = 5; - private static final int PROJECTION_BEGIN_INDEX = 6; - private static final int PROJECTION_END_INDEX = 7; - private static final int PROJECTION_START_DAY_INDEX = 9; - private static final int PROJECTION_END_DAY_INDEX = 10; - private static final int PROJECTION_START_MINUTE_INDEX = 11; - private static final int PROJECTION_END_MINUTE_INDEX = 12; - private static final int PROJECTION_HAS_ALARM_INDEX = 13; - private static final int PROJECTION_RRULE_INDEX = 14; - private static final int PROJECTION_RDATE_INDEX = 15; - private static final int PROJECTION_SELF_ATTENDEE_STATUS_INDEX = 16; - private static final int PROJECTION_ORGANIZER_INDEX = 17; - private static final int PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX = 18; - private static final int PROJECTION_DISPLAY_AS_ALLDAY = 19; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[PROJECTION_COLOR_INDEX] = Instances.CALENDAR_COLOR; - } - } - - private static String mNoTitleString; - private static int mNoColorColor; - - public long id; - public int color; - public CharSequence title; - public CharSequence location; - public boolean allDay; - public String organizer; - public boolean guestsCanModify; - - public int startDay; // start Julian day - public int endDay; // end Julian day - public int startTime; // Start and end time are in minutes since midnight - public int endTime; - - public long startMillis; // UTC milliseconds since the epoch - public long endMillis; // UTC milliseconds since the epoch - private int mColumn; - private int mMaxColumns; - - public boolean hasAlarm; - public boolean isRepeating; - - public int selfAttendeeStatus; - - // The coordinates of the event rectangle drawn on the screen. - public float left; - public float right; - public float top; - public float bottom; - - // These 4 fields are used for navigating among events within the selected - // hour in the Day and Week view. - public Event nextRight; - public Event nextLeft; - public Event nextUp; - public Event nextDown; - - @Override - public final Object clone() throws CloneNotSupportedException { - super.clone(); - Event e = new Event(); - - e.title = title; - e.color = color; - e.location = location; - e.allDay = allDay; - e.startDay = startDay; - e.endDay = endDay; - e.startTime = startTime; - e.endTime = endTime; - e.startMillis = startMillis; - e.endMillis = endMillis; - e.hasAlarm = hasAlarm; - e.isRepeating = isRepeating; - e.selfAttendeeStatus = selfAttendeeStatus; - e.organizer = organizer; - e.guestsCanModify = guestsCanModify; - - return e; - } - - public final void copyTo(Event dest) { - dest.id = id; - dest.title = title; - dest.color = color; - dest.location = location; - dest.allDay = allDay; - dest.startDay = startDay; - dest.endDay = endDay; - dest.startTime = startTime; - dest.endTime = endTime; - dest.startMillis = startMillis; - dest.endMillis = endMillis; - dest.hasAlarm = hasAlarm; - dest.isRepeating = isRepeating; - dest.selfAttendeeStatus = selfAttendeeStatus; - dest.organizer = organizer; - dest.guestsCanModify = guestsCanModify; - } - - public static final Event newInstance() { - Event e = new Event(); - - e.id = 0; - e.title = null; - e.color = 0; - e.location = null; - e.allDay = false; - e.startDay = 0; - e.endDay = 0; - e.startTime = 0; - e.endTime = 0; - e.startMillis = 0; - e.endMillis = 0; - e.hasAlarm = false; - e.isRepeating = false; - e.selfAttendeeStatus = Attendees.ATTENDEE_STATUS_NONE; - - return e; - } - - /** - * Loads <i>days</i> days worth of instances starting at <i>startDay</i>. - */ - public static void loadEvents(Context context, ArrayList<Event> events, int startDay, int days, - int requestId, AtomicInteger sequenceNumber) { - - if (PROFILE) { - Debug.startMethodTracing("loadEvents"); - } - - Cursor cEvents = null; - Cursor cAllday = null; - - events.clear(); - try { - int endDay = startDay + days - 1; - - // We use the byDay instances query to get a list of all events for - // the days we're interested in. - // The sort order is: events with an earlier start time occur - // first and if the start times are the same, then events with - // a later end time occur first. The later end time is ordered - // first so that long rectangles in the calendar views appear on - // the left side. If the start and end times of two events are - // the same then we sort alphabetically on the title. This isn't - // required for correctness, it just adds a nice touch. - - // Respect the preference to show/hide declined events - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - boolean hideDeclined = prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, - false); - - String where = EVENTS_WHERE; - String whereAllday = ALLDAY_WHERE; - if (hideDeclined) { - String hideString = " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" - + Attendees.ATTENDEE_STATUS_DECLINED; - where += hideString; - whereAllday += hideString; - } - - cEvents = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, - endDay, where, null, SORT_EVENTS_BY); - cAllday = instancesQuery(context.getContentResolver(), EVENT_PROJECTION, startDay, - endDay, whereAllday, null, SORT_ALLDAY_BY); - - // Check if we should return early because there are more recent - // load requests waiting. - if (requestId != sequenceNumber.get()) { - return; - } - - buildEventsFromCursor(events, cEvents, context, startDay, endDay); - buildEventsFromCursor(events, cAllday, context, startDay, endDay); - - } finally { - if (cEvents != null) { - cEvents.close(); - } - if (cAllday != null) { - cAllday.close(); - } - if (PROFILE) { - Debug.stopMethodTracing(); - } - } - } - - /** - * Performs a query to return all visible instances in the given range - * that match the given selection. This is a blocking function and - * should not be done on the UI thread. This will cause an expansion of - * recurring events to fill this time range if they are not already - * expanded and will slow down for larger time ranges with many - * recurring events. - * - * @param cr The ContentResolver to use for the query - * @param projection The columns to return - * @param begin The start of the time range to query in UTC millis since - * epoch - * @param end The end of the time range to query in UTC millis since - * epoch - * @param selection Filter on the query as an SQL WHERE statement - * @param selectionArgs Args to replace any '?'s in the selection - * @param orderBy How to order the rows as an SQL ORDER BY statement - * @return A Cursor of instances matching the selection - */ - private static final Cursor instancesQuery(ContentResolver cr, String[] projection, - int startDay, int endDay, String selection, String[] selectionArgs, String orderBy) { - String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=?"; - String[] WHERE_CALENDARS_ARGS = {"1"}; - String DEFAULT_SORT_ORDER = "begin ASC"; - - Uri.Builder builder = Instances.CONTENT_BY_DAY_URI.buildUpon(); - ContentUris.appendId(builder, startDay); - ContentUris.appendId(builder, endDay); - if (TextUtils.isEmpty(selection)) { - selection = WHERE_CALENDARS_SELECTED; - selectionArgs = WHERE_CALENDARS_ARGS; - } else { - selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED; - if (selectionArgs != null && selectionArgs.length > 0) { - selectionArgs = Arrays.copyOf(selectionArgs, selectionArgs.length + 1); - selectionArgs[selectionArgs.length - 1] = WHERE_CALENDARS_ARGS[0]; - } else { - selectionArgs = WHERE_CALENDARS_ARGS; - } - } - return cr.query(builder.build(), projection, selection, selectionArgs, - orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - /** - * Adds all the events from the cursors to the events list. - * - * @param events The list of events - * @param cEvents Events to add to the list - * @param context - * @param startDay - * @param endDay - */ - public static void buildEventsFromCursor( - ArrayList<Event> events, Cursor cEvents, Context context, int startDay, int endDay) { - if (cEvents == null || events == null) { - Log.e(TAG, "buildEventsFromCursor: null cursor or null events list!"); - return; - } - - int count = cEvents.getCount(); - - if (count == 0) { - return; - } - - Resources res = context.getResources(); - mNoTitleString = res.getString(R.string.no_title_label); - mNoColorColor = res.getColor(R.color.event_center); - // Sort events in two passes so we ensure the allday and standard events - // get sorted in the correct order - cEvents.moveToPosition(-1); - while (cEvents.moveToNext()) { - Event e = generateEventFromCursor(cEvents); - if (e.startDay > endDay || e.endDay < startDay) { - continue; - } - events.add(e); - } - } - - /** - * @param cEvents Cursor pointing at event - * @return An event created from the cursor - */ - private static Event generateEventFromCursor(Cursor cEvents) { - Event e = new Event(); - - e.id = cEvents.getLong(PROJECTION_EVENT_ID_INDEX); - e.title = cEvents.getString(PROJECTION_TITLE_INDEX); - e.location = cEvents.getString(PROJECTION_LOCATION_INDEX); - e.allDay = cEvents.getInt(PROJECTION_ALL_DAY_INDEX) != 0; - e.organizer = cEvents.getString(PROJECTION_ORGANIZER_INDEX); - e.guestsCanModify = cEvents.getInt(PROJECTION_GUESTS_CAN_INVITE_OTHERS_INDEX) != 0; - - if (e.title == null || e.title.length() == 0) { - e.title = mNoTitleString; - } - - if (!cEvents.isNull(PROJECTION_COLOR_INDEX)) { - // Read the color from the database - e.color = Utils.getDisplayColorFromColor(cEvents.getInt(PROJECTION_COLOR_INDEX)); - } else { - e.color = mNoColorColor; - } - - long eStart = cEvents.getLong(PROJECTION_BEGIN_INDEX); - long eEnd = cEvents.getLong(PROJECTION_END_INDEX); - - e.startMillis = eStart; - e.startTime = cEvents.getInt(PROJECTION_START_MINUTE_INDEX); - e.startDay = cEvents.getInt(PROJECTION_START_DAY_INDEX); - - e.endMillis = eEnd; - e.endTime = cEvents.getInt(PROJECTION_END_MINUTE_INDEX); - e.endDay = cEvents.getInt(PROJECTION_END_DAY_INDEX); - - e.hasAlarm = cEvents.getInt(PROJECTION_HAS_ALARM_INDEX) != 0; - - // Check if this is a repeating event - String rrule = cEvents.getString(PROJECTION_RRULE_INDEX); - String rdate = cEvents.getString(PROJECTION_RDATE_INDEX); - if (!TextUtils.isEmpty(rrule) || !TextUtils.isEmpty(rdate)) { - e.isRepeating = true; - } else { - e.isRepeating = false; - } - - e.selfAttendeeStatus = cEvents.getInt(PROJECTION_SELF_ATTENDEE_STATUS_INDEX); - return e; - } - - /** - * Computes a position for each event. Each event is displayed - * as a non-overlapping rectangle. For normal events, these rectangles - * are displayed in separate columns in the week view and day view. For - * all-day events, these rectangles are displayed in separate rows along - * the top. In both cases, each event is assigned two numbers: N, and - * Max, that specify that this event is the Nth event of Max number of - * events that are displayed in a group. The width and position of each - * rectangle depend on the maximum number of rectangles that occur at - * the same time. - * - * @param eventsList the list of events, sorted into increasing time order - * @param minimumDurationMillis minimum duration acceptable as cell height of each event - * rectangle in millisecond. Should be 0 when it is not determined. - */ - /* package */ static void computePositions(ArrayList<Event> eventsList, - long minimumDurationMillis) { - if (eventsList == null) { - return; - } - - // Compute the column positions separately for the all-day events - doComputePositions(eventsList, minimumDurationMillis, false); - doComputePositions(eventsList, minimumDurationMillis, true); - } - - private static void doComputePositions(ArrayList<Event> eventsList, - long minimumDurationMillis, boolean doAlldayEvents) { - final ArrayList<Event> activeList = new ArrayList<Event>(); - final ArrayList<Event> groupList = new ArrayList<Event>(); - - if (minimumDurationMillis < 0) { - minimumDurationMillis = 0; - } - - long colMask = 0; - int maxCols = 0; - for (Event event : eventsList) { - // Process all-day events separately - if (event.drawAsAllday() != doAlldayEvents) - continue; - - if (!doAlldayEvents) { - colMask = removeNonAlldayActiveEvents( - event, activeList.iterator(), minimumDurationMillis, colMask); - } else { - colMask = removeAlldayActiveEvents(event, activeList.iterator(), colMask); - } - - // If the active list is empty, then reset the max columns, clear - // the column bit mask, and empty the groupList. - if (activeList.isEmpty()) { - for (Event ev : groupList) { - ev.setMaxColumns(maxCols); - } - maxCols = 0; - colMask = 0; - groupList.clear(); - } - - // Find the first empty column. Empty columns are represented by - // zero bits in the column mask "colMask". - int col = findFirstZeroBit(colMask); - if (col == 64) - col = 63; - colMask |= (1L << col); - event.setColumn(col); - activeList.add(event); - groupList.add(event); - int len = activeList.size(); - if (maxCols < len) - maxCols = len; - } - for (Event ev : groupList) { - ev.setMaxColumns(maxCols); - } - } - - private static long removeAlldayActiveEvents(Event event, Iterator<Event> iter, long colMask) { - // Remove the inactive allday events. An event on the active list - // becomes inactive when the end day is less than the current event's - // start day. - while (iter.hasNext()) { - final Event active = iter.next(); - if (active.endDay < event.startDay) { - colMask &= ~(1L << active.getColumn()); - iter.remove(); - } - } - return colMask; - } - - private static long removeNonAlldayActiveEvents( - Event event, Iterator<Event> iter, long minDurationMillis, long colMask) { - long start = event.getStartMillis(); - // Remove the inactive events. An event on the active list - // becomes inactive when its end time is less than or equal to - // the current event's start time. - while (iter.hasNext()) { - final Event active = iter.next(); - - final long duration = Math.max( - active.getEndMillis() - active.getStartMillis(), minDurationMillis); - if ((active.getStartMillis() + duration) <= start) { - colMask &= ~(1L << active.getColumn()); - iter.remove(); - } - } - return colMask; - } - - public static int findFirstZeroBit(long val) { - for (int ii = 0; ii < 64; ++ii) { - if ((val & (1L << ii)) == 0) - return ii; - } - return 64; - } - - public final void dump() { - Log.e("Cal", "+-----------------------------------------+"); - Log.e("Cal", "+ id = " + id); - Log.e("Cal", "+ color = " + color); - Log.e("Cal", "+ title = " + title); - Log.e("Cal", "+ location = " + location); - Log.e("Cal", "+ allDay = " + allDay); - Log.e("Cal", "+ startDay = " + startDay); - Log.e("Cal", "+ endDay = " + endDay); - Log.e("Cal", "+ startTime = " + startTime); - Log.e("Cal", "+ endTime = " + endTime); - Log.e("Cal", "+ organizer = " + organizer); - Log.e("Cal", "+ guestwrt = " + guestsCanModify); - } - - public final boolean intersects(int julianDay, int startMinute, - int endMinute) { - if (endDay < julianDay) { - return false; - } - - if (startDay > julianDay) { - return false; - } - - if (endDay == julianDay) { - if (endTime < startMinute) { - return false; - } - // An event that ends at the start minute should not be considered - // as intersecting the given time span, but don't exclude - // zero-length (or very short) events. - if (endTime == startMinute - && (startTime != endTime || startDay != endDay)) { - return false; - } - } - - if (startDay == julianDay && startTime > endMinute) { - return false; - } - - return true; - } - - /** - * Returns the event title and location separated by a comma. If the - * location is already part of the title (at the end of the title), then - * just the title is returned. - * - * @return the event title and location as a String - */ - public String getTitleAndLocation() { - String text = title.toString(); - - // Append the location to the title, unless the title ends with the - // location (for example, "meeting in building 42" ends with the - // location). - if (location != null) { - String locationString = location.toString(); - if (!text.endsWith(locationString)) { - text += ", " + locationString; - } - } - return text; - } - - public void setColumn(int column) { - mColumn = column; - } - - public int getColumn() { - return mColumn; - } - - public void setMaxColumns(int maxColumns) { - mMaxColumns = maxColumns; - } - - public int getMaxColumns() { - return mMaxColumns; - } - - public void setStartMillis(long startMillis) { - this.startMillis = startMillis; - } - - public long getStartMillis() { - return startMillis; - } - - public void setEndMillis(long endMillis) { - this.endMillis = endMillis; - } - - public long getEndMillis() { - return endMillis; - } - - public boolean drawAsAllday() { - // Use >= so we'll pick up Exchange allday events - return allDay || endMillis - startMillis >= DateUtils.DAY_IN_MILLIS; - } -} diff --git a/src/com/android/calendar/EventGeometry.java b/src/com/android/calendar/EventGeometry.java deleted file mode 100644 index cdecb49c..00000000 --- a/src/com/android/calendar/EventGeometry.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2008 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.graphics.Rect; - -public class EventGeometry { - // This is the space from the grid line to the event rectangle. - private int mCellMargin = 0; - - private float mMinuteHeight; - - private float mHourGap; - private float mMinEventHeight; - - void setCellMargin(int cellMargin) { - mCellMargin = cellMargin; - } - - public void setHourGap(float gap) { - mHourGap = gap; - } - - public void setMinEventHeight(float height) { - mMinEventHeight = height; - } - - public void setHourHeight(float height) { - mMinuteHeight = height / 60.0f; - } - - // Computes the rectangle coordinates of the given event on the screen. - // Returns true if the rectangle is visible on the screen. - public boolean computeEventRect(int date, int left, int top, int cellWidth, Event event) { - if (event.drawAsAllday()) { - return false; - } - - float cellMinuteHeight = mMinuteHeight; - int startDay = event.startDay; - int endDay = event.endDay; - - if (startDay > date || endDay < date) { - return false; - } - - int startTime = event.startTime; - int endTime = event.endTime; - - // If the event started on a previous day, then show it starting - // at the beginning of this day. - if (startDay < date) { - startTime = 0; - } - - // If the event ends on a future day, then show it extending to - // the end of this day. - if (endDay > date) { - endTime = DayView.MINUTES_PER_DAY; - } - - int col = event.getColumn(); - int maxCols = event.getMaxColumns(); - int startHour = startTime / 60; - int endHour = endTime / 60; - - // If the end point aligns on a cell boundary then count it as - // ending in the previous cell so that we don't cross the border - // between hours. - if (endHour * 60 == endTime) - endHour -= 1; - - event.top = top; - event.top += (int) (startTime * cellMinuteHeight); - event.top += startHour * mHourGap; - - event.bottom = top; - event.bottom += (int) (endTime * cellMinuteHeight); - event.bottom += endHour * mHourGap - 1; - - // Make the rectangle be at least mMinEventHeight pixels high - if (event.bottom < event.top + mMinEventHeight) { - event.bottom = event.top + mMinEventHeight; - } - - float colWidth = (float) (cellWidth - (maxCols + 1) * mCellMargin) / (float) maxCols; - event.left = left + col * (colWidth + mCellMargin); - event.right = event.left + colWidth; - return true; - } - - /** - * Returns true if this event intersects the selection region. - */ - boolean eventIntersectsSelection(Event event, Rect selection) { - if (event.left < selection.right && event.right >= selection.left - && event.top < selection.bottom && event.bottom >= selection.top) { - return true; - } - return false; - } - - /** - * Computes the distance from the given point to the given event. - */ - float pointToEvent(float x, float y, Event event) { - float left = event.left; - float right = event.right; - float top = event.top; - float bottom = event.bottom; - - if (x >= left) { - if (x <= right) { - if (y >= top) { - if (y <= bottom) { - // x,y is inside the event rectangle - return 0f; - } - // x,y is below the event rectangle - return y - bottom; - } - // x,y is above the event rectangle - return top - y; - } - - // x > right - float dx = x - right; - if (y < top) { - // the upper right corner - float dy = top - y; - return (float) Math.sqrt(dx * dx + dy * dy); - } - if (y > bottom) { - // the lower right corner - float dy = y - bottom; - return (float) Math.sqrt(dx * dx + dy * dy); - } - // x,y is to the right of the event rectangle - return dx; - } - // x < left - float dx = left - x; - if (y < top) { - // the upper left corner - float dy = top - y; - return (float) Math.sqrt(dx * dx + dy * dy); - } - if (y > bottom) { - // the lower left corner - float dy = y - bottom; - return (float) Math.sqrt(dx * dx + dy * dy); - } - // x,y is to the left of the event rectangle - return dx; - } -} diff --git a/src/com/android/calendar/EventInfoActivity.java b/src/com/android/calendar/EventInfoActivity.java deleted file mode 100644 index 626e099d..00000000 --- a/src/com/android/calendar/EventInfoActivity.java +++ /dev/null @@ -1,190 +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_BEGIN_TIME; -import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; -import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; - -import android.app.ActionBar; -import android.app.Activity; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.Intent; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Attendees; -import android.util.Log; -import android.widget.Toast; - -import java.util.ArrayList; -import java.util.List; - -public class EventInfoActivity extends Activity { - private static final String TAG = "EventInfoActivity"; - private EventInfoFragment mInfoFragment; - private long mStartMillis, mEndMillis; - private long mEventId; - - // 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 false; - } - - @Override - public void onChange(boolean selfChange) { - if (selfChange) return; - if (mInfoFragment != null) { - mInfoFragment.reloadEvents(); - } - } - }; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - // Get the info needed for the fragment - Intent intent = getIntent(); - int attendeeResponse = 0; - mEventId = -1; - boolean isDialog = false; - - if (icicle != null) { - mEventId = icicle.getLong(EventInfoFragment.BUNDLE_KEY_EVENT_ID); - mStartMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_START_MILLIS); - mEndMillis = icicle.getLong(EventInfoFragment.BUNDLE_KEY_END_MILLIS); - attendeeResponse = icicle.getInt(EventInfoFragment.BUNDLE_KEY_ATTENDEE_RESPONSE); - isDialog = icicle.getBoolean(EventInfoFragment.BUNDLE_KEY_IS_DIALOG); - } else if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) { - mStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0); - mEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0); - attendeeResponse = intent.getIntExtra(ATTENDEE_STATUS, - Attendees.ATTENDEE_STATUS_NONE); - Uri data = intent.getData(); - if (data != null) { - try { - List<String> pathSegments = data.getPathSegments(); - int size = pathSegments.size(); - if (size > 2 && "EventTime".equals(pathSegments.get(2))) { - // Support non-standard VIEW intent format: - //dat = content://com.android.calendar/events/[id]/EventTime/[start]/[end] - mEventId = Long.parseLong(pathSegments.get(1)); - if (size > 4) { - mStartMillis = Long.parseLong(pathSegments.get(3)); - mEndMillis = Long.parseLong(pathSegments.get(4)); - } - } else { - mEventId = Long.parseLong(data.getLastPathSegment()); - } - } catch (NumberFormatException e) { - if (mEventId == -1) { - // do nothing here , deal with it later - } else if (mStartMillis == 0 || mEndMillis ==0) { - // Parsing failed on the start or end time , make sure the times were not - // pulled from the intent's extras and reset them. - mStartMillis = 0; - mEndMillis = 0; - } - } - } - } - - if (mEventId == -1) { - Log.w(TAG, "No event id"); - Toast.makeText(this, R.string.event_not_found, Toast.LENGTH_SHORT).show(); - finish(); - } - - // If we do not support showing full screen event info in this configuration, - // close the activity and show the event in AllInOne. - Resources res = getResources(); - if (!res.getBoolean(R.bool.agenda_show_event_info_full_screen) - && !res.getBoolean(R.bool.show_event_info_full_screen)) { - CalendarController.getInstance(this) - .launchViewEvent(mEventId, mStartMillis, mEndMillis, attendeeResponse); - finish(); - return; - } - - setContentView(R.layout.simple_frame_layout); - - // Get the fragment if exists - mInfoFragment = (EventInfoFragment) - getFragmentManager().findFragmentById(R.id.main_frame); - - - // Remove the application title - ActionBar bar = getActionBar(); - if (bar != null) { - bar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_HOME); - } - - // Create a new fragment if none exists - if (mInfoFragment == null) { - FragmentManager fragmentManager = getFragmentManager(); - FragmentTransaction ft = fragmentManager.beginTransaction(); - mInfoFragment = new EventInfoFragment(this, mEventId, mStartMillis, mEndMillis, - attendeeResponse, isDialog, (isDialog ? - EventInfoFragment.DIALOG_WINDOW_STYLE : - EventInfoFragment.FULL_WINDOW_STYLE)); - ft.replace(R.id.main_frame, mInfoFragment); - ft.commit(); - } - } - - @Override - protected void onNewIntent(Intent intent) { - // From the Android Dev Guide: "It's important to note that when - // onNewIntent(Intent) is called, the Activity has not been restarted, - // so the getIntent() method will still return the Intent that was first - // received with onCreate(). This is why setIntent(Intent) is called - // inside onNewIntent(Intent) (just in case you call getIntent() at a - // later time)." - setIntent(intent); - } - - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - } - - @Override - protected void onResume() { - super.onResume(); - getContentResolver().registerContentObserver(CalendarContract.Events.CONTENT_URI, - true, mObserver); - } - - @Override - protected void onPause() { - super.onPause(); - getContentResolver().unregisterContentObserver(mObserver); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - } -} diff --git a/src/com/android/calendar/EventInfoFragment.java b/src/com/android/calendar/EventInfoFragment.java deleted file mode 100644 index 0aa83d02..00000000 --- a/src/com/android/calendar/EventInfoFragment.java +++ /dev/null @@ -1,877 +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 com.android.calendar.CalendarController.EVENT_EDIT_ON_LAUNCH; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.app.Activity; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.FragmentManager; -import android.app.Service; -import android.content.ActivityNotFoundException; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.Color; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Bundle; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Reminders; -import android.provider.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds; -import android.provider.ContactsContract.Intents; -import android.provider.ContactsContract.QuickContact; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.format.Time; -import android.text.method.LinkMovementMethod; -import android.text.method.MovementMethod; -import android.text.style.ForegroundColorSpan; -import android.text.util.Rfc822Token; -import android.util.Log; -import android.util.SparseIntArray; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.RadioGroup.OnCheckedChangeListener; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.alerts.QuickResponseActivity; -import com.android.calendarcommon2.DateException; -import com.android.calendarcommon2.Duration; -import com.android.calendarcommon2.EventRecurrence; -import com.android.colorpicker.HsvColorComparator; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public class EventInfoFragment extends DialogFragment implements OnCheckedChangeListener, - CalendarController.EventHandler, OnClickListener { - - public static final boolean DEBUG = false; - - public static final String TAG = "EventInfoFragment"; - - protected static final String BUNDLE_KEY_EVENT_ID = "key_event_id"; - protected static final String BUNDLE_KEY_START_MILLIS = "key_start_millis"; - protected static final String BUNDLE_KEY_END_MILLIS = "key_end_millis"; - protected static final String BUNDLE_KEY_IS_DIALOG = "key_fragment_is_dialog"; - protected static final String BUNDLE_KEY_DELETE_DIALOG_VISIBLE = "key_delete_dialog_visible"; - protected static final String BUNDLE_KEY_WINDOW_STYLE = "key_window_style"; - protected static final String BUNDLE_KEY_CALENDAR_COLOR = "key_calendar_color"; - protected static final String BUNDLE_KEY_CALENDAR_COLOR_INIT = "key_calendar_color_init"; - protected static final String BUNDLE_KEY_CURRENT_COLOR = "key_current_color"; - protected static final String BUNDLE_KEY_CURRENT_COLOR_KEY = "key_current_color_key"; - protected static final String BUNDLE_KEY_CURRENT_COLOR_INIT = "key_current_color_init"; - protected static final String BUNDLE_KEY_ORIGINAL_COLOR = "key_original_color"; - protected static final String BUNDLE_KEY_ORIGINAL_COLOR_INIT = "key_original_color_init"; - protected static final String BUNDLE_KEY_ATTENDEE_RESPONSE = "key_attendee_response"; - protected static final String BUNDLE_KEY_USER_SET_ATTENDEE_RESPONSE = - "key_user_set_attendee_response"; - protected static final String BUNDLE_KEY_TENTATIVE_USER_RESPONSE = - "key_tentative_user_response"; - protected static final String BUNDLE_KEY_RESPONSE_WHICH_EVENTS = "key_response_which_events"; - protected static final String BUNDLE_KEY_REMINDER_MINUTES = "key_reminder_minutes"; - protected static final String BUNDLE_KEY_REMINDER_METHODS = "key_reminder_methods"; - - - private static final String PERIOD_SPACE = ". "; - - private static final String NO_EVENT_COLOR = ""; - - /** - * These are the corresponding indices into the array of strings - * "R.array.change_response_labels" in the resource file. - */ - static final int UPDATE_SINGLE = 0; - static final int UPDATE_ALL = 1; - - // Style of view - public static final int FULL_WINDOW_STYLE = 0; - public static final int DIALOG_WINDOW_STYLE = 1; - - private int mWindowStyle = DIALOG_WINDOW_STYLE; - - // Query tokens for QueryHandler - private static final int TOKEN_QUERY_EVENT = 1 << 0; - private static final int TOKEN_QUERY_CALENDARS = 1 << 1; - private static final int TOKEN_QUERY_ATTENDEES = 1 << 2; - private static final int TOKEN_QUERY_DUPLICATE_CALENDARS = 1 << 3; - private static final int TOKEN_QUERY_REMINDERS = 1 << 4; - private static final int TOKEN_QUERY_VISIBLE_CALENDARS = 1 << 5; - private static final int TOKEN_QUERY_COLORS = 1 << 6; - - private static final int TOKEN_QUERY_ALL = TOKEN_QUERY_DUPLICATE_CALENDARS - | TOKEN_QUERY_ATTENDEES | TOKEN_QUERY_CALENDARS | TOKEN_QUERY_EVENT - | TOKEN_QUERY_REMINDERS | TOKEN_QUERY_VISIBLE_CALENDARS | TOKEN_QUERY_COLORS; - - private int mCurrentQuery = 0; - - private static final String[] EVENT_PROJECTION = new String[] { - Events._ID, // 0 do not remove; used in DeleteEventHelper - Events.TITLE, // 1 do not remove; used in DeleteEventHelper - Events.RRULE, // 2 do not remove; used in DeleteEventHelper - Events.ALL_DAY, // 3 do not remove; used in DeleteEventHelper - Events.CALENDAR_ID, // 4 do not remove; used in DeleteEventHelper - Events.DTSTART, // 5 do not remove; used in DeleteEventHelper - Events._SYNC_ID, // 6 do not remove; used in DeleteEventHelper - Events.EVENT_TIMEZONE, // 7 do not remove; used in DeleteEventHelper - Events.DESCRIPTION, // 8 - Events.EVENT_LOCATION, // 9 - Calendars.CALENDAR_ACCESS_LEVEL, // 10 - Events.CALENDAR_COLOR, // 11 - Events.EVENT_COLOR, // 12 - Events.HAS_ATTENDEE_DATA, // 13 - Events.ORGANIZER, // 14 - Events.HAS_ALARM, // 15 - Calendars.MAX_REMINDERS, // 16 - Calendars.ALLOWED_REMINDERS, // 17 - Events.CUSTOM_APP_PACKAGE, // 18 - Events.CUSTOM_APP_URI, // 19 - Events.DTEND, // 20 - Events.DURATION, // 21 - Events.ORIGINAL_SYNC_ID // 22 do not remove; used in DeleteEventHelper - }; - private static final int EVENT_INDEX_ID = 0; - private static final int EVENT_INDEX_TITLE = 1; - private static final int EVENT_INDEX_RRULE = 2; - private static final int EVENT_INDEX_ALL_DAY = 3; - private static final int EVENT_INDEX_CALENDAR_ID = 4; - private static final int EVENT_INDEX_DTSTART = 5; - private static final int EVENT_INDEX_SYNC_ID = 6; - private static final int EVENT_INDEX_EVENT_TIMEZONE = 7; - private static final int EVENT_INDEX_DESCRIPTION = 8; - private static final int EVENT_INDEX_EVENT_LOCATION = 9; - private static final int EVENT_INDEX_ACCESS_LEVEL = 10; - private static final int EVENT_INDEX_CALENDAR_COLOR = 11; - private static final int EVENT_INDEX_EVENT_COLOR = 12; - private static final int EVENT_INDEX_HAS_ATTENDEE_DATA = 13; - private static final int EVENT_INDEX_ORGANIZER = 14; - private static final int EVENT_INDEX_HAS_ALARM = 15; - private static final int EVENT_INDEX_MAX_REMINDERS = 16; - private static final int EVENT_INDEX_ALLOWED_REMINDERS = 17; - private static final int EVENT_INDEX_CUSTOM_APP_PACKAGE = 18; - private static final int EVENT_INDEX_CUSTOM_APP_URI = 19; - private static final int EVENT_INDEX_DTEND = 20; - private static final int EVENT_INDEX_DURATION = 21; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_PACKAGE] = Events._ID; // nonessential value - EVENT_PROJECTION[EVENT_INDEX_CUSTOM_APP_URI] = Events._ID; // nonessential value - } - } - - static final String[] CALENDARS_PROJECTION = new String[] { - Calendars._ID, // 0 - Calendars.CALENDAR_DISPLAY_NAME, // 1 - Calendars.OWNER_ACCOUNT, // 2 - Calendars.CAN_ORGANIZER_RESPOND, // 3 - Calendars.ACCOUNT_NAME, // 4 - Calendars.ACCOUNT_TYPE // 5 - }; - static final int CALENDARS_INDEX_DISPLAY_NAME = 1; - static final int CALENDARS_INDEX_OWNER_ACCOUNT = 2; - static final int CALENDARS_INDEX_OWNER_CAN_RESPOND = 3; - static final int CALENDARS_INDEX_ACCOUNT_NAME = 4; - static final int CALENDARS_INDEX_ACCOUNT_TYPE = 5; - - static final String CALENDARS_WHERE = Calendars._ID + "=?"; - static final String CALENDARS_DUPLICATE_NAME_WHERE = Calendars.CALENDAR_DISPLAY_NAME + "=?"; - static final String CALENDARS_VISIBLE_WHERE = Calendars.VISIBLE + "=?"; - - public static final int COLORS_INDEX_COLOR = 1; - public static final int COLORS_INDEX_COLOR_KEY = 2; - - private View mView; - - private Uri mUri; - private long mEventId; - private Cursor mEventCursor; - private Cursor mCalendarsCursor; - - private static float mScale = 0; // Used for supporting different screen densities - - private static int mCustomAppIconSize = 32; - - private long mStartMillis; - private long mEndMillis; - private boolean mAllDay; - - private boolean mOwnerCanRespond; - private String mSyncAccountName; - private String mCalendarOwnerAccount; - private boolean mIsBusyFreeCalendar; - - private int mOriginalAttendeeResponse; - private int mAttendeeResponseFromIntent = Attendees.ATTENDEE_STATUS_NONE; - private int mUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; - private int mWhichEvents = -1; - // Used as the temporary response until the dialog is confirmed. It is also - // able to be used as a state marker for configuration changes. - private int mTentativeUserSetResponse = Attendees.ATTENDEE_STATUS_NONE; - private boolean mHasAlarm; - // Used to prevent saving changes in event if it is being deleted. - private boolean mEventDeletionStarted = false; - - private TextView mTitle; - private TextView mWhenDateTime; - private TextView mWhere; - private Menu mMenu = null; - private View mHeadlines; - private ScrollView mScrollView; - private View mLoadingMsgView; - private View mErrorMsgView; - private ObjectAnimator mAnimateAlpha; - private long mLoadingMsgStartTime; - - private SparseIntArray mDisplayColorKeyMap = new SparseIntArray(); - private int mOriginalColor = -1; - private boolean mOriginalColorInitialized = false; - private int mCalendarColor = -1; - private boolean mCalendarColorInitialized = false; - private int mCurrentColor = -1; - private boolean mCurrentColorInitialized = false; - private int mCurrentColorKey = -1; - - private static final int FADE_IN_TIME = 300; // in milliseconds - private static final int LOADING_MSG_DELAY = 600; // in milliseconds - private static final int LOADING_MSG_MIN_DISPLAY_TIME = 600; - private boolean mNoCrossFade = false; // Used to prevent repeated cross-fade - private RadioGroup mResponseRadioGroup; - - ArrayList<String> mToEmails = new ArrayList<String>(); - ArrayList<String> mCcEmails = new ArrayList<String>(); - - - private final Runnable mTZUpdater = new Runnable() { - @Override - public void run() { - updateEvent(mView); - } - }; - - private final Runnable mLoadingMsgAlphaUpdater = new Runnable() { - @Override - public void run() { - // Since this is run after a delay, make sure to only show the message - // if the event's data is not shown yet. - if (!mAnimateAlpha.isRunning() && mScrollView.getAlpha() == 0) { - mLoadingMsgStartTime = System.currentTimeMillis(); - mLoadingMsgView.setAlpha(1); - } - } - }; - - private static int mDialogWidth = 500; - private static int mDialogHeight = 600; - private static int DIALOG_TOP_MARGIN = 8; - private boolean mIsDialog = false; - private boolean mIsPaused = true; - private boolean mDismissOnResume = false; - private int mX = -1; - private int mY = -1; - private int mMinTop; // Dialog cannot be above this location - private boolean mIsTabletConfig; - private Activity mActivity; - private Context mContext; - - private CalendarController mController; - - private void sendAccessibilityEventIfQueryDone(int token) { - mCurrentQuery |= token; - if (mCurrentQuery == TOKEN_QUERY_ALL) { - sendAccessibilityEvent(); - } - } - - public EventInfoFragment(Context context, Uri uri, long startMillis, long endMillis, - int attendeeResponse, boolean isDialog, int windowStyle) { - - Resources r = context.getResources(); - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - mCustomAppIconSize *= mScale; - if (isDialog) { - DIALOG_TOP_MARGIN *= mScale; - } - } - } - if (isDialog) { - setDialogSize(r); - } - mIsDialog = isDialog; - - setStyle(DialogFragment.STYLE_NO_TITLE, 0); - mUri = uri; - mStartMillis = startMillis; - mEndMillis = endMillis; - mAttendeeResponseFromIntent = attendeeResponse; - mWindowStyle = windowStyle; - } - - // This is currently required by the fragment manager. - public EventInfoFragment() { - } - - public EventInfoFragment(Context context, long eventId, long startMillis, long endMillis, - int attendeeResponse, boolean isDialog, int windowStyle) { - this(context, ContentUris.withAppendedId(Events.CONTENT_URI, eventId), startMillis, - endMillis, attendeeResponse, isDialog, windowStyle); - mEventId = eventId; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - if (mIsDialog) { - applyDialogParams(); - } - - final Activity activity = getActivity(); - mContext = activity; - } - - private void applyDialogParams() { - Dialog dialog = getDialog(); - dialog.setCanceledOnTouchOutside(true); - - Window window = dialog.getWindow(); - window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); - - WindowManager.LayoutParams a = window.getAttributes(); - a.dimAmount = .4f; - - a.width = mDialogWidth; - a.height = mDialogHeight; - - - // On tablets , do smart positioning of dialog - // On phones , use the whole screen - - if (mX != -1 || mY != -1) { - a.x = mX - mDialogWidth / 2; - a.y = mY - mDialogHeight / 2; - if (a.y < mMinTop) { - a.y = mMinTop + DIALOG_TOP_MARGIN; - } - a.gravity = Gravity.LEFT | Gravity.TOP; - } - window.setAttributes(a); - } - - public void setDialogParams(int x, int y, int minTop) { - mX = x; - mY = y; - mMinTop = minTop; - } - - // Implements OnCheckedChangeListener - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - } - - public void onNothingSelected(AdapterView<?> parent) { - } - - @Override - public void onDetach() { - super.onDetach(); - mController.deregisterEventHandler(R.layout.event_info); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mActivity = activity; - // Ensure that mIsTabletConfig is set before creating the menu. - mIsTabletConfig = Utils.getConfigBool(mActivity, R.bool.tablet_config); - mController = CalendarController.getInstance(mActivity); - mController.registerEventHandler(R.layout.event_info, this); - - if (!mIsDialog) { - setHasOptionsMenu(true); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - if (mWindowStyle == DIALOG_WINDOW_STYLE) { - mView = inflater.inflate(R.layout.event_info_dialog, container, false); - } else { - mView = inflater.inflate(R.layout.event_info, container, false); - } - mScrollView = (ScrollView) mView.findViewById(R.id.event_info_scroll_view); - mLoadingMsgView = mView.findViewById(R.id.event_info_loading_msg); - mErrorMsgView = mView.findViewById(R.id.event_info_error_msg); - mTitle = (TextView) mView.findViewById(R.id.title); - mWhenDateTime = (TextView) mView.findViewById(R.id.when_datetime); - mWhere = (TextView) mView.findViewById(R.id.where); - mHeadlines = mView.findViewById(R.id.event_info_headline); - - mResponseRadioGroup = (RadioGroup) mView.findViewById(R.id.response_value); - - mAnimateAlpha = ObjectAnimator.ofFloat(mScrollView, "Alpha", 0, 1); - mAnimateAlpha.setDuration(FADE_IN_TIME); - mAnimateAlpha.addListener(new AnimatorListenerAdapter() { - int defLayerType; - - @Override - public void onAnimationStart(Animator animation) { - // Use hardware layer for better performance during animation - defLayerType = mScrollView.getLayerType(); - mScrollView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - // Ensure that the loading message is gone before showing the - // event info - mLoadingMsgView.removeCallbacks(mLoadingMsgAlphaUpdater); - mLoadingMsgView.setVisibility(View.GONE); - } - - @Override - public void onAnimationCancel(Animator animation) { - mScrollView.setLayerType(defLayerType, null); - } - - @Override - public void onAnimationEnd(Animator animation) { - mScrollView.setLayerType(defLayerType, null); - // Do not cross fade after the first time - mNoCrossFade = true; - } - }); - - mLoadingMsgView.setAlpha(0); - mScrollView.setAlpha(0); - mErrorMsgView.setVisibility(View.INVISIBLE); - mLoadingMsgView.postDelayed(mLoadingMsgAlphaUpdater, LOADING_MSG_DELAY); - - // Hide Edit/Delete buttons if in full screen mode on a phone - if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { - mView.findViewById(R.id.event_info_buttons_container).setVisibility(View.GONE); - } - - return mView; - } - - private void updateTitle() { - Resources res = getActivity().getResources(); - getActivity().setTitle(res.getString(R.string.event_info_title)); - } - - /** - * Initializes the event cursor, which is expected to point to the first - * (and only) result from a query. - * @return false if the cursor is empty, true otherwise - */ - private boolean initEventCursor() { - if ((mEventCursor == null) || (mEventCursor.getCount() == 0)) { - return false; - } - mEventCursor.moveToFirst(); - mEventId = mEventCursor.getInt(EVENT_INDEX_ID); - String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); - // mHasAlarm will be true if it was saved in the event already. - mHasAlarm = (mEventCursor.getInt(EVENT_INDEX_HAS_ALARM) == 1)? true : false; - return true; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - // Show color/edit/delete buttons only in non-dialog configuration - if (!mIsDialog && !mIsTabletConfig || mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) { - inflater.inflate(R.menu.event_info_title_bar, menu); - mMenu = menu; - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - // If we're a dialog we don't want to handle menu buttons - if (mIsDialog) { - return false; - } - // Handles option menu selections: - // Home button - close event info activity and start the main calendar - // one - // Edit button - start the event edit activity and close the info - // activity - // Delete button - start a delete query that calls a runnable that close - // the info activity - - final int itemId = item.getItemId(); - if (itemId == android.R.id.home) { - Utils.returnToCalendarHome(mContext); - mActivity.finish(); - return true; - } else if (itemId == R.id.info_action_edit) { - mActivity.finish(); - } - return super.onOptionsItemSelected(item); - } - - @Override - public void onStop() { - super.onStop(); - } - - @Override - public void onDestroy() { - if (mEventCursor != null) { - mEventCursor.close(); - } - if (mCalendarsCursor != null) { - mCalendarsCursor.close(); - } - super.onDestroy(); - } - - /** - * Creates an exception to a recurring event. The only change we're making is to the - * "self attendee status" value. The provider will take care of updating the corresponding - * Attendees.attendeeStatus entry. - * - * @param eventId The recurring event. - * @param status The new value for selfAttendeeStatus. - */ - private void createExceptionResponse(long eventId, int status) { - ContentValues values = new ContentValues(); - values.put(Events.ORIGINAL_INSTANCE_TIME, mStartMillis); - values.put(Events.SELF_ATTENDEE_STATUS, status); - values.put(Events.STATUS, Events.STATUS_CONFIRMED); - - ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); - Uri exceptionUri = Uri.withAppendedPath(Events.CONTENT_EXCEPTION_URI, - String.valueOf(eventId)); - ops.add(ContentProviderOperation.newInsert(exceptionUri).withValues(values).build()); - } - - public static int getResponseFromButtonId(int buttonId) { - return Attendees.ATTENDEE_STATUS_NONE; - } - - public static int findButtonIdForResponse(int response) { - return -1; - } - - private void displayEventNotFound() { - mErrorMsgView.setVisibility(View.VISIBLE); - mScrollView.setVisibility(View.GONE); - mLoadingMsgView.setVisibility(View.GONE); - } - - private void updateEvent(View view) { - if (mEventCursor == null || view == null) { - return; - } - - Context context = view.getContext(); - if (context == null) { - return; - } - - String eventName = mEventCursor.getString(EVENT_INDEX_TITLE); - if (eventName == null || eventName.length() == 0) { - eventName = getActivity().getString(R.string.no_title_label); - } - - // 3rd parties might not have specified the start/end time when firing the - // Events.CONTENT_URI intent. Update these with values read from the db. - if (mStartMillis == 0 && mEndMillis == 0) { - mStartMillis = mEventCursor.getLong(EVENT_INDEX_DTSTART); - mEndMillis = mEventCursor.getLong(EVENT_INDEX_DTEND); - if (mEndMillis == 0) { - String duration = mEventCursor.getString(EVENT_INDEX_DURATION); - if (!TextUtils.isEmpty(duration)) { - try { - Duration d = new Duration(); - d.parse(duration); - long endMillis = mStartMillis + d.getMillis(); - if (endMillis >= mStartMillis) { - mEndMillis = endMillis; - } else { - Log.d(TAG, "Invalid duration string: " + duration); - } - } catch (DateException e) { - Log.d(TAG, "Error parsing duration string " + duration, e); - } - } - if (mEndMillis == 0) { - mEndMillis = mStartMillis; - } - } - } - - mAllDay = mEventCursor.getInt(EVENT_INDEX_ALL_DAY) != 0; - String location = mEventCursor.getString(EVENT_INDEX_EVENT_LOCATION); - String description = mEventCursor.getString(EVENT_INDEX_DESCRIPTION); - String rRule = mEventCursor.getString(EVENT_INDEX_RRULE); - String eventTimezone = mEventCursor.getString(EVENT_INDEX_EVENT_TIMEZONE); - - mHeadlines.setBackgroundColor(mCurrentColor); - - // What - if (eventName != null) { - setTextCommon(view, R.id.title, eventName); - } - - // When - // Set the date and repeats (if any) - String localTimezone = Utils.getTimeZone(mActivity, mTZUpdater); - - Resources resources = context.getResources(); - String displayedDatetime = Utils.getDisplayedDatetime(mStartMillis, mEndMillis, - System.currentTimeMillis(), localTimezone, mAllDay, context); - - String displayedTimezone = null; - if (!mAllDay) { - displayedTimezone = Utils.getDisplayedTimezone(mStartMillis, localTimezone, - eventTimezone); - } - // Display the datetime. Make the timezone (if any) transparent. - if (displayedTimezone == null) { - setTextCommon(view, R.id.when_datetime, displayedDatetime); - } else { - int timezoneIndex = displayedDatetime.length(); - displayedDatetime += " " + displayedTimezone; - SpannableStringBuilder sb = new SpannableStringBuilder(displayedDatetime); - ForegroundColorSpan transparentColorSpan = new ForegroundColorSpan( - resources.getColor(R.color.event_info_headline_transparent_color)); - sb.setSpan(transparentColorSpan, timezoneIndex, displayedDatetime.length(), - Spannable.SPAN_INCLUSIVE_INCLUSIVE); - setTextCommon(view, R.id.when_datetime, sb); - } - - view.findViewById(R.id.when_repeat).setVisibility(View.GONE); - - // Organizer view is setup in the updateCalendar method - - - // Where - if (location == null || location.trim().length() == 0) { - setVisibilityCommon(view, R.id.where, View.GONE); - } else { - final TextView textView = mWhere; - if (textView != null) { - textView.setText(location.trim()); - } - } - - // Launch Custom App - if (Utils.isJellybeanOrLater()) { - updateCustomAppButton(); - } - } - - private void updateCustomAppButton() { - setVisibilityCommon(mView, R.id.launch_custom_app_container, View.GONE); - return; - } - - private void sendAccessibilityEvent() { - AccessibilityManager am = - (AccessibilityManager) getActivity().getSystemService(Service.ACCESSIBILITY_SERVICE); - if (!am.isEnabled()) { - return; - } - - AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); - event.setClassName(EventInfoFragment.class.getName()); - event.setPackageName(getActivity().getPackageName()); - List<CharSequence> text = event.getText(); - - if (mResponseRadioGroup.getVisibility() == View.VISIBLE) { - int id = mResponseRadioGroup.getCheckedRadioButtonId(); - if (id != View.NO_ID) { - text.add(((TextView) getView().findViewById(R.id.response_label)).getText()); - text.add((((RadioButton) (mResponseRadioGroup.findViewById(id))) - .getText() + PERIOD_SPACE)); - } - } - - am.sendAccessibilityEvent(event); - } - - private void updateCalendar(View view) { - - mCalendarOwnerAccount = ""; - if (mCalendarsCursor != null && mEventCursor != null) { - mCalendarsCursor.moveToFirst(); - String tempAccount = mCalendarsCursor.getString(CALENDARS_INDEX_OWNER_ACCOUNT); - mCalendarOwnerAccount = (tempAccount == null) ? "" : tempAccount; - mOwnerCanRespond = mCalendarsCursor.getInt(CALENDARS_INDEX_OWNER_CAN_RESPOND) != 0; - mSyncAccountName = mCalendarsCursor.getString(CALENDARS_INDEX_ACCOUNT_NAME); - - setVisibilityCommon(view, R.id.organizer_container, View.GONE); - mIsBusyFreeCalendar = - mEventCursor.getInt(EVENT_INDEX_ACCESS_LEVEL) == Calendars.CAL_ACCESS_FREEBUSY; - - if (!mIsBusyFreeCalendar) { - - View b = mView.findViewById(R.id.edit); - b.setEnabled(true); - b.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - // For dialogs, just close the fragment - // For full screen, close activity on phone, leave it for tablet - if (mIsDialog) { - EventInfoFragment.this.dismiss(); - } - else if (!mIsTabletConfig){ - getActivity().finish(); - } - } - }); - } - View button; - if ((!mIsDialog && !mIsTabletConfig || - mWindowStyle == EventInfoFragment.FULL_WINDOW_STYLE) && mMenu != null) { - mActivity.invalidateOptionsMenu(); - } - } else { - setVisibilityCommon(view, R.id.calendar, View.GONE); - sendAccessibilityEventIfQueryDone(TOKEN_QUERY_DUPLICATE_CALENDARS); - } - } - - private void setTextCommon(View view, int id, CharSequence text) { - TextView textView = (TextView) view.findViewById(id); - if (textView == null) - return; - textView.setText(text); - } - - private void setVisibilityCommon(View view, int id, int visibility) { - View v = view.findViewById(id); - if (v != null) { - v.setVisibility(visibility); - } - return; - } - - @Override - public void onPause() { - mIsPaused = true; - super.onPause(); - } - - @Override - public void onResume() { - super.onResume(); - if (mIsDialog) { - setDialogSize(getActivity().getResources()); - applyDialogParams(); - } - mIsPaused = false; - if (mTentativeUserSetResponse != Attendees.ATTENDEE_STATUS_NONE) { - int buttonId = findButtonIdForResponse(mTentativeUserSetResponse); - mResponseRadioGroup.check(buttonId); - } - } - - @Override - public void eventsChanged() { - } - - @Override - public long getSupportedEventTypes() { - return EventType.EVENTS_CHANGED; - } - - @Override - public void handleEvent(EventInfo event) { - reloadEvents(); - } - - public void reloadEvents() { - } - - @Override - public void onClick(View view) { - } - - public long getEventId() { - return mEventId; - } - - public long getStartMillis() { - return mStartMillis; - } - public long getEndMillis() { - return mEndMillis; - } - private void setDialogSize(Resources r) { - mDialogWidth = (int)r.getDimension(R.dimen.event_info_dialog_width); - mDialogHeight = (int)r.getDimension(R.dimen.event_info_dialog_height); - } -} diff --git a/src/com/android/calendar/EventLoader.java b/src/com/android/calendar/EventLoader.java deleted file mode 100644 index d34b1c7c..00000000 --- a/src/com/android/calendar/EventLoader.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) 2008 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.ContentResolver; -import android.content.Context; -import android.database.Cursor; -import android.os.Handler; -import android.os.Process; -import android.provider.CalendarContract; -import android.provider.CalendarContract.EventDays; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; - -public class EventLoader { - - private Context mContext; - private Handler mHandler = new Handler(); - private AtomicInteger mSequenceNumber = new AtomicInteger(); - - private LinkedBlockingQueue<LoadRequest> mLoaderQueue; - private LoaderThread mLoaderThread; - private ContentResolver mResolver; - - private static interface LoadRequest { - public void processRequest(EventLoader eventLoader); - public void skipRequest(EventLoader eventLoader); - } - - private static class ShutdownRequest implements LoadRequest { - public void processRequest(EventLoader eventLoader) { - } - - public void skipRequest(EventLoader eventLoader) { - } - } - - /** - * - * Code for handling requests to get whether days have an event or not - * and filling in the eventDays array. - * - */ - private static class LoadEventDaysRequest implements LoadRequest { - public int startDay; - public int numDays; - public boolean[] eventDays; - public Runnable uiCallback; - - /** - * The projection used by the EventDays query. - */ - private static final String[] PROJECTION = { - CalendarContract.EventDays.STARTDAY, CalendarContract.EventDays.ENDDAY - }; - - public LoadEventDaysRequest(int startDay, int numDays, boolean[] eventDays, - final Runnable uiCallback) - { - this.startDay = startDay; - this.numDays = numDays; - this.eventDays = eventDays; - this.uiCallback = uiCallback; - } - - @Override - public void processRequest(EventLoader eventLoader) - { - final Handler handler = eventLoader.mHandler; - ContentResolver cr = eventLoader.mResolver; - - // Clear the event days - Arrays.fill(eventDays, false); - - //query which days have events - Cursor cursor = EventDays.query(cr, startDay, numDays, PROJECTION); - try { - int startDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.STARTDAY); - int endDayColumnIndex = cursor.getColumnIndexOrThrow(EventDays.ENDDAY); - - //Set all the days with events to true - while (cursor.moveToNext()) { - int firstDay = cursor.getInt(startDayColumnIndex); - int lastDay = cursor.getInt(endDayColumnIndex); - //we want the entire range the event occurs, but only within the month - int firstIndex = Math.max(firstDay - startDay, 0); - int lastIndex = Math.min(lastDay - startDay, 30); - - for(int i = firstIndex; i <= lastIndex; i++) { - eventDays[i] = true; - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - handler.post(uiCallback); - } - - @Override - public void skipRequest(EventLoader eventLoader) { - } - } - - private static class LoadEventsRequest implements LoadRequest { - - public int id; - public int startDay; - public int numDays; - public ArrayList<Event> events; - public Runnable successCallback; - public Runnable cancelCallback; - - public LoadEventsRequest(int id, int startDay, int numDays, ArrayList<Event> events, - final Runnable successCallback, final Runnable cancelCallback) { - this.id = id; - this.startDay = startDay; - this.numDays = numDays; - this.events = events; - this.successCallback = successCallback; - this.cancelCallback = cancelCallback; - } - - public void processRequest(EventLoader eventLoader) { - Event.loadEvents(eventLoader.mContext, events, startDay, - numDays, id, eventLoader.mSequenceNumber); - - // Check if we are still the most recent request. - if (id == eventLoader.mSequenceNumber.get()) { - eventLoader.mHandler.post(successCallback); - } else { - eventLoader.mHandler.post(cancelCallback); - } - } - - public void skipRequest(EventLoader eventLoader) { - eventLoader.mHandler.post(cancelCallback); - } - } - - private static class LoaderThread extends Thread { - LinkedBlockingQueue<LoadRequest> mQueue; - EventLoader mEventLoader; - - public LoaderThread(LinkedBlockingQueue<LoadRequest> queue, EventLoader eventLoader) { - mQueue = queue; - mEventLoader = eventLoader; - } - - public void shutdown() { - try { - mQueue.put(new ShutdownRequest()); - } catch (InterruptedException ex) { - // The put() method fails with InterruptedException if the - // queue is full. This should never happen because the queue - // has no limit. - Log.e("Cal", "LoaderThread.shutdown() interrupted!"); - } - } - - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - while (true) { - try { - // Wait for the next request - LoadRequest request = mQueue.take(); - - // If there are a bunch of requests already waiting, then - // skip all but the most recent request. - while (!mQueue.isEmpty()) { - // Let the request know that it was skipped - request.skipRequest(mEventLoader); - - // Skip to the next request - request = mQueue.take(); - } - - if (request instanceof ShutdownRequest) { - return; - } - request.processRequest(mEventLoader); - } catch (InterruptedException ex) { - Log.e("Cal", "background LoaderThread interrupted!"); - } - } - } - } - - public EventLoader(Context context) { - mContext = context; - mLoaderQueue = new LinkedBlockingQueue<LoadRequest>(); - mResolver = context.getContentResolver(); - } - - /** - * Call this from the activity's onResume() - */ - public void startBackgroundThread() { - mLoaderThread = new LoaderThread(mLoaderQueue, this); - mLoaderThread.start(); - } - - /** - * Call this from the activity's onPause() - */ - public void stopBackgroundThread() { - mLoaderThread.shutdown(); - } - - /** - * Loads "numDays" days worth of events, starting at start, into events. - * Posts uiCallback to the {@link Handler} for this view, which will run in the UI thread. - * Reuses an existing background thread, if events were already being loaded in the background. - * NOTE: events and uiCallback are not used if an existing background thread gets reused -- - * the ones that were passed in on the call that results in the background thread getting - * created are used, and the most recent call's worth of data is loaded into events and posted - * via the uiCallback. - */ - public void loadEventsInBackground(final int numDays, final ArrayList<Event> events, - int startDay, final Runnable successCallback, final Runnable cancelCallback) { - - // Increment the sequence number for requests. We don't care if the - // sequence numbers wrap around because we test for equality with the - // latest one. - int id = mSequenceNumber.incrementAndGet(); - - // Send the load request to the background thread - LoadEventsRequest request = new LoadEventsRequest(id, startDay, numDays, - events, successCallback, cancelCallback); - - try { - mLoaderQueue.put(request); - } catch (InterruptedException ex) { - // The put() method fails with InterruptedException if the - // queue is full. This should never happen because the queue - // has no limit. - Log.e("Cal", "loadEventsInBackground() interrupted!"); - } - } - - /** - * Sends a request for the days with events to be marked. Loads "numDays" - * worth of days, starting at start, and fills in eventDays to express which - * days have events. - * - * @param startDay First day to check for events - * @param numDays Days following the start day to check - * @param eventDay Whether or not an event exists on that day - * @param uiCallback What to do when done (log data, redraw screen) - */ - void loadEventDaysInBackground(int startDay, int numDays, boolean[] eventDays, - final Runnable uiCallback) - { - // Send load request to the background thread - LoadEventDaysRequest request = new LoadEventDaysRequest(startDay, numDays, - eventDays, uiCallback); - try { - mLoaderQueue.put(request); - } catch (InterruptedException ex) { - // The put() method fails with InterruptedException if the - // queue is full. This should never happen because the queue - // has no limit. - Log.e("Cal", "loadEventDaysInBackground() interrupted!"); - } - } -} diff --git a/src/com/android/calendar/GeneralPreferences.java b/src/com/android/calendar/GeneralPreferences.java deleted file mode 100644 index a42f07e3..00000000 --- a/src/com/android/calendar/GeneralPreferences.java +++ /dev/null @@ -1,400 +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.app.Activity; -import android.app.FragmentManager; -import android.app.backup.BackupManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.content.SharedPreferences.OnSharedPreferenceChangeListener; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.Vibrator; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.preference.RingtonePreference; -import android.provider.CalendarContract; -import android.provider.CalendarContract.CalendarCache; -import android.provider.SearchRecentSuggestions; -import android.text.TextUtils; -import android.text.format.Time; -import android.widget.Toast; - -import com.android.calendar.alerts.AlertReceiver; -import com.android.timezonepicker.TimeZoneInfo; -import com.android.timezonepicker.TimeZonePickerDialog; -import com.android.timezonepicker.TimeZonePickerDialog.OnTimeZoneSetListener; -import com.android.timezonepicker.TimeZonePickerUtils; - -public class GeneralPreferences extends PreferenceFragment implements - OnSharedPreferenceChangeListener, OnPreferenceChangeListener, OnTimeZoneSetListener { - // 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. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - static final String SHARED_PREFS_NAME_NO_BACKUP = "com.android.calendar_preferences_no_backup"; - - private static final String FRAG_TAG_TIME_ZONE_PICKER = "TimeZonePicker"; - - // Preference keys - public static final String KEY_HIDE_DECLINED = "preferences_hide_declined"; - public static final String KEY_WEEK_START_DAY = "preferences_week_start_day"; - public static final String KEY_SHOW_WEEK_NUM = "preferences_show_week_num"; - public static final String KEY_DAYS_PER_WEEK = "preferences_days_per_week"; - public static final String KEY_SKIP_SETUP = "preferences_skip_setup"; - - public static final String KEY_CLEAR_SEARCH_HISTORY = "preferences_clear_search_history"; - - public static final String KEY_ALERTS_CATEGORY = "preferences_alerts_category"; - public static final String KEY_ALERTS = "preferences_alerts"; - public static final String KEY_ALERTS_VIBRATE = "preferences_alerts_vibrate"; - public static final String KEY_ALERTS_RINGTONE = "preferences_alerts_ringtone"; - public static final String KEY_ALERTS_POPUP = "preferences_alerts_popup"; - - public static final String KEY_SHOW_CONTROLS = "preferences_show_controls"; - - public static final String KEY_DEFAULT_REMINDER = "preferences_default_reminder"; - public static final int NO_REMINDER = -1; - public static final String NO_REMINDER_STRING = "-1"; - public static final int REMINDER_DEFAULT_TIME = 10; // in minutes - - public static final String KEY_DEFAULT_CELL_HEIGHT = "preferences_default_cell_height"; - public static final String KEY_VERSION = "preferences_version"; - - /** Key to SharePreference for default view (CalendarController.ViewType) */ - public static final String KEY_START_VIEW = "preferred_startView"; - /** - * Key to SharePreference for default detail view (CalendarController.ViewType) - * Typically used by widget - */ - public static final String KEY_DETAILED_VIEW = "preferred_detailedView"; - public static final String KEY_DEFAULT_CALENDAR = "preference_defaultCalendar"; - - // These must be in sync with the array preferences_week_start_day_values - public static final String WEEK_START_DEFAULT = "-1"; - public static final String WEEK_START_SATURDAY = "7"; - public static final String WEEK_START_SUNDAY = "1"; - public static final String WEEK_START_MONDAY = "2"; - - // These keys are kept to enable migrating users from previous versions - private static final String KEY_ALERTS_TYPE = "preferences_alerts_type"; - private static final String ALERT_TYPE_ALERTS = "0"; - private static final String ALERT_TYPE_STATUS_BAR = "1"; - private static final String ALERT_TYPE_OFF = "2"; - static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled"; - static final String KEY_HOME_TZ = "preferences_home_tz"; - - // Default preference values - public static final int DEFAULT_START_VIEW = CalendarController.ViewType.WEEK; - public static final int DEFAULT_DETAILED_VIEW = CalendarController.ViewType.DAY; - public static final boolean DEFAULT_SHOW_WEEK_NUM = false; - // This should match the XML file. - public static final String DEFAULT_RINGTONE = "content://settings/system/notification_sound"; - - CheckBoxPreference mAlert; - CheckBoxPreference mVibrate; - CheckBoxPreference mPopup; - CheckBoxPreference mUseHomeTZ; - CheckBoxPreference mHideDeclined; - Preference mHomeTZ; - TimeZonePickerUtils mTzPickerUtils; - ListPreference mWeekStart; - ListPreference mDefaultReminder; - - private String mTimeZoneId; - - /** Return a properly configured SharedPreferences instance */ - public static SharedPreferences getSharedPreferences(Context context) { - return context.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE); - } - - /** Set the default shared preferences in the proper context */ - public static void setDefaultValues(Context context) { - PreferenceManager.setDefaultValues(context, SHARED_PREFS_NAME, Context.MODE_PRIVATE, - R.xml.general_preferences, false); - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final Activity activity = getActivity(); - - // Make sure to always use the same preferences file regardless of the package name - // we're running under - final PreferenceManager preferenceManager = getPreferenceManager(); - final SharedPreferences sharedPreferences = getSharedPreferences(activity); - preferenceManager.setSharedPreferencesName(SHARED_PREFS_NAME); - - // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.general_preferences); - - final PreferenceScreen preferenceScreen = getPreferenceScreen(); - mAlert = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS); - mVibrate = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_VIBRATE); - Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); - if (vibrator == null || !vibrator.hasVibrator()) { - PreferenceCategory mAlertGroup = (PreferenceCategory) preferenceScreen - .findPreference(KEY_ALERTS_CATEGORY); - mAlertGroup.removePreference(mVibrate); - } - - mPopup = (CheckBoxPreference) preferenceScreen.findPreference(KEY_ALERTS_POPUP); - mUseHomeTZ = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HOME_TZ_ENABLED); - mHideDeclined = (CheckBoxPreference) preferenceScreen.findPreference(KEY_HIDE_DECLINED); - mWeekStart = (ListPreference) preferenceScreen.findPreference(KEY_WEEK_START_DAY); - mDefaultReminder = (ListPreference) preferenceScreen.findPreference(KEY_DEFAULT_REMINDER); - mHomeTZ = preferenceScreen.findPreference(KEY_HOME_TZ); - mWeekStart.setSummary(mWeekStart.getEntry()); - mDefaultReminder.setSummary(mDefaultReminder.getEntry()); - - // This triggers an asynchronous call to the provider to refresh the data in shared pref - mTimeZoneId = Utils.getTimeZone(activity, null); - - SharedPreferences prefs = CalendarUtils.getSharedPreferences(activity, - Utils.SHARED_PREFS_NAME); - - // Utils.getTimeZone will return the currentTimeZone instead of the one - // in the shared_pref if home time zone is disabled. So if home tz is - // off, we will explicitly read it. - if (!prefs.getBoolean(KEY_HOME_TZ_ENABLED, false)) { - mTimeZoneId = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone()); - } - - mHomeTZ.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - showTimezoneDialog(); - return true; - } - }); - - if (mTzPickerUtils == null) { - mTzPickerUtils = new TimeZonePickerUtils(getActivity()); - } - CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName(getActivity(), mTimeZoneId, - System.currentTimeMillis(), false); - mHomeTZ.setSummary(timezoneName != null ? timezoneName : mTimeZoneId); - - TimeZonePickerDialog tzpd = (TimeZonePickerDialog) activity.getFragmentManager() - .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); - if (tzpd != null) { - tzpd.setOnTimeZoneSetListener(this); - } - - migrateOldPreferences(sharedPreferences); - - updateChildPreferences(); - } - - private void showTimezoneDialog() { - final Activity activity = getActivity(); - if (activity == null) { - return; - } - - Bundle b = new Bundle(); - b.putLong(TimeZonePickerDialog.BUNDLE_START_TIME_MILLIS, System.currentTimeMillis()); - b.putString(TimeZonePickerDialog.BUNDLE_TIME_ZONE, Utils.getTimeZone(activity, null)); - - FragmentManager fm = getActivity().getFragmentManager(); - TimeZonePickerDialog tzpd = (TimeZonePickerDialog) fm - .findFragmentByTag(FRAG_TAG_TIME_ZONE_PICKER); - if (tzpd != null) { - tzpd.dismiss(); - } - tzpd = new TimeZonePickerDialog(); - tzpd.setArguments(b); - tzpd.setOnTimeZoneSetListener(this); - tzpd.show(fm, FRAG_TAG_TIME_ZONE_PICKER); - } - - @Override - public void onStart() { - super.onStart(); - getPreferenceScreen().getSharedPreferences() - .registerOnSharedPreferenceChangeListener(this); - setPreferenceListeners(this); - } - - /** - * Sets up all the preference change listeners to use the specified - * listener. - */ - private void setPreferenceListeners(OnPreferenceChangeListener listener) { - mUseHomeTZ.setOnPreferenceChangeListener(listener); - mHomeTZ.setOnPreferenceChangeListener(listener); - mWeekStart.setOnPreferenceChangeListener(listener); - mDefaultReminder.setOnPreferenceChangeListener(listener); - mHideDeclined.setOnPreferenceChangeListener(listener); - mVibrate.setOnPreferenceChangeListener(listener); - } - - @Override - public void onStop() { - getPreferenceScreen().getSharedPreferences() - .unregisterOnSharedPreferenceChangeListener(this); - setPreferenceListeners(null); - super.onStop(); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Activity a = getActivity(); - if (key.equals(KEY_ALERTS)) { - updateChildPreferences(); - if (a != null) { - Intent intent = new Intent(); - intent.setClass(a, AlertReceiver.class); - if (mAlert.isChecked()) { - intent.setAction(AlertReceiver.ACTION_DISMISS_OLD_REMINDERS); - } else { - intent.setAction(AlertReceiver.EVENT_REMINDER_APP_ACTION); - } - a.sendBroadcast(intent); - } - } - if (a != null) { - BackupManager.dataChanged(a.getPackageName()); - } - } - - /** - * Handles time zone preference changes - */ - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String tz; - final Activity activity = getActivity(); - if (preference == mUseHomeTZ) { - if ((Boolean)newValue) { - tz = mTimeZoneId; - } else { - tz = CalendarCache.TIMEZONE_TYPE_AUTO; - } - Utils.setTimeZone(activity, tz); - return true; - } else if (preference == mHideDeclined) { - mHideDeclined.setChecked((Boolean) newValue); - Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(activity)); - intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); - activity.sendBroadcast(intent); - return true; - } else if (preference == mWeekStart) { - mWeekStart.setValue((String) newValue); - mWeekStart.setSummary(mWeekStart.getEntry()); - } else if (preference == mDefaultReminder) { - mDefaultReminder.setValue((String) newValue); - mDefaultReminder.setSummary(mDefaultReminder.getEntry()); - } else if (preference == mVibrate) { - mVibrate.setChecked((Boolean) newValue); - return true; - } else { - return true; - } - return false; - } - - public String getRingtoneTitleFromUri(Context context, String uri) { - if (TextUtils.isEmpty(uri)) { - return null; - } - - Ringtone ring = RingtoneManager.getRingtone(getActivity(), Uri.parse(uri)); - if (ring != null) { - return ring.getTitle(context); - } - return null; - } - - /** - * If necessary, upgrades previous versions of preferences to the current - * set of keys and values. - * @param prefs the preferences to upgrade - */ - private void migrateOldPreferences(SharedPreferences prefs) { - // If needed, migrate vibration setting from a previous version - - mVibrate.setChecked(Utils.getDefaultVibrate(getActivity(), prefs)); - - // If needed, migrate the old alerts type settin - if (!prefs.contains(KEY_ALERTS) && prefs.contains(KEY_ALERTS_TYPE)) { - String type = prefs.getString(KEY_ALERTS_TYPE, ALERT_TYPE_STATUS_BAR); - if (type.equals(ALERT_TYPE_OFF)) { - mAlert.setChecked(false); - mPopup.setChecked(false); - mPopup.setEnabled(false); - } else if (type.equals(ALERT_TYPE_STATUS_BAR)) { - mAlert.setChecked(true); - mPopup.setChecked(false); - mPopup.setEnabled(true); - } else if (type.equals(ALERT_TYPE_ALERTS)) { - mAlert.setChecked(true); - mPopup.setChecked(true); - mPopup.setEnabled(true); - } - // clear out the old setting - prefs.edit().remove(KEY_ALERTS_TYPE).commit(); - } - } - - /** - * Keeps the dependent settings in sync with the parent preference, so for - * example, when notifications are turned off, we disable the preferences - * for configuring the exact notification behavior. - */ - private void updateChildPreferences() { - if (mAlert.isChecked()) { - mVibrate.setEnabled(true); - mPopup.setEnabled(true); - } else { - mVibrate.setEnabled(false); - mPopup.setEnabled(false); - } - } - - - @Override - public boolean onPreferenceTreeClick( - PreferenceScreen preferenceScreen, Preference preference) { - final String key = preference.getKey(); - return super.onPreferenceTreeClick(preferenceScreen, preference); - } - - @Override - public void onTimeZoneSet(TimeZoneInfo tzi) { - if (mTzPickerUtils == null) { - mTzPickerUtils = new TimeZonePickerUtils(getActivity()); - } - - final CharSequence timezoneName = mTzPickerUtils.getGmtDisplayName( - getActivity(), tzi.mTzId, System.currentTimeMillis(), false); - mHomeTZ.setSummary(timezoneName); - Utils.setTimeZone(getActivity(), tzi.mTzId); - } -} diff --git a/src/com/android/calendar/GoogleCalendarUriIntentFilter.java b/src/com/android/calendar/GoogleCalendarUriIntentFilter.java deleted file mode 100644 index 3970115b..00000000 --- a/src/com/android/calendar/GoogleCalendarUriIntentFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* -** -** Copyright 2009, 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, -** See the License for the specific language governing permissions and -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** limitations under the License. -*/ - -package com.android.calendar; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.os.Bundle; - -public class GoogleCalendarUriIntentFilter extends Activity { - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Intent intent = getIntent(); - if (intent != null) { - // Pass it on to the next Activity. - try { - startNextMatchingActivity(intent); - } catch (ActivityNotFoundException ex) { - // no browser installed? Just drop it. - } - } - finish(); - } -} diff --git a/src/com/android/calendar/MultiStateButton.java b/src/com/android/calendar/MultiStateButton.java deleted file mode 100644 index 8034b28e..00000000 --- a/src/com/android/calendar/MultiStateButton.java +++ /dev/null @@ -1,192 +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.Context; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.widget.Button; - -/** - * <p> - * A button with more than two states. When the button is pressed - * or clicked, the state transitions automatically. - * </p> - * - * <p><strong>XML attributes</strong></p> - * <p> - * See {@link R.styleable#MultiStateButton - * MultiStateButton Attributes}, {@link android.R.styleable#Button Button - * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link - * android.R.styleable#View View Attributes} - * </p> - */ - -public class MultiStateButton extends Button { - //The current state for this button, ranging from 0 to maxState-1 - private int mState; - //The maximum number of states allowed for this button. - private int mMaxStates; - //The currently displaying resource ID. This gets set to a default on creation and remains - //on the last set if the resources get set to null. - private int mButtonResource; - //A list of all drawable resources used by this button in the order it uses them. - private int[] mButtonResources; - private Drawable mButtonDrawable; - - public MultiStateButton(Context context) { - this(context, null); - } - - public MultiStateButton(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public MultiStateButton(Context context, AttributeSet attrs, int defStyle) { - //Currently using the standard buttonStyle, will update when new resources are added. - super(context, attrs, defStyle); - mMaxStates = 1; - mState = 0; - //TODO add a more generic default button - mButtonResources = new int[] { R.drawable.widget_show }; - setButtonDrawable(mButtonResources[mState]); - } - - @Override - public boolean performClick() { - /* When clicked, toggle the state */ - transitionState(); - return super.performClick(); - } - - public void transitionState() { - mState = (mState + 1) % mMaxStates; - setButtonDrawable(mButtonResources[mState]); - } - - /** - * Allows for a new set of drawable resource ids to be set. - * - * This sets the maximum states allowed to the length of the resources array. It will also - * set the current state to the maximum allowed if it's greater than the new max. - */ - public void setButtonResources(int[] resources) throws IllegalArgumentException { - if(resources == null) { - throw new IllegalArgumentException("Button resources cannot be null"); - } - mMaxStates = resources.length; - if(mState >= mMaxStates) { - mState = mMaxStates - 1; - } - mButtonResources = resources; - } - - /** - * Attempts to set the state. Returns true if successful, false otherwise. - */ - public boolean setState(int state){ - if(state >= mMaxStates || state < 0) { - //When moved out of Calendar the tag should be changed. - Log.w("Cal", "MultiStateButton state set to value greater than maxState or < 0"); - return false; - } - mState = state; - setButtonDrawable(mButtonResources[mState]); - return true; - } - - public int getState() { - return mState; - } - - /** - * Set the background to a given Drawable, identified by its resource id. - * - * @param resid the resource id of the drawable to use as the background - */ - public void setButtonDrawable(int resid) { - if (resid != 0 && resid == mButtonResource) { - return; - } - - mButtonResource = resid; - - Drawable d = null; - if (mButtonResource != 0) { - d = getResources().getDrawable(mButtonResource); - } - setButtonDrawable(d); - } - - /** - * Set the background to a given Drawable - * - * @param d The Drawable to use as the background - */ - public void setButtonDrawable(Drawable d) { - if (d != null) { - if (mButtonDrawable != null) { - mButtonDrawable.setCallback(null); - unscheduleDrawable(mButtonDrawable); - } - d.setCallback(this); - d.setState(getDrawableState()); - d.setVisible(getVisibility() == VISIBLE, false); - mButtonDrawable = d; - mButtonDrawable.setState(null); - setMinHeight(mButtonDrawable.getIntrinsicHeight()); - setWidth(mButtonDrawable.getIntrinsicWidth()); - } - refreshDrawableState(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (mButtonDrawable != null) { - final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; - final int horizontalGravity = getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK; - final int height = mButtonDrawable.getIntrinsicHeight(); - final int width = mButtonDrawable.getIntrinsicWidth(); - - int y = 0; - int x = 0; - - switch (verticalGravity) { - case Gravity.BOTTOM: - y = getHeight() - height; - break; - case Gravity.CENTER_VERTICAL: - y = (getHeight() - height) / 2; - break; - } - switch (horizontalGravity) { - case Gravity.RIGHT: - x = getWidth() - width; - break; - case Gravity.CENTER_HORIZONTAL: - x = (getWidth() - width) / 2; - break; - } - - mButtonDrawable.setBounds(x, y, x + width, y + height); - mButtonDrawable.draw(canvas); - } - } -} diff --git a/src/com/android/calendar/OtherPreferences.java b/src/com/android/calendar/OtherPreferences.java deleted file mode 100644 index a59d3f46..00000000 --- a/src/com/android/calendar/OtherPreferences.java +++ /dev/null @@ -1,210 +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 android.app.Activity; -import android.app.Dialog; -import android.app.TimePickerDialog; -import android.content.ComponentName; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceFragment; -import android.preference.PreferenceManager; -import android.preference.PreferenceScreen; -import android.text.format.DateFormat; -import android.text.format.Time; -import android.util.Log; -import android.widget.TimePicker; - -public class OtherPreferences extends PreferenceFragment implements OnPreferenceChangeListener{ - private static final String TAG = "CalendarOtherPreferences"; - - // 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. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - - // Must be the same keys that are used in the other_preferences.xml file. - public static final String KEY_OTHER_COPY_DB = "preferences_copy_db"; - public static final String KEY_OTHER_QUIET_HOURS = "preferences_reminders_quiet_hours"; - public static final String KEY_OTHER_REMINDERS_RESPONDED = "preferences_reminders_responded"; - public static final String KEY_OTHER_QUIET_HOURS_START = - "preferences_reminders_quiet_hours_start"; - public static final String KEY_OTHER_QUIET_HOURS_START_HOUR = - "preferences_reminders_quiet_hours_start_hour"; - public static final String KEY_OTHER_QUIET_HOURS_START_MINUTE = - "preferences_reminders_quiet_hours_start_minute"; - public static final String KEY_OTHER_QUIET_HOURS_END = - "preferences_reminders_quiet_hours_end"; - public static final String KEY_OTHER_QUIET_HOURS_END_HOUR = - "preferences_reminders_quiet_hours_end_hour"; - public static final String KEY_OTHER_QUIET_HOURS_END_MINUTE = - "preferences_reminders_quiet_hours_end_minute"; - public static final String KEY_OTHER_1 = "preferences_tardis_1"; - - public static final int QUIET_HOURS_DEFAULT_START_HOUR = 22; - public static final int QUIET_HOURS_DEFAULT_START_MINUTE = 0; - public static final int QUIET_HOURS_DEFAULT_END_HOUR = 8; - public static final int QUIET_HOURS_DEFAULT_END_MINUTE = 0; - - private static final int START_LISTENER = 1; - private static final int END_LISTENER = 2; - private static final String format24Hour = "%H:%M"; - private static final String format12Hour = "%I:%M%P"; - - private Preference mCopyDb; - private CheckBoxPreference mQuietHours; - private Preference mQuietHoursStart; - private Preference mQuietHoursEnd; - - private TimePickerDialog mTimePickerDialog; - private TimeSetListener mQuietHoursStartListener; - private TimePickerDialog mQuietHoursStartDialog; - private TimeSetListener mQuietHoursEndListener; - private TimePickerDialog mQuietHoursEndDialog; - private boolean mIs24HourMode; - - public OtherPreferences() { - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - PreferenceManager manager = getPreferenceManager(); - manager.setSharedPreferencesName(SHARED_PREFS_NAME); - SharedPreferences prefs = manager.getSharedPreferences(); - - addPreferencesFromResource(R.xml.other_preferences); - mCopyDb = findPreference(KEY_OTHER_COPY_DB); - - Activity activity = getActivity(); - if (activity == null) { - Log.d(TAG, "Activity was null"); - } - mIs24HourMode = DateFormat.is24HourFormat(activity); - - mQuietHours = - (CheckBoxPreference) findPreference(KEY_OTHER_QUIET_HOURS); - - int startHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_HOUR, - QUIET_HOURS_DEFAULT_START_HOUR); - int startMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, - QUIET_HOURS_DEFAULT_START_MINUTE); - mQuietHoursStart = findPreference(KEY_OTHER_QUIET_HOURS_START); - mQuietHoursStartListener = new TimeSetListener(START_LISTENER); - mQuietHoursStartDialog = new TimePickerDialog( - activity, mQuietHoursStartListener, - startHour, startMinute, mIs24HourMode); - mQuietHoursStart.setSummary(formatTime(startHour, startMinute)); - - int endHour = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_HOUR, - QUIET_HOURS_DEFAULT_END_HOUR); - int endMinute = prefs.getInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, - QUIET_HOURS_DEFAULT_END_MINUTE); - mQuietHoursEnd = findPreference(KEY_OTHER_QUIET_HOURS_END); - mQuietHoursEndListener = new TimeSetListener(END_LISTENER); - mQuietHoursEndDialog = new TimePickerDialog( - activity, mQuietHoursEndListener, - endHour, endMinute, mIs24HourMode); - mQuietHoursEnd.setSummary(formatTime(endHour, endMinute)); - } - - @Override - public boolean onPreferenceChange(Preference preference, Object objValue) { - return true; - } - - @Override - public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { - if (preference == mCopyDb) { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setComponent(new ComponentName("com.android.providers.calendar", - "com.android.providers.calendar.CalendarDebugActivity")); - startActivity(intent); - } else if (preference == mQuietHoursStart) { - if (mTimePickerDialog == null) { - mTimePickerDialog = mQuietHoursStartDialog; - mTimePickerDialog.show(); - } else { - Log.v(TAG, "not null"); - } - } else if (preference == mQuietHoursEnd) { - if (mTimePickerDialog == null) { - mTimePickerDialog = mQuietHoursEndDialog; - mTimePickerDialog.show(); - } else { - Log.v(TAG, "not null"); - } - } else { - return super.onPreferenceTreeClick(screen, preference); - } - return true; - } - - private class TimeSetListener implements TimePickerDialog.OnTimeSetListener { - private int mListenerId; - - public TimeSetListener(int listenerId) { - mListenerId = listenerId; - } - - @Override - public void onTimeSet(TimePicker view, int hourOfDay, int minute) { - mTimePickerDialog = null; - - SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - SharedPreferences.Editor editor = prefs.edit(); - - String summary = formatTime(hourOfDay, minute); - switch (mListenerId) { - case (START_LISTENER): - mQuietHoursStart.setSummary(summary); - editor.putInt(KEY_OTHER_QUIET_HOURS_START_HOUR, hourOfDay); - editor.putInt(KEY_OTHER_QUIET_HOURS_START_MINUTE, minute); - break; - case (END_LISTENER): - mQuietHoursEnd.setSummary(summary); - editor.putInt(KEY_OTHER_QUIET_HOURS_END_HOUR, hourOfDay); - editor.putInt(KEY_OTHER_QUIET_HOURS_END_MINUTE, minute); - break; - default: - Log.d(TAG, "Set time for unknown listener: "+mListenerId); - } - - editor.commit(); - } - } - - /** - * @param hourOfDay the hour of the day (0-24) - * @param minute - * @return human-readable string formatted based on 24-hour mode. - */ - private String formatTime(int hourOfDay, int minute) { - Time time = new Time(); - time.hour = hourOfDay; - time.minute = minute; - - String format = mIs24HourMode? format24Hour : format12Hour; - return time.format(format); - } -} diff --git a/src/com/android/calendar/StickyHeaderListView.java b/src/com/android/calendar/StickyHeaderListView.java deleted file mode 100644 index 981e7af7..00000000 --- a/src/com/android/calendar/StickyHeaderListView.java +++ /dev/null @@ -1,395 +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 android.content.Context; -import android.graphics.Color; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.Adapter; -import android.widget.FrameLayout; -import android.widget.ListView; - -/** - * Implements a ListView class with a sticky header at the top. The header is - * per section and it is pinned to the top as long as its section is at the top - * of the view. If it is not, the header slides up or down (depending on the - * scroll movement) and the header of the current section slides to the top. - * Notes: - * 1. The class uses the first available child ListView as the working - * ListView. If no ListView child exists, the class will create a default one. - * 2. The ListView's adapter must be passed to this class using the 'setAdapter' - * method. The adapter must implement the HeaderIndexer interface. If no adapter - * is specified, the class will try to extract it from the ListView - * 3. The class registers itself as a listener to scroll events (OnScrollListener), if the - * ListView needs to receive scroll events, it must register its listener using - * this class' setOnScrollListener method. - * 4. Headers for the list view must be added before using the StickyHeaderListView - * 5. The implementation should register to listen to dataset changes. Right now this is not done - * since a change the dataset in a listview forces a call to OnScroll. The needed code is - * commented out. - */ -public class StickyHeaderListView extends FrameLayout implements OnScrollListener { - - private static final String TAG = "StickyHeaderListView"; - protected boolean mChildViewsCreated = false; - protected boolean mDoHeaderReset = false; - - protected Context mContext = null; - protected Adapter mAdapter = null; - protected HeaderIndexer mIndexer = null; - protected HeaderHeightListener mHeaderHeightListener = null; - protected View mStickyHeader = null; - protected View mNonessentialHeader = null; // A invisible header used when a section has no header - protected ListView mListView = null; - protected ListView.OnScrollListener mListener = null; - - private int mSeparatorWidth; - private View mSeparatorView; - private int mLastStickyHeaderHeight = 0; - - // This code is needed only if dataset changes do not force a call to OnScroll - // protected DataSetObserver mListDataObserver = null; - - - protected int mCurrentSectionPos = -1; // Position of section that has its header on the - // top of the view - protected int mNextSectionPosition = -1; // Position of next section's header - protected int mListViewHeadersCount = 0; - - /** - * Interface that must be implemented by the ListView adapter to provide headers locations - * and number of items under each header. - * - */ - public interface HeaderIndexer { - /** - * Calculates the position of the header of a specific item in the adapter's data set. - * For example: Assuming you have a list with albums and songs names: - * Album A, song 1, song 2, ...., song 10, Album B, song 1, ..., song 7. A call to - * this method with the position of song 5 in Album B, should return the position - * of Album B. - * @param position - Position of the item in the ListView dataset - * @return Position of header. -1 if the is no header - */ - - int getHeaderPositionFromItemPosition(int position); - - /** - * Calculates the number of items in the section defined by the header (not including - * the header). - * For example: A list with albums and songs, the method should return - * the number of songs names (without the album name). - * - * @param headerPosition - the value returned by 'getHeaderPositionFromItemPosition' - * @return Number of items. -1 on error. - */ - int getHeaderItemsNumber(int headerPosition); - } - - /*** - * - * Interface that is used to update the sticky header's height - * - */ - public interface HeaderHeightListener { - - /*** - * Updated a change in the sticky header's size - * - * @param height - new height of sticky header - */ - void OnHeaderHeightChanged(int height); - } - - /** - * Sets the adapter to be used by the class to get views of headers - * - * @param adapter - The adapter. - */ - - public void setAdapter(Adapter adapter) { - - // This code is needed only if dataset changes do not force a call to - // OnScroll - // if (mAdapter != null && mListDataObserver != null) { - // mAdapter.unregisterDataSetObserver(mListDataObserver); - // } - - if (adapter != null) { - mAdapter = adapter; - // This code is needed only if dataset changes do not force a call - // to OnScroll - // mAdapter.registerDataSetObserver(mListDataObserver); - } - } - - /** - * Sets the indexer object (that implements the HeaderIndexer interface). - * - * @param indexer - The indexer. - */ - - public void setIndexer(HeaderIndexer indexer) { - mIndexer = indexer; - } - - /** - * Sets the list view that is displayed - * @param lv - The list view. - */ - - public void setListView(ListView lv) { - mListView = lv; - mListView.setOnScrollListener(this); - mListViewHeadersCount = mListView.getHeaderViewsCount(); - } - - /** - * Sets an external OnScroll listener. Since the StickyHeaderListView sets - * itself as the scroll events listener of the listview, this method allows - * the user to register another listener that will be called after this - * class listener is called. - * - * @param listener - The external listener. - */ - public void setOnScrollListener(ListView.OnScrollListener listener) { - mListener = listener; - } - - public void setHeaderHeightListener(HeaderHeightListener listener) { - mHeaderHeightListener = listener; - } - - // This code is needed only if dataset changes do not force a call to OnScroll - // protected void createDataListener() { - // mListDataObserver = new DataSetObserver() { - // @Override - // public void onChanged() { - // onDataChanged(); - // } - // }; - // } - - /** - * Constructor - * - * @param context - application context. - * @param attrs - layout attributes. - */ - public StickyHeaderListView(Context context, AttributeSet attrs) { - super(context, attrs); - mContext = context; - // This code is needed only if dataset changes do not force a call to OnScroll - // createDataListener(); - } - - /** - * Scroll status changes listener - * - * @param view - the scrolled view - * @param scrollState - new scroll state. - */ - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - if (mListener != null) { - mListener.onScrollStateChanged(view, scrollState); - } - } - - /** - * Scroll events listener - * - * @param view - the scrolled view - * @param firstVisibleItem - the index (in the list's adapter) of the top - * visible item. - * @param visibleItemCount - the number of visible items in the list - * @param totalItemCount - the total number items in the list - */ - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, - int totalItemCount) { - - updateStickyHeader(firstVisibleItem); - - if (mListener != null) { - mListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); - } - } - - /** - * Sets a separator below the sticky header, which will be visible while the sticky header - * is not scrolling up. - * @param color - color of separator - * @param width - width in pixels of separator - */ - public void setHeaderSeparator(int color, int width) { - mSeparatorView = new View(mContext); - ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, - width, Gravity.TOP); - mSeparatorView.setLayoutParams(params); - mSeparatorView.setBackgroundColor(color); - mSeparatorWidth = width; - this.addView(mSeparatorView); - } - - protected void updateStickyHeader(int firstVisibleItem) { - - // Try to make sure we have an adapter to work with (may not succeed). - if (mAdapter == null && mListView != null) { - setAdapter(mListView.getAdapter()); - } - - firstVisibleItem -= mListViewHeadersCount; - if (mAdapter != null && mIndexer != null && mDoHeaderReset) { - - // Get the section header position - int sectionSize = 0; - int sectionPos = mIndexer.getHeaderPositionFromItemPosition(firstVisibleItem); - - // New section - set it in the header view - boolean newView = false; - if (sectionPos != mCurrentSectionPos) { - - // No header for current position , use the nonessential invisible one, hide the separator - if (sectionPos == -1) { - sectionSize = 0; - this.removeView(mStickyHeader); - mStickyHeader = mNonessentialHeader; - if (mSeparatorView != null) { - mSeparatorView.setVisibility(View.GONE); - } - newView = true; - } else { - // Create a copy of the header view to show on top - sectionSize = mIndexer.getHeaderItemsNumber(sectionPos); - View v = mAdapter.getView(sectionPos + mListViewHeadersCount, null, mListView); - v.measure(MeasureSpec.makeMeasureSpec(mListView.getWidth(), - MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mListView.getHeight(), - MeasureSpec.AT_MOST)); - this.removeView(mStickyHeader); - mStickyHeader = v; - newView = true; - } - mCurrentSectionPos = sectionPos; - mNextSectionPosition = sectionSize + sectionPos + 1; - } - - - // Do transitions - // If position of bottom of last item in a section is smaller than the height of the - // sticky header - shift drawable of header. - if (mStickyHeader != null) { - int sectionLastItemPosition = mNextSectionPosition - firstVisibleItem - 1; - int stickyHeaderHeight = mStickyHeader.getHeight(); - if (stickyHeaderHeight == 0) { - stickyHeaderHeight = mStickyHeader.getMeasuredHeight(); - } - - // Update new header height - if (mHeaderHeightListener != null && - mLastStickyHeaderHeight != stickyHeaderHeight) { - mLastStickyHeaderHeight = stickyHeaderHeight; - mHeaderHeightListener.OnHeaderHeightChanged(stickyHeaderHeight); - } - - View SectionLastView = mListView.getChildAt(sectionLastItemPosition); - if (SectionLastView != null && SectionLastView.getBottom() <= stickyHeaderHeight) { - int lastViewBottom = SectionLastView.getBottom(); - mStickyHeader.setTranslationY(lastViewBottom - stickyHeaderHeight); - if (mSeparatorView != null) { - mSeparatorView.setVisibility(View.GONE); - } - } else if (stickyHeaderHeight != 0) { - mStickyHeader.setTranslationY(0); - if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)) { - mSeparatorView.setVisibility(View.VISIBLE); - } - } - if (newView) { - mStickyHeader.setVisibility(View.INVISIBLE); - this.addView(mStickyHeader); - if (mSeparatorView != null && !mStickyHeader.equals(mNonessentialHeader)){ - FrameLayout.LayoutParams params = - new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, - mSeparatorWidth); - params.setMargins(0, mStickyHeader.getMeasuredHeight(), 0, 0); - mSeparatorView.setLayoutParams(params); - mSeparatorView.setVisibility(View.VISIBLE); - } - mStickyHeader.setVisibility(View.VISIBLE); - } - } - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - if (!mChildViewsCreated) { - setChildViews(); - } - mDoHeaderReset = true; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (!mChildViewsCreated) { - setChildViews(); - } - mDoHeaderReset = true; - } - - - // Resets the sticky header when the adapter data set was changed - // This code is needed only if dataset changes do not force a call to OnScroll - // protected void onDataChanged() { - // Should do a call to updateStickyHeader if needed - // } - - private void setChildViews() { - - // Find a child ListView (if any) - int iChildNum = getChildCount(); - for (int i = 0; i < iChildNum; i++) { - Object v = getChildAt(i); - if (v instanceof ListView) { - setListView((ListView) v); - } - } - - // No child ListView - add one - if (mListView == null) { - setListView(new ListView(mContext)); - } - - // Create a nonessential view , it will be used in case a section has no header - mNonessentialHeader = new View (mContext); - ViewGroup.LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, - 1, Gravity.TOP); - mNonessentialHeader.setLayoutParams(params); - mNonessentialHeader.setBackgroundColor(Color.TRANSPARENT); - - mChildViewsCreated = true; - } - -} diff --git a/src/com/android/calendar/UpgradeReceiver.java b/src/com/android/calendar/UpgradeReceiver.java deleted file mode 100644 index 0e89286d..00000000 --- a/src/com/android/calendar/UpgradeReceiver.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2013 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.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class UpgradeReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, final Intent intent) { - Utils.trySyncAndDisableUpgradeReceiver(context); - } - -}
\ No newline at end of file diff --git a/src/com/android/calendar/Utils.java b/src/com/android/calendar/Utils.java deleted file mode 100644 index cc55c999..00000000 --- a/src/com/android/calendar/Utils.java +++ /dev/null @@ -1,1499 +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; - -import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; - -import android.accounts.Account; -import android.app.Activity; -import android.app.SearchManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.graphics.Color; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.provider.CalendarContract.Calendars; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.text.style.URLSpan; -import android.text.util.Linkify; -import android.util.Log; - -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.CalendarUtils.TimeZoneUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Utils { - private static final boolean DEBUG = false; - private static final String TAG = "CalUtils"; - - // Set to 0 until we have UI to perform undo - public static final long UNDO_DELAY = 0; - - // For recurring events which instances of the series are being modified - public static final int MODIFY_UNINITIALIZED = 0; - public static final int MODIFY_SELECTED = 1; - public static final int MODIFY_ALL_FOLLOWING = 2; - public static final int MODIFY_ALL = 3; - - // When the edit event view finishes it passes back the appropriate exit - // code. - public static final int DONE_REVERT = 1 << 0; - public static final int DONE_SAVE = 1 << 1; - public static final int DONE_DELETE = 1 << 2; - // And should re run with DONE_EXIT if it should also leave the view, just - // exiting is identical to reverting - public static final int DONE_EXIT = 1 << 0; - - public static final String OPEN_EMAIL_MARKER = " <"; - public static final String CLOSE_EMAIL_MARKER = ">"; - - public static final String INTENT_KEY_DETAIL_VIEW = "DETAIL_VIEW"; - public static final String INTENT_KEY_VIEW_TYPE = "VIEW"; - public static final String INTENT_VALUE_VIEW_TYPE_DAY = "DAY"; - public static final String INTENT_KEY_HOME = "KEY_HOME"; - - public static final int MONDAY_BEFORE_JULIAN_EPOCH = Time.EPOCH_JULIAN_DAY - 3; - public static final int DECLINED_EVENT_ALPHA = 0x66; - public static final int DECLINED_EVENT_TEXT_ALPHA = 0xC0; - - private static final float SATURATION_ADJUST = 1.3f; - private static final float INTENSITY_ADJUST = 0.8f; - - // Defines used by the DNA generation code - static final int DAY_IN_MINUTES = 60 * 24; - static final int WEEK_IN_MINUTES = DAY_IN_MINUTES * 7; - // The work day is being counted as 6am to 8pm - static int WORK_DAY_MINUTES = 14 * 60; - static int WORK_DAY_START_MINUTES = 6 * 60; - static int WORK_DAY_END_MINUTES = 20 * 60; - static int WORK_DAY_END_LENGTH = (24 * 60) - WORK_DAY_END_MINUTES; - static int CONFLICT_COLOR = 0xFF000000; - static boolean mMinutesLoaded = false; - - public static final int YEAR_MIN = 1970; - public static final int YEAR_MAX = 2036; - - // 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. - static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; - - public static final String KEY_QUICK_RESPONSES = "preferences_quick_responses"; - - public static final String KEY_ALERTS_VIBRATE_WHEN = "preferences_alerts_vibrateWhen"; - - public static final String APPWIDGET_DATA_TYPE = "vnd.android.data/update"; - - static final String MACHINE_GENERATED_ADDRESS = "calendar.google.com"; - - private static final TimeZoneUtils mTZUtils = new TimeZoneUtils(SHARED_PREFS_NAME); - private static boolean mAllowWeekForDetailView = false; - private static long mTardis = 0; - private static String sVersion = null; - - private static final Pattern mWildcardPattern = Pattern.compile("^.*$"); - - /** - * A coordinate must be of the following form for Google Maps to correctly use it: - * Latitude, Longitude - * - * This may be in decimal form: - * Latitude: {-90 to 90} - * Longitude: {-180 to 180} - * - * Or, in degrees, minutes, and seconds: - * Latitude: {-90 to 90}° {0 to 59}' {0 to 59}" - * Latitude: {-180 to 180}° {0 to 59}' {0 to 59}" - * + or - degrees may also be represented with N or n, S or s for latitude, and with - * E or e, W or w for longitude, where the direction may either precede or follow the value. - * - * Some examples of coordinates that will be accepted by the regex: - * 37.422081°, -122.084576° - * 37.422081,-122.084576 - * +37°25'19.49", -122°5'4.47" - * 37°25'19.49"N, 122°5'4.47"W - * N 37° 25' 19.49", W 122° 5' 4.47" - **/ - private static final String COORD_DEGREES_LATITUDE = - "([-+NnSs]" + "(\\s)*)?" - + "[1-9]?[0-9](\u00B0)" + "(\\s)*" - + "([1-5]?[0-9]\')?" + "(\\s)*" - + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" - + "((\\s)*" + "[NnSs])?"; - private static final String COORD_DEGREES_LONGITUDE = - "([-+EeWw]" + "(\\s)*)?" - + "(1)?[0-9]?[0-9](\u00B0)" + "(\\s)*" - + "([1-5]?[0-9]\')?" + "(\\s)*" - + "([1-5]?[0-9]" + "(\\.[0-9]+)?\")?" - + "((\\s)*" + "[EeWw])?"; - private static final String COORD_DEGREES_PATTERN = - COORD_DEGREES_LATITUDE - + "(\\s)*" + "," + "(\\s)*" - + COORD_DEGREES_LONGITUDE; - private static final String COORD_DECIMAL_LATITUDE = - "[+-]?" - + "[1-9]?[0-9]" + "(\\.[0-9]+)" - + "(\u00B0)?"; - private static final String COORD_DECIMAL_LONGITUDE = - "[+-]?" - + "(1)?[0-9]?[0-9]" + "(\\.[0-9]+)" - + "(\u00B0)?"; - private static final String COORD_DECIMAL_PATTERN = - COORD_DECIMAL_LATITUDE - + "(\\s)*" + "," + "(\\s)*" - + COORD_DECIMAL_LONGITUDE; - private static final Pattern COORD_PATTERN = - Pattern.compile(COORD_DEGREES_PATTERN + "|" + COORD_DECIMAL_PATTERN); - - private static final String NANP_ALLOWED_SYMBOLS = "()+-*#."; - private static final int NANP_MIN_DIGITS = 7; - private static final int NANP_MAX_DIGITS = 11; - - - /** - * Returns whether the SDK is the Jellybean release or later. - */ - public static boolean isJellybeanOrLater() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; - } - - /** - * Returns whether the SDK is the KeyLimePie release or later. - */ - public static boolean isKeyLimePieOrLater() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - } - - public static int getViewTypeFromIntentAndSharedPref(Activity activity) { - Intent intent = activity.getIntent(); - Bundle extras = intent.getExtras(); - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(activity); - - if (TextUtils.equals(intent.getAction(), Intent.ACTION_EDIT)) { - return ViewType.EDIT; - } - if (extras != null) { - if (extras.getBoolean(INTENT_KEY_DETAIL_VIEW, false)) { - // This is the "detail" view which is either agenda or day view - return prefs.getInt(GeneralPreferences.KEY_DETAILED_VIEW, - GeneralPreferences.DEFAULT_DETAILED_VIEW); - } else if (INTENT_VALUE_VIEW_TYPE_DAY.equals(extras.getString(INTENT_KEY_VIEW_TYPE))) { - // Not sure who uses this. This logic came from LaunchActivity - return ViewType.DAY; - } - } - - // Default to the last view - return prefs.getInt( - GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW); - } - - /** - * Gets the intent action for telling the widget to update. - */ - public static String getWidgetUpdateAction(Context context) { - return context.getPackageName() + ".APPWIDGET_UPDATE"; - } - - /** - * Gets the intent action for telling the widget to update. - */ - public static String getWidgetScheduledUpdateAction(Context context) { - return context.getPackageName() + ".APPWIDGET_SCHEDULED_UPDATE"; - } - - /** - * 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 - * **tbd** 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 **tbd** - */ - public static void setTimeZone(Context context, String timeZone) { - mTZUtils.setTimeZone(context, timeZone); - } - - /** - * 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 static String getTimeZone(Context context, Runnable callback) { - return mTZUtils.getTimeZone(context, callback); - } - - /** - * Formats a date or a time range according to the local conventions. - * - * @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 static String formatDateRange( - Context context, long startMillis, long endMillis, int flags) { - return mTZUtils.formatDateRange(context, startMillis, endMillis, flags); - } - - public static boolean getDefaultVibrate(Context context, SharedPreferences prefs) { - boolean vibrate; - if (prefs.contains(KEY_ALERTS_VIBRATE_WHEN)) { - // Migrate setting to new 4.2 behavior - // - // silent and never -> off - // always -> on - String vibrateWhen = prefs.getString(KEY_ALERTS_VIBRATE_WHEN, null); - vibrate = vibrateWhen != null && vibrateWhen.equals(context - .getString(R.string.prefDefault_alerts_vibrate_true)); - prefs.edit().remove(KEY_ALERTS_VIBRATE_WHEN).commit(); - Log.d(TAG, "Migrating KEY_ALERTS_VIBRATE_WHEN(" + vibrateWhen - + ") to KEY_ALERTS_VIBRATE = " + vibrate); - } else { - vibrate = prefs.getBoolean(GeneralPreferences.KEY_ALERTS_VIBRATE, - false); - } - return vibrate; - } - - public static String[] getSharedPreference(Context context, String key, String[] defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - Set<String> ss = prefs.getStringSet(key, null); - if (ss != null) { - String strings[] = new String[ss.size()]; - return ss.toArray(strings); - } - return defaultValue; - } - - public static String getSharedPreference(Context context, String key, String defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getString(key, defaultValue); - } - - public static int getSharedPreference(Context context, String key, int defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getInt(key, defaultValue); - } - - public static boolean getSharedPreference(Context context, String key, boolean defaultValue) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean(key, defaultValue); - } - - /** - * Asynchronously sets the preference with the given key to the given value - * - * @param context the context to use to get preferences from - * @param key the key of the preference to set - * @param value the value to set - */ - public static void setSharedPreference(Context context, String key, String value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - prefs.edit().putString(key, value).apply(); - } - - public static void setSharedPreference(Context context, String key, String[] values) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - LinkedHashSet<String> set = new LinkedHashSet<String>(); - for (String value : values) { - set.add(value); - } - prefs.edit().putStringSet(key, set).apply(); - } - - protected static void tardis() { - mTardis = System.currentTimeMillis(); - } - - protected static long getTardis() { - return mTardis; - } - - public static void setSharedPreference(Context context, String key, boolean value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(key, value); - editor.apply(); - } - - static void setSharedPreference(Context context, String key, int value) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - editor.putInt(key, value); - editor.apply(); - } - - public static void removeSharedPreference(Context context, String key) { - SharedPreferences prefs = context.getSharedPreferences( - GeneralPreferences.SHARED_PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().remove(key).apply(); - } - - /** - * Save default agenda/day/week/month view for next time - * - * @param context - * @param viewId {@link CalendarController.ViewType} - */ - static void setDefaultView(Context context, int viewId) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - SharedPreferences.Editor editor = prefs.edit(); - - boolean validDetailView = false; - if (mAllowWeekForDetailView && viewId == CalendarController.ViewType.WEEK) { - validDetailView = true; - } else { - validDetailView = viewId == CalendarController.ViewType.AGENDA - || viewId == CalendarController.ViewType.DAY; - } - - if (validDetailView) { - // Record the detail start view - editor.putInt(GeneralPreferences.KEY_DETAILED_VIEW, viewId); - } - - // Record the (new) start view - editor.putInt(GeneralPreferences.KEY_START_VIEW, viewId); - editor.apply(); - } - - public static MatrixCursor matrixCursorFromCursor(Cursor cursor) { - if (cursor == null) { - return null; - } - - String[] columnNames = cursor.getColumnNames(); - if (columnNames == null) { - columnNames = new String[] {}; - } - MatrixCursor newCursor = new MatrixCursor(columnNames); - int numColumns = cursor.getColumnCount(); - String data[] = new String[numColumns]; - cursor.moveToPosition(-1); - while (cursor.moveToNext()) { - for (int i = 0; i < numColumns; i++) { - data[i] = cursor.getString(i); - } - newCursor.addRow(data); - } - return newCursor; - } - - /** - * Compares two cursors to see if they contain the same data. - * - * @return Returns true of the cursors contain the same data and are not - * null, false otherwise - */ - public static boolean compareCursors(Cursor c1, Cursor c2) { - if (c1 == null || c2 == null) { - return false; - } - - int numColumns = c1.getColumnCount(); - if (numColumns != c2.getColumnCount()) { - return false; - } - - if (c1.getCount() != c2.getCount()) { - return false; - } - - c1.moveToPosition(-1); - c2.moveToPosition(-1); - while (c1.moveToNext() && c2.moveToNext()) { - for (int i = 0; i < numColumns; i++) { - if (!TextUtils.equals(c1.getString(i), c2.getString(i))) { - return false; - } - } - } - - return true; - } - - /** - * If the given intent specifies a time (in milliseconds since the epoch), - * then that time is returned. Otherwise, the current time is returned. - */ - public static final long timeFromIntentInMillis(Intent intent) { - // If the time was specified, then use that. Otherwise, use the current - // time. - Uri data = intent.getData(); - long millis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1); - if (millis == -1 && data != null && data.isHierarchical()) { - List<String> path = data.getPathSegments(); - if (path.size() == 2 && path.get(0).equals("time")) { - try { - millis = Long.valueOf(data.getLastPathSegment()); - } catch (NumberFormatException e) { - Log.i("Calendar", "timeFromIntentInMillis: Data existed but no valid time " - + "found. Using current time."); - } - } - } - if (millis <= 0) { - millis = System.currentTimeMillis(); - } - return millis; - } - - /** - * Formats the given Time object so that it gives the month and year (for - * example, "September 2007"). - * - * @param time the time to format - * @return the string containing the weekday and the date - */ - public static String formatMonthYear(Context context, Time time) { - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR; - long millis = time.toMillis(true); - return formatDateRange(context, millis, millis, flags); - } - - /** - * Returns a list joined together by the provided delimiter, for example, - * ["a", "b", "c"] could be joined into "a,b,c" - * - * @param things the things to join together - * @param delim the delimiter to use - * @return a string contained the things joined together - */ - public static String join(List<?> things, String delim) { - StringBuilder builder = new StringBuilder(); - boolean first = true; - for (Object thing : things) { - if (first) { - first = false; - } else { - builder.append(delim); - } - builder.append(thing.toString()); - } - return builder.toString(); - } - - /** - * Returns the week since {@link Time#EPOCH_JULIAN_DAY} (Jan 1, 1970) - * adjusted for first day of week. - * - * This takes a julian day and the week start day and calculates which - * week since {@link Time#EPOCH_JULIAN_DAY} that day occurs in, starting - * at 0. *Do not* use this to compute the ISO week number for the year. - * - * @param julianDay The julian day to calculate the week number for - * @param firstDayOfWeek Which week day is the first day of the week, - * see {@link Time#SUNDAY} - * @return Weeks since the epoch - */ - public static int getWeeksSinceEpochFromJulianDay(int julianDay, int firstDayOfWeek) { - int diff = Time.THURSDAY - firstDayOfWeek; - if (diff < 0) { - diff += 7; - } - int refDay = Time.EPOCH_JULIAN_DAY - diff; - return (julianDay - refDay) / 7; - } - - /** - * Takes a number of weeks since the epoch and calculates the Julian day of - * the Monday for that week. - * - * This assumes that the week containing the {@link Time#EPOCH_JULIAN_DAY} - * is considered week 0. It returns the Julian day for the Monday - * {@code week} weeks after the Monday of the week containing the epoch. - * - * @param week Number of weeks since the epoch - * @return The julian day for the Monday of the given week since the epoch - */ - public static int getJulianMondayFromWeeksSinceEpoch(int week) { - return MONDAY_BEFORE_JULIAN_EPOCH + week * 7; - } - - /** - * Get first day of week as android.text.format.Time constant. - * - * @return the first day of week in android.text.format.Time - */ - public static int getFirstDayOfWeek(Context context) { - SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - String pref = prefs.getString( - GeneralPreferences.KEY_WEEK_START_DAY, GeneralPreferences.WEEK_START_DEFAULT); - - int startDay; - if (GeneralPreferences.WEEK_START_DEFAULT.equals(pref)) { - startDay = Calendar.getInstance().getFirstDayOfWeek(); - } else { - startDay = Integer.parseInt(pref); - } - - if (startDay == Calendar.SATURDAY) { - return Time.SATURDAY; - } else if (startDay == Calendar.MONDAY) { - return Time.MONDAY; - } else { - return Time.SUNDAY; - } - } - - /** - * Get first day of week as java.util.Calendar constant. - * - * @return the first day of week as a java.util.Calendar constant - */ - public static int getFirstDayOfWeekAsCalendar(Context context) { - return convertDayOfWeekFromTimeToCalendar(getFirstDayOfWeek(context)); - } - - /** - * Converts the day of the week from android.text.format.Time to java.util.Calendar - */ - public static int convertDayOfWeekFromTimeToCalendar(int timeDayOfWeek) { - switch (timeDayOfWeek) { - case Time.MONDAY: - return Calendar.MONDAY; - case Time.TUESDAY: - return Calendar.TUESDAY; - case Time.WEDNESDAY: - return Calendar.WEDNESDAY; - case Time.THURSDAY: - return Calendar.THURSDAY; - case Time.FRIDAY: - return Calendar.FRIDAY; - case Time.SATURDAY: - return Calendar.SATURDAY; - case Time.SUNDAY: - return Calendar.SUNDAY; - default: - throw new IllegalArgumentException("Argument must be between Time.SUNDAY and " + - "Time.SATURDAY"); - } - } - - /** - * @return true when week number should be shown. - */ - public static boolean getShowWeekNumber(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean( - GeneralPreferences.KEY_SHOW_WEEK_NUM, GeneralPreferences.DEFAULT_SHOW_WEEK_NUM); - } - - /** - * @return true when declined events should be hidden. - */ - public static boolean getHideDeclinedEvents(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getBoolean(GeneralPreferences.KEY_HIDE_DECLINED, false); - } - - public static int getDaysPerWeek(Context context) { - final SharedPreferences prefs = GeneralPreferences.getSharedPreferences(context); - return prefs.getInt(GeneralPreferences.KEY_DAYS_PER_WEEK, 7); - } - - /** - * Determine whether the column position is Saturday or not. - * - * @param column the column position - * @param firstDayOfWeek the first day of week in android.text.format.Time - * @return true if the column is Saturday position - */ - public static boolean isSaturday(int column, int firstDayOfWeek) { - return (firstDayOfWeek == Time.SUNDAY && column == 6) - || (firstDayOfWeek == Time.MONDAY && column == 5) - || (firstDayOfWeek == Time.SATURDAY && column == 0); - } - - /** - * Determine whether the column position is Sunday or not. - * - * @param column the column position - * @param firstDayOfWeek the first day of week in android.text.format.Time - * @return true if the column is Sunday position - */ - public static boolean isSunday(int column, int firstDayOfWeek) { - return (firstDayOfWeek == Time.SUNDAY && column == 0) - || (firstDayOfWeek == Time.MONDAY && column == 6) - || (firstDayOfWeek == Time.SATURDAY && column == 1); - } - - /** - * Convert given UTC time into current local time. This assumes it is for an - * allday event and will adjust the time to be on a midnight boundary. - * - * @param recycle Time object to recycle, otherwise null. - * @param utcTime Time to convert, in UTC. - * @param tz The time zone to convert this time to. - */ - public static long convertAlldayUtcToLocal(Time recycle, long utcTime, String tz) { - if (recycle == null) { - recycle = new Time(); - } - recycle.timezone = Time.TIMEZONE_UTC; - recycle.set(utcTime); - recycle.timezone = tz; - return recycle.normalize(true); - } - - public static long convertAlldayLocalToUTC(Time recycle, long localTime, String tz) { - if (recycle == null) { - recycle = new Time(); - } - recycle.timezone = tz; - recycle.set(localTime); - recycle.timezone = Time.TIMEZONE_UTC; - return recycle.normalize(true); - } - - /** - * Finds and returns the next midnight after "theTime" in milliseconds UTC - * - * @param recycle - Time object to recycle, otherwise null. - * @param theTime - Time used for calculations (in UTC) - * @param tz The time zone to convert this time to. - */ - public static long getNextMidnight(Time recycle, long theTime, String tz) { - if (recycle == null) { - recycle = new Time(); - } - recycle.timezone = tz; - recycle.set(theTime); - recycle.monthDay ++; - recycle.hour = 0; - recycle.minute = 0; - recycle.second = 0; - return recycle.normalize(true); - } - - public static void setAllowWeekForDetailView(boolean allowWeekView) { - mAllowWeekForDetailView = allowWeekView; - } - - public static boolean getAllowWeekForDetailView() { - return mAllowWeekForDetailView; - } - - public static boolean getConfigBool(Context c, int key) { - return c.getResources().getBoolean(key); - } - - /** - * For devices with Jellybean or later, darkens the given color to ensure that white text is - * clearly visible on top of it. For devices prior to Jellybean, does nothing, as the - * sync adapter handles the color change. - * - * @param color - */ - public static int getDisplayColorFromColor(int color) { - if (!isJellybeanOrLater()) { - return color; - } - - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[1] = Math.min(hsv[1] * SATURATION_ADJUST, 1.0f); - hsv[2] = hsv[2] * INTENSITY_ADJUST; - return Color.HSVToColor(hsv); - } - - // This takes a color and computes what it would look like blended with - // white. The result is the color that should be used for declined events. - public static int getDeclinedColorFromColor(int color) { - int bg = 0xffffffff; - int a = DECLINED_EVENT_ALPHA; - int r = (((color & 0x00ff0000) * a) + ((bg & 0x00ff0000) * (0xff - a))) & 0xff000000; - int g = (((color & 0x0000ff00) * a) + ((bg & 0x0000ff00) * (0xff - a))) & 0x00ff0000; - int b = (((color & 0x000000ff) * a) + ((bg & 0x000000ff) * (0xff - a))) & 0x0000ff00; - return (0xff000000) | ((r | g | b) >> 8); - } - - public static void trySyncAndDisableUpgradeReceiver(Context context) { - final PackageManager pm = context.getPackageManager(); - ComponentName upgradeComponent = new ComponentName(context, UpgradeReceiver.class); - if (pm.getComponentEnabledSetting(upgradeComponent) == - PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { - // The upgrade receiver has been disabled, which means this code has been run before, - // so no need to sync. - return; - } - - Bundle extras = new Bundle(); - extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); - ContentResolver.requestSync( - null /* no account */, - Calendars.CONTENT_URI.getAuthority(), - extras); - - // Now unregister the receiver so that we won't continue to sync every time. - pm.setComponentEnabledSetting(upgradeComponent, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); - } - - // A single strand represents one color of events. Events are divided up by - // color to make them convenient to draw. The black strand is special in - // that it holds conflicting events as well as color settings for allday on - // each day. - public static class DNAStrand { - public float[] points; - public int[] allDays; // color for the allday, 0 means no event - int position; - public int color; - int count; - } - - // A segment is a single continuous length of time occupied by a single - // color. Segments should never span multiple days. - private static class DNASegment { - int startMinute; // in minutes since the start of the week - int endMinute; - int color; // Calendar color or black for conflicts - int day; // quick reference to the day this segment is on - } - - /** - * Converts a list of events to a list of segments to draw. Assumes list is - * ordered by start time of the events. The function processes events for a - * range of days from firstJulianDay to firstJulianDay + dayXs.length - 1. - * The algorithm goes over all the events and creates a set of segments - * ordered by start time. This list of segments is then converted into a - * HashMap of strands which contain the draw points and are organized by - * color. The strands can then be drawn by setting the paint color to each - * strand's color and calling drawLines on its set of points. The points are - * set up using the following parameters. - * <ul> - * <li>Events between midnight and WORK_DAY_START_MINUTES are compressed - * into the first 1/8th of the space between top and bottom.</li> - * <li>Events between WORK_DAY_END_MINUTES and the following midnight are - * compressed into the last 1/8th of the space between top and bottom</li> - * <li>Events between WORK_DAY_START_MINUTES and WORK_DAY_END_MINUTES use - * the remaining 3/4ths of the space</li> - * <li>All segments drawn will maintain at least minPixels height, except - * for conflicts in the first or last 1/8th, which may be smaller</li> - * </ul> - * - * @param firstJulianDay The julian day of the first day of events - * @param events A list of events sorted by start time - * @param top The lowest y value the dna should be drawn at - * @param bottom The highest y value the dna should be drawn at - * @param dayXs An array of x values to draw the dna at, one for each day - * @param conflictColor the color to use for conflicts - * @return - */ - public static HashMap<Integer, DNAStrand> createDNAStrands(int firstJulianDay, - ArrayList<Event> events, int top, int bottom, int minPixels, int[] dayXs, - Context context) { - - if (!mMinutesLoaded) { - if (context == null) { - Log.wtf(TAG, "No context and haven't loaded parameters yet! Can't create DNA."); - } - Resources res = context.getResources(); - CONFLICT_COLOR = res.getColor(R.color.month_dna_conflict_time_color); - WORK_DAY_START_MINUTES = res.getInteger(R.integer.work_start_minutes); - WORK_DAY_END_MINUTES = res.getInteger(R.integer.work_end_minutes); - WORK_DAY_END_LENGTH = DAY_IN_MINUTES - WORK_DAY_END_MINUTES; - WORK_DAY_MINUTES = WORK_DAY_END_MINUTES - WORK_DAY_START_MINUTES; - mMinutesLoaded = true; - } - - if (events == null || events.isEmpty() || dayXs == null || dayXs.length < 1 - || bottom - top < 8 || minPixels < 0) { - Log.e(TAG, - "Bad values for createDNAStrands! events:" + events + " dayXs:" - + Arrays.toString(dayXs) + " bot-top:" + (bottom - top) + " minPixels:" - + minPixels); - return null; - } - - LinkedList<DNASegment> segments = new LinkedList<DNASegment>(); - HashMap<Integer, DNAStrand> strands = new HashMap<Integer, DNAStrand>(); - // add a black strand by default, other colors will get added in - // the loop - DNAStrand blackStrand = new DNAStrand(); - blackStrand.color = CONFLICT_COLOR; - strands.put(CONFLICT_COLOR, blackStrand); - // the min length is the number of minutes that will occupy - // MIN_SEGMENT_PIXELS in the 'work day' time slot. This computes the - // minutes/pixel * minpx where the number of pixels are 3/4 the total - // dna height: 4*(mins/(px * 3/4)) - int minMinutes = minPixels * 4 * WORK_DAY_MINUTES / (3 * (bottom - top)); - - // There are slightly fewer than half as many pixels in 1/6 the space, - // so round to 2.5x for the min minutes in the non-work area - int minOtherMinutes = minMinutes * 5 / 2; - int lastJulianDay = firstJulianDay + dayXs.length - 1; - - Event event = new Event(); - // Go through all the events for the week - for (Event currEvent : events) { - // if this event is outside the weeks range skip it - if (currEvent.endDay < firstJulianDay || currEvent.startDay > lastJulianDay) { - continue; - } - if (currEvent.drawAsAllday()) { - addAllDayToStrands(currEvent, strands, firstJulianDay, dayXs.length); - continue; - } - // Copy the event over so we can clip its start and end to our range - currEvent.copyTo(event); - if (event.startDay < firstJulianDay) { - event.startDay = firstJulianDay; - event.startTime = 0; - } - // If it starts after the work day make sure the start is at least - // minPixels from midnight - if (event.startTime > DAY_IN_MINUTES - minOtherMinutes) { - event.startTime = DAY_IN_MINUTES - minOtherMinutes; - } - if (event.endDay > lastJulianDay) { - event.endDay = lastJulianDay; - event.endTime = DAY_IN_MINUTES - 1; - } - // If the end time is before the work day make sure it ends at least - // minPixels after midnight - if (event.endTime < minOtherMinutes) { - event.endTime = minOtherMinutes; - } - // If the start and end are on the same day make sure they are at - // least minPixels apart. This only needs to be done for times - // outside the work day as the min distance for within the work day - // is enforced in the segment code. - if (event.startDay == event.endDay && - event.endTime - event.startTime < minOtherMinutes) { - // If it's less than minPixels in an area before the work - // day - if (event.startTime < WORK_DAY_START_MINUTES) { - // extend the end to the first easy guarantee that it's - // minPixels - event.endTime = Math.min(event.startTime + minOtherMinutes, - WORK_DAY_START_MINUTES + minMinutes); - // if it's in the area after the work day - } else if (event.endTime > WORK_DAY_END_MINUTES) { - // First try shifting the end but not past midnight - event.endTime = Math.min(event.endTime + minOtherMinutes, DAY_IN_MINUTES - 1); - // if it's still too small move the start back - if (event.endTime - event.startTime < minOtherMinutes) { - event.startTime = event.endTime - minOtherMinutes; - } - } - } - - // This handles adding the first segment - if (segments.size() == 0) { - addNewSegment(segments, event, strands, firstJulianDay, 0, minMinutes); - continue; - } - // Now compare our current start time to the end time of the last - // segment in the list - DNASegment lastSegment = segments.getLast(); - int startMinute = (event.startDay - firstJulianDay) * DAY_IN_MINUTES + event.startTime; - int endMinute = Math.max((event.endDay - firstJulianDay) * DAY_IN_MINUTES - + event.endTime, startMinute + minMinutes); - - if (startMinute < 0) { - startMinute = 0; - } - if (endMinute >= WEEK_IN_MINUTES) { - endMinute = WEEK_IN_MINUTES - 1; - } - // If we start before the last segment in the list ends we need to - // start going through the list as this may conflict with other - // events - if (startMinute < lastSegment.endMinute) { - int i = segments.size(); - // find the last segment this event intersects with - while (--i >= 0 && endMinute < segments.get(i).startMinute); - - DNASegment currSegment; - // for each segment this event intersects with - for (; i >= 0 && startMinute <= (currSegment = segments.get(i)).endMinute; i--) { - // if the segment is already a conflict ignore it - if (currSegment.color == CONFLICT_COLOR) { - continue; - } - // if the event ends before the segment and wouldn't create - // a segment that is too small split off the right side - if (endMinute < currSegment.endMinute - minMinutes) { - DNASegment rhs = new DNASegment(); - rhs.endMinute = currSegment.endMinute; - rhs.color = currSegment.color; - rhs.startMinute = endMinute + 1; - rhs.day = currSegment.day; - currSegment.endMinute = endMinute; - segments.add(i + 1, rhs); - strands.get(rhs.color).count++; - if (DEBUG) { - Log.d(TAG, "Added rhs, curr:" + currSegment.toString() + " i:" - + segments.get(i).toString()); - } - } - // if the event starts after the segment and wouldn't create - // a segment that is too small split off the left side - if (startMinute > currSegment.startMinute + minMinutes) { - DNASegment lhs = new DNASegment(); - lhs.startMinute = currSegment.startMinute; - lhs.color = currSegment.color; - lhs.endMinute = startMinute - 1; - lhs.day = currSegment.day; - currSegment.startMinute = startMinute; - // increment i so that we are at the right position when - // referencing the segments to the right and left of the - // current segment. - segments.add(i++, lhs); - strands.get(lhs.color).count++; - if (DEBUG) { - Log.d(TAG, "Added lhs, curr:" + currSegment.toString() + " i:" - + segments.get(i).toString()); - } - } - // if the right side is black merge this with the segment to - // the right if they're on the same day and overlap - if (i + 1 < segments.size()) { - DNASegment rhs = segments.get(i + 1); - if (rhs.color == CONFLICT_COLOR && currSegment.day == rhs.day - && rhs.startMinute <= currSegment.endMinute + 1) { - rhs.startMinute = Math.min(currSegment.startMinute, rhs.startMinute); - segments.remove(currSegment); - strands.get(currSegment.color).count--; - // point at the new current segment - currSegment = rhs; - } - } - // if the left side is black merge this with the segment to - // the left if they're on the same day and overlap - if (i - 1 >= 0) { - DNASegment lhs = segments.get(i - 1); - if (lhs.color == CONFLICT_COLOR && currSegment.day == lhs.day - && lhs.endMinute >= currSegment.startMinute - 1) { - lhs.endMinute = Math.max(currSegment.endMinute, lhs.endMinute); - segments.remove(currSegment); - strands.get(currSegment.color).count--; - // point at the new current segment - currSegment = lhs; - // point i at the new current segment in case new - // code is added - i--; - } - } - // if we're still not black, decrement the count for the - // color being removed, change this to black, and increment - // the black count - if (currSegment.color != CONFLICT_COLOR) { - strands.get(currSegment.color).count--; - currSegment.color = CONFLICT_COLOR; - strands.get(CONFLICT_COLOR).count++; - } - } - - } - // If this event extends beyond the last segment add a new segment - if (endMinute > lastSegment.endMinute) { - addNewSegment(segments, event, strands, firstJulianDay, lastSegment.endMinute, - minMinutes); - } - } - weaveDNAStrands(segments, firstJulianDay, strands, top, bottom, dayXs); - return strands; - } - - // This figures out allDay colors as allDay events are found - private static void addAllDayToStrands(Event event, HashMap<Integer, DNAStrand> strands, - int firstJulianDay, int numDays) { - DNAStrand strand = getOrCreateStrand(strands, CONFLICT_COLOR); - // if we haven't initialized the allDay portion create it now - if (strand.allDays == null) { - strand.allDays = new int[numDays]; - } - - // For each day this event is on update the color - int end = Math.min(event.endDay - firstJulianDay, numDays - 1); - for (int i = Math.max(event.startDay - firstJulianDay, 0); i <= end; i++) { - if (strand.allDays[i] != 0) { - // if this day already had a color, it is now a conflict - strand.allDays[i] = CONFLICT_COLOR; - } else { - // else it's just the color of the event - strand.allDays[i] = event.color; - } - } - } - - // This processes all the segments, sorts them by color, and generates a - // list of points to draw - private static void weaveDNAStrands(LinkedList<DNASegment> segments, int firstJulianDay, - HashMap<Integer, DNAStrand> strands, int top, int bottom, int[] dayXs) { - // First, get rid of any colors that ended up with no segments - Iterator<DNAStrand> strandIterator = strands.values().iterator(); - while (strandIterator.hasNext()) { - DNAStrand strand = strandIterator.next(); - if (strand.count < 1 && strand.allDays == null) { - strandIterator.remove(); - continue; - } - strand.points = new float[strand.count * 4]; - strand.position = 0; - } - // Go through each segment and compute its points - for (DNASegment segment : segments) { - // Add the points to the strand of that color - DNAStrand strand = strands.get(segment.color); - int dayIndex = segment.day - firstJulianDay; - int dayStartMinute = segment.startMinute % DAY_IN_MINUTES; - int dayEndMinute = segment.endMinute % DAY_IN_MINUTES; - int height = bottom - top; - int workDayHeight = height * 3 / 4; - int remainderHeight = (height - workDayHeight) / 2; - - int x = dayXs[dayIndex]; - int y0 = 0; - int y1 = 0; - - y0 = top + getPixelOffsetFromMinutes(dayStartMinute, workDayHeight, remainderHeight); - y1 = top + getPixelOffsetFromMinutes(dayEndMinute, workDayHeight, remainderHeight); - if (DEBUG) { - Log.d(TAG, "Adding " + Integer.toHexString(segment.color) + " at x,y0,y1: " + x - + " " + y0 + " " + y1 + " for " + dayStartMinute + " " + dayEndMinute); - } - strand.points[strand.position++] = x; - strand.points[strand.position++] = y0; - strand.points[strand.position++] = x; - strand.points[strand.position++] = y1; - } - } - - /** - * Compute a pixel offset from the top for a given minute from the work day - * height and the height of the top area. - */ - private static int getPixelOffsetFromMinutes(int minute, int workDayHeight, - int remainderHeight) { - int y; - if (minute < WORK_DAY_START_MINUTES) { - y = minute * remainderHeight / WORK_DAY_START_MINUTES; - } else if (minute < WORK_DAY_END_MINUTES) { - y = remainderHeight + (minute - WORK_DAY_START_MINUTES) * workDayHeight - / WORK_DAY_MINUTES; - } else { - y = remainderHeight + workDayHeight + (minute - WORK_DAY_END_MINUTES) * remainderHeight - / WORK_DAY_END_LENGTH; - } - return y; - } - - /** - * Add a new segment based on the event provided. This will handle splitting - * segments across day boundaries and ensures a minimum size for segments. - */ - private static void addNewSegment(LinkedList<DNASegment> segments, Event event, - HashMap<Integer, DNAStrand> strands, int firstJulianDay, int minStart, int minMinutes) { - if (event.startDay > event.endDay) { - Log.wtf(TAG, "Event starts after it ends: " + event.toString()); - } - // If this is a multiday event split it up by day - if (event.startDay != event.endDay) { - Event lhs = new Event(); - lhs.color = event.color; - lhs.startDay = event.startDay; - // the first day we want the start time to be the actual start time - lhs.startTime = event.startTime; - lhs.endDay = lhs.startDay; - lhs.endTime = DAY_IN_MINUTES - 1; - // Nearly recursive iteration! - while (lhs.startDay != event.endDay) { - addNewSegment(segments, lhs, strands, firstJulianDay, minStart, minMinutes); - // The days in between are all day, even though that shouldn't - // actually happen due to the allday filtering - lhs.startDay++; - lhs.endDay = lhs.startDay; - lhs.startTime = 0; - minStart = 0; - } - // The last day we want the end time to be the actual end time - lhs.endTime = event.endTime; - event = lhs; - } - // Create the new segment and compute its fields - DNASegment segment = new DNASegment(); - int dayOffset = (event.startDay - firstJulianDay) * DAY_IN_MINUTES; - int endOfDay = dayOffset + DAY_IN_MINUTES - 1; - // clip the start if needed - segment.startMinute = Math.max(dayOffset + event.startTime, minStart); - // and extend the end if it's too small, but not beyond the end of the - // day - int minEnd = Math.min(segment.startMinute + minMinutes, endOfDay); - segment.endMinute = Math.max(dayOffset + event.endTime, minEnd); - if (segment.endMinute > endOfDay) { - segment.endMinute = endOfDay; - } - - segment.color = event.color; - segment.day = event.startDay; - segments.add(segment); - // increment the count for the correct color or add a new strand if we - // don't have that color yet - DNAStrand strand = getOrCreateStrand(strands, segment.color); - strand.count++; - } - - /** - * Try to get a strand of the given color. Create it if it doesn't exist. - */ - private static DNAStrand getOrCreateStrand(HashMap<Integer, DNAStrand> strands, int color) { - DNAStrand strand = strands.get(color); - if (strand == null) { - strand = new DNAStrand(); - strand.color = color; - strand.count = 0; - strands.put(strand.color, strand); - } - return strand; - } - - /** - * Sends an intent to launch the top level Calendar view. - * - * @param context - */ - public static void returnToCalendarHome(Context context) { - Intent launchIntent = new Intent(context, AllInOneActivity.class); - launchIntent.setAction(Intent.ACTION_DEFAULT); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - launchIntent.putExtra(INTENT_KEY_HOME, true); - context.startActivity(launchIntent); - } - - /** - * Given a context and a time in millis since unix epoch figures out the - * correct week of the year for that time. - * - * @param millisSinceEpoch - * @return - */ - public static int getWeekNumberFromTime(long millisSinceEpoch, Context context) { - Time weekTime = new Time(getTimeZone(context, null)); - weekTime.set(millisSinceEpoch); - weekTime.normalize(true); - int firstDayOfWeek = getFirstDayOfWeek(context); - // if the date is on Saturday or Sunday and the start of the week - // isn't Monday we may need to shift the date to be in the correct - // week - if (weekTime.weekDay == Time.SUNDAY - && (firstDayOfWeek == Time.SUNDAY || firstDayOfWeek == Time.SATURDAY)) { - weekTime.monthDay++; - weekTime.normalize(true); - } else if (weekTime.weekDay == Time.SATURDAY && firstDayOfWeek == Time.SATURDAY) { - weekTime.monthDay += 2; - weekTime.normalize(true); - } - return weekTime.getWeekNumber(); - } - - /** - * Formats a day of the week string. This is either just the name of the day - * or a combination of yesterday/today/tomorrow and the day of the week. - * - * @param julianDay The julian day to get the string for - * @param todayJulianDay The julian day for today's date - * @param millis A utc millis since epoch time that falls on julian day - * @param context The calling context, used to get the timezone and do the - * formatting - * @return - */ - public static String getDayOfWeekString(int julianDay, int todayJulianDay, long millis, - Context context) { - getTimeZone(context, null); - int flags = DateUtils.FORMAT_SHOW_WEEKDAY; - String dayViewText; - if (julianDay == todayJulianDay) { - dayViewText = context.getString(R.string.agenda_today, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); - } else if (julianDay == todayJulianDay - 1) { - dayViewText = context.getString(R.string.agenda_yesterday, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); - } else if (julianDay == todayJulianDay + 1) { - dayViewText = context.getString(R.string.agenda_tomorrow, - mTZUtils.formatDateRange(context, millis, millis, flags).toString()); - } else { - dayViewText = mTZUtils.formatDateRange(context, millis, millis, flags).toString(); - } - dayViewText = dayViewText.toUpperCase(); - return dayViewText; - } - - // Calculate the time until midnight + 1 second and set the handler to - // do run the runnable - public static void setMidnightUpdater(Handler h, Runnable r, String timezone) { - if (h == null || r == null || timezone == null) { - return; - } - long now = System.currentTimeMillis(); - Time time = new Time(timezone); - time.set(now); - long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 - - time.second + 1) * 1000; - h.removeCallbacks(r); - h.postDelayed(r, runInMillis); - } - - // Stop the midnight update thread - public static void resetMidnightUpdater(Handler h, Runnable r) { - if (h == null || r == null) { - return; - } - h.removeCallbacks(r); - } - - /** - * Returns a string description of the specified time interval. - */ - public static String getDisplayedDatetime(long startMillis, long endMillis, long currentMillis, - String localTimezone, boolean allDay, Context context) { - // Configure date/time formatting. - int flagsDate = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; - int flagsTime = DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(context)) { - flagsTime |= DateUtils.FORMAT_24HOUR; - } - - Time currentTime = new Time(localTimezone); - currentTime.set(currentMillis); - Resources resources = context.getResources(); - String datetimeString = null; - if (allDay) { - // All day events require special timezone adjustment. - long localStartMillis = convertAlldayUtcToLocal(null, startMillis, localTimezone); - long localEndMillis = convertAlldayUtcToLocal(null, endMillis, localTimezone); - if (singleDayEvent(localStartMillis, localEndMillis, currentTime.gmtoff)) { - // If possible, use "Today" or "Tomorrow" instead of a full date string. - int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), - localStartMillis, currentMillis, currentTime.gmtoff); - if (TODAY == todayOrTomorrow) { - datetimeString = resources.getString(R.string.today); - } else if (TOMORROW == todayOrTomorrow) { - datetimeString = resources.getString(R.string.tomorrow); - } - } - if (datetimeString == null) { - // For multi-day allday events or single-day all-day events that are not - // today or tomorrow, use framework formatter. - Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault()); - datetimeString = DateUtils.formatDateRange(context, f, startMillis, - endMillis, flagsDate, Time.TIMEZONE_UTC).toString(); - } - } else { - if (singleDayEvent(startMillis, endMillis, currentTime.gmtoff)) { - // Format the time. - String timeString = Utils.formatDateRange(context, startMillis, endMillis, - flagsTime); - - // If possible, use "Today" or "Tomorrow" instead of a full date string. - int todayOrTomorrow = isTodayOrTomorrow(context.getResources(), startMillis, - currentMillis, currentTime.gmtoff); - if (TODAY == todayOrTomorrow) { - // Example: "Today at 1:00pm - 2:00 pm" - datetimeString = resources.getString(R.string.today_at_time_fmt, - timeString); - } else if (TOMORROW == todayOrTomorrow) { - // Example: "Tomorrow at 1:00pm - 2:00 pm" - datetimeString = resources.getString(R.string.tomorrow_at_time_fmt, - timeString); - } else { - // Format the full date. Example: "Thursday, April 12, 1:00pm - 2:00pm" - String dateString = Utils.formatDateRange(context, startMillis, endMillis, - flagsDate); - datetimeString = resources.getString(R.string.date_time_fmt, dateString, - timeString); - } - } else { - // For multiday events, shorten day/month names. - // Example format: "Fri Apr 6, 5:00pm - Sun, Apr 8, 6:00pm" - int flagsDatetime = flagsDate | flagsTime | DateUtils.FORMAT_ABBREV_MONTH | - DateUtils.FORMAT_ABBREV_WEEKDAY; - datetimeString = Utils.formatDateRange(context, startMillis, endMillis, - flagsDatetime); - } - } - return datetimeString; - } - - /** - * Returns the timezone to display in the event info, if the local timezone is different - * from the event timezone. Otherwise returns null. - */ - public static String getDisplayedTimezone(long startMillis, String localTimezone, - String eventTimezone) { - String tzDisplay = null; - if (!TextUtils.equals(localTimezone, eventTimezone)) { - // Figure out if this is in DST - TimeZone tz = TimeZone.getTimeZone(localTimezone); - if (tz == null || tz.getID().equals("GMT")) { - tzDisplay = localTimezone; - } else { - Time startTime = new Time(localTimezone); - startTime.set(startMillis); - tzDisplay = tz.getDisplayName(startTime.isDst != 0, TimeZone.SHORT); - } - } - return tzDisplay; - } - - /** - * Returns whether the specified time interval is in a single day. - */ - private static boolean singleDayEvent(long startMillis, long endMillis, long localGmtOffset) { - if (startMillis == endMillis) { - return true; - } - - // An event ending at midnight should still be a single-day event, so check - // time end-1. - int startDay = Time.getJulianDay(startMillis, localGmtOffset); - int endDay = Time.getJulianDay(endMillis - 1, localGmtOffset); - return startDay == endDay; - } - - // Using int constants as a return value instead of an enum to minimize resources. - private static final int TODAY = 1; - private static final int TOMORROW = 2; - private static final int NONE = 0; - - /** - * Returns TODAY or TOMORROW if applicable. Otherwise returns NONE. - */ - private static int isTodayOrTomorrow(Resources r, long dayMillis, - long currentMillis, long localGmtOffset) { - int startDay = Time.getJulianDay(dayMillis, localGmtOffset); - int currentDay = Time.getJulianDay(currentMillis, localGmtOffset); - - int days = startDay - currentDay; - if (days == 1) { - return TOMORROW; - } else if (days == 0) { - return TODAY; - } else { - return NONE; - } - } - - /** - * Inserts a drawable with today's day into the today's icon in the option menu - * @param icon - today's icon from the options menu - */ - public static void setTodayIcon(LayerDrawable icon, Context c, String timezone) { - DayOfMonthDrawable today; - - // Reuse current drawable if possible - Drawable currentDrawable = icon.findDrawableByLayerId(R.id.today_icon_day); - if (currentDrawable != null && currentDrawable instanceof DayOfMonthDrawable) { - today = (DayOfMonthDrawable)currentDrawable; - } else { - today = new DayOfMonthDrawable(c); - } - // Set the day and update the icon - Time now = new Time(timezone); - now.setToNow(); - now.normalize(false); - today.setDayOfMonth(now.monthDay); - icon.mutate(); - icon.setDrawableByLayerId(R.id.today_icon_day, today); - } - - /** - * Get a list of quick responses used for emailing guests from the - * SharedPreferences. If not are found, get the hard coded ones that shipped - * with the app - * - * @param context - * @return a list of quick responses. - */ - public static String[] getQuickResponses(Context context) { - String[] s = Utils.getSharedPreference(context, KEY_QUICK_RESPONSES, (String[]) null); - - if (s == null) { - s = context.getResources().getStringArray(R.array.quick_response_defaults); - } - - return s; - } - - /** - * Return the app version code. - */ - public static String getVersionCode(Context context) { - if (sVersion == null) { - try { - sVersion = context.getPackageManager().getPackageInfo( - context.getPackageName(), 0).versionName; - } catch (PackageManager.NameNotFoundException e) { - // Can't find version; just leave it blank. - Log.e(TAG, "Error finding package " + context.getApplicationInfo().packageName); - } - } - return sVersion; - } -} diff --git a/src/com/android/calendar/alerts/AlarmManagerInterface.java b/src/com/android/calendar/alerts/AlarmManagerInterface.java deleted file mode 100644 index 3c66434d..00000000 --- a/src/com/android/calendar/alerts/AlarmManagerInterface.java +++ /dev/null @@ -1,26 +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.alerts; - -import android.app.PendingIntent; - -/** - * AlarmManager abstracted to an interface for testability. - */ -public interface AlarmManagerInterface { - public void set(int type, long triggerAtMillis, PendingIntent operation); -} diff --git a/src/com/android/calendar/alerts/AlarmScheduler.java b/src/com/android/calendar/alerts/AlarmScheduler.java deleted file mode 100644 index 97828229..00000000 --- a/src/com/android/calendar/alerts/AlarmScheduler.java +++ /dev/null @@ -1,322 +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.alerts; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Events; -import android.provider.CalendarContract.Instances; -import android.provider.CalendarContract.Reminders; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Schedules the next EVENT_REMINDER_APP broadcast with AlarmManager, by querying the events - * and reminders tables for the next upcoming alert. - */ -public class AlarmScheduler { - private static final String TAG = "AlarmScheduler"; - - private static final String INSTANCES_WHERE = Events.VISIBLE + "=? AND " - + Instances.BEGIN + ">=? AND " + Instances.BEGIN + "<=? AND " - + Events.ALL_DAY + "=?"; - static final String[] INSTANCES_PROJECTION = new String[] { - Instances.EVENT_ID, - Instances.BEGIN, - Instances.ALL_DAY, - }; - private static final int INSTANCES_INDEX_EVENTID = 0; - private static final int INSTANCES_INDEX_BEGIN = 1; - private static final int INSTANCES_INDEX_ALL_DAY = 2; - - private static final String REMINDERS_WHERE = Reminders.METHOD + "=1 AND " - + Reminders.EVENT_ID + " IN "; - static final String[] REMINDERS_PROJECTION = new String[] { - Reminders.EVENT_ID, - Reminders.MINUTES, - Reminders.METHOD, - }; - private static final int REMINDERS_INDEX_EVENT_ID = 0; - private static final int REMINDERS_INDEX_MINUTES = 1; - private static final int REMINDERS_INDEX_METHOD = 2; - - // Add a slight delay for the EVENT_REMINDER_APP broadcast for a couple reasons: - // (1) so that the concurrent reminder broadcast from the provider doesn't result - // in a double ring, and (2) some OEMs modified the provider to not add an alert to - // the CalendarAlerts table until the alert time, so for the unbundled app's - // notifications to work on these devices, a delay ensures that AlertService won't - // read from the CalendarAlerts table until the alert is present. - static final int ALARM_DELAY_MS = 1000; - - // The reminders query looks like "SELECT ... AND eventId IN 101,102,202,...". This - // sets the max # of events in the query before batching into multiple queries, to - // limit the SQL query length. - private static final int REMINDER_QUERY_BATCH_SIZE = 50; - - // We really need to query for reminder times that fall in some interval, but - // the Reminders table only stores the reminder interval (10min, 15min, etc), and - // we cannot do the join with the Events table to calculate the actual alert time - // from outside of the provider. So the best we can do for now consider events - // whose start times begin within some interval (ie. 1 week out). This means - // reminders which are configured for more than 1 week out won't fire on time. We - // can minimize this to being only 1 day late by putting a 1 day max on the alarm time. - private static final long EVENT_LOOKAHEAD_WINDOW_MS = DateUtils.WEEK_IN_MILLIS; - private static final long MAX_ALARM_ELAPSED_MS = DateUtils.DAY_IN_MILLIS; - - /** - * Schedules the nearest upcoming alarm, to refresh notifications. - * - * This is historically done in the provider but we dupe this here so the unbundled - * app will work on devices that have modified this portion of the provider. This - * has the limitation of querying events within some interval from now (ie. looks at - * reminders for all events occurring in the next week). This means for example, - * a 2 week notification will not fire on time. - */ - public static void scheduleNextAlarm(Context context) { - scheduleNextAlarm(context, AlertUtils.createAlarmManager(context), - REMINDER_QUERY_BATCH_SIZE, System.currentTimeMillis()); - } - - // VisibleForTesting - static void scheduleNextAlarm(Context context, AlarmManagerInterface alarmManager, - int batchSize, long currentMillis) { - Cursor instancesCursor = null; - try { - instancesCursor = queryUpcomingEvents(context, context.getContentResolver(), - currentMillis); - if (instancesCursor != null) { - queryNextReminderAndSchedule(instancesCursor, context, - context.getContentResolver(), alarmManager, batchSize, currentMillis); - } - } finally { - if (instancesCursor != null) { - instancesCursor.close(); - } - } - } - - /** - * Queries events starting within a fixed interval from now. - */ - private static Cursor queryUpcomingEvents(Context context, ContentResolver contentResolver, - long currentMillis) { - Time time = new Time(); - time.normalize(false); - long localOffset = time.gmtoff * 1000; - final long localStartMin = currentMillis; - final long localStartMax = localStartMin + EVENT_LOOKAHEAD_WINDOW_MS; - final long utcStartMin = localStartMin - localOffset; - final long utcStartMax = utcStartMin + EVENT_LOOKAHEAD_WINDOW_MS; - - // Expand Instances table range by a day on either end to account for - // all-day events. - Uri.Builder uriBuilder = Instances.CONTENT_URI.buildUpon(); - ContentUris.appendId(uriBuilder, localStartMin - DateUtils.DAY_IN_MILLIS); - ContentUris.appendId(uriBuilder, localStartMax + DateUtils.DAY_IN_MILLIS); - - // Build query for all events starting within the fixed interval. - StringBuilder queryBuilder = new StringBuilder(); - queryBuilder.append("("); - queryBuilder.append(INSTANCES_WHERE); - queryBuilder.append(") OR ("); - queryBuilder.append(INSTANCES_WHERE); - queryBuilder.append(")"); - String[] queryArgs = new String[] { - // allday selection - "1", /* visible = ? */ - String.valueOf(utcStartMin), /* begin >= ? */ - String.valueOf(utcStartMax), /* begin <= ? */ - "1", /* allDay = ? */ - - // non-allday selection - "1", /* visible = ? */ - String.valueOf(localStartMin), /* begin >= ? */ - String.valueOf(localStartMax), /* begin <= ? */ - "0" /* allDay = ? */ - }; - - Cursor cursor = contentResolver.query(uriBuilder.build(), INSTANCES_PROJECTION, - queryBuilder.toString(), queryArgs, null); - return cursor; - } - - /** - * Queries for all the reminders of the events in the instancesCursor, and schedules - * the alarm for the next upcoming reminder. - */ - private static void queryNextReminderAndSchedule(Cursor instancesCursor, Context context, - ContentResolver contentResolver, AlarmManagerInterface alarmManager, - int batchSize, long currentMillis) { - if (AlertService.DEBUG) { - int eventCount = instancesCursor.getCount(); - if (eventCount == 0) { - Log.d(TAG, "No events found starting within 1 week."); - } else { - Log.d(TAG, "Query result count for events starting within 1 week: " + eventCount); - } - } - - // Put query results of all events starting within some interval into map of event ID to - // local start time. - Map<Integer, List<Long>> eventMap = new HashMap<Integer, List<Long>>(); - Time timeObj = new Time(); - long nextAlarmTime = Long.MAX_VALUE; - int nextAlarmEventId = 0; - instancesCursor.moveToPosition(-1); - while (!instancesCursor.isAfterLast()) { - int index = 0; - eventMap.clear(); - StringBuilder eventIdsForQuery = new StringBuilder(); - eventIdsForQuery.append('('); - while (index++ < batchSize && instancesCursor.moveToNext()) { - int eventId = instancesCursor.getInt(INSTANCES_INDEX_EVENTID); - long begin = instancesCursor.getLong(INSTANCES_INDEX_BEGIN); - boolean allday = instancesCursor.getInt(INSTANCES_INDEX_ALL_DAY) != 0; - long localStartTime; - if (allday) { - // Adjust allday to local time. - localStartTime = Utils.convertAlldayUtcToLocal(timeObj, begin, - Time.getCurrentTimezone()); - } else { - localStartTime = begin; - } - List<Long> startTimes = eventMap.get(eventId); - if (startTimes == null) { - startTimes = new ArrayList<Long>(); - eventMap.put(eventId, startTimes); - eventIdsForQuery.append(eventId); - eventIdsForQuery.append(","); - } - startTimes.add(localStartTime); - - // Log for debugging. - if (Log.isLoggable(TAG, Log.DEBUG)) { - timeObj.set(localStartTime); - StringBuilder msg = new StringBuilder(); - msg.append("Events cursor result -- eventId:").append(eventId); - msg.append(", allDay:").append(allday); - msg.append(", start:").append(localStartTime); - msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")).append(")"); - Log.d(TAG, msg.toString()); - } - } - if (eventIdsForQuery.charAt(eventIdsForQuery.length() - 1) == ',') { - eventIdsForQuery.deleteCharAt(eventIdsForQuery.length() - 1); - } - eventIdsForQuery.append(')'); - - // Query the reminders table for the events found. - Cursor cursor = null; - try { - cursor = contentResolver.query(Reminders.CONTENT_URI, REMINDERS_PROJECTION, - REMINDERS_WHERE + eventIdsForQuery, null, null); - - // Process the reminders query results to find the next reminder time. - cursor.moveToPosition(-1); - while (cursor.moveToNext()) { - int eventId = cursor.getInt(REMINDERS_INDEX_EVENT_ID); - int reminderMinutes = cursor.getInt(REMINDERS_INDEX_MINUTES); - List<Long> startTimes = eventMap.get(eventId); - if (startTimes != null) { - for (Long startTime : startTimes) { - long alarmTime = startTime - - reminderMinutes * DateUtils.MINUTE_IN_MILLIS; - if (alarmTime > currentMillis && alarmTime < nextAlarmTime) { - nextAlarmTime = alarmTime; - nextAlarmEventId = eventId; - } - - if (Log.isLoggable(TAG, Log.DEBUG)) { - timeObj.set(alarmTime); - StringBuilder msg = new StringBuilder(); - msg.append("Reminders cursor result -- eventId:").append(eventId); - msg.append(", startTime:").append(startTime); - msg.append(", minutes:").append(reminderMinutes); - msg.append(", alarmTime:").append(alarmTime); - msg.append(" (").append(timeObj.format("%a, %b %d, %Y %I:%M%P")) - .append(")"); - Log.d(TAG, msg.toString()); - } - } - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - } - - // Schedule the alarm for the next reminder time. - if (nextAlarmTime < Long.MAX_VALUE) { - scheduleAlarm(context, nextAlarmEventId, nextAlarmTime, currentMillis, alarmManager); - } - } - - /** - * Schedules an alarm for the EVENT_REMINDER_APP broadcast, for the specified - * alarm time with a slight delay (to account for the possible duplicate broadcast - * from the provider). - */ - private static void scheduleAlarm(Context context, long eventId, long alarmTime, - long currentMillis, AlarmManagerInterface alarmManager) { - // Max out the alarm time to 1 day out, so an alert for an event far in the future - // (not present in our event query results for a limited range) can only be at - // most 1 day late. - long maxAlarmTime = currentMillis + MAX_ALARM_ELAPSED_MS; - if (alarmTime > maxAlarmTime) { - alarmTime = maxAlarmTime; - } - - // Add a slight delay (see comments on the member var). - alarmTime += ALARM_DELAY_MS; - - if (AlertService.DEBUG) { - Time time = new Time(); - time.set(alarmTime); - String schedTime = time.format("%a, %b %d, %Y %I:%M%P"); - Log.d(TAG, "Scheduling alarm for EVENT_REMINDER_APP broadcast for event " + eventId - + " at " + alarmTime + " (" + schedTime + ")"); - } - - // Schedule an EVENT_REMINDER_APP broadcast with AlarmManager. The extra is - // only used by AlertService for logging. It is ignored by Intent.filterEquals, - // so this scheduling will still overwrite the alarm that was previously pending. - // Note that the 'setClass' is required, because otherwise it seems the broadcast - // can be eaten by other apps and we somehow may never receive it. - Intent intent = new Intent(AlertReceiver.EVENT_REMINDER_APP_ACTION); - intent.setClass(context, AlertReceiver.class); - intent.putExtra(CalendarContract.CalendarAlerts.ALARM_TIME, alarmTime); - PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); - alarmManager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi); - } -} diff --git a/src/com/android/calendar/alerts/AlertReceiver.java b/src/com/android/calendar/alerts/AlertReceiver.java deleted file mode 100644 index ce80cae1..00000000 --- a/src/com/android/calendar/alerts/AlertReceiver.java +++ /dev/null @@ -1,123 +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.alerts; - -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.PowerManager; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.telephony.TelephonyManager; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.TextUtils; -import android.text.style.RelativeSizeSpan; -import android.text.style.TextAppearanceSpan; -import android.text.style.URLSpan; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; - -import com.android.calendar.R; -import com.android.calendar.Utils; -import com.android.calendar.alerts.AlertService.NotificationWrapper; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -/** - * Receives android.intent.action.EVENT_REMINDER intents and handles - * event reminders. The intent URI specifies an alert id in the - * CalendarAlerts database table. This class also receives the - * BOOT_COMPLETED intent so that it can add a status bar notification - * if there are Calendar event alarms that have not been dismissed. - * It also receives the TIME_CHANGED action so that it can fire off - * snoozed alarms that have become ready. The real work is done in - * the AlertService class. - * - * To trigger this code after pushing the apk to device: - * adb shell am broadcast -a "android.intent.action.EVENT_REMINDER" - * -n "com.android.calendar/.alerts.AlertReceiver" - */ -public class AlertReceiver extends BroadcastReceiver { - private static final String TAG = "AlertReceiver"; - - // The broadcast for notification refreshes scheduled by the app. This is to - // distinguish the EVENT_REMINDER broadcast sent by the provider. - public static final String EVENT_REMINDER_APP_ACTION = - "com.android.calendar.EVENT_REMINDER_APP"; - - public static final String ACTION_DISMISS_OLD_REMINDERS = "removeOldReminders"; - - @Override - public void onReceive(final Context context, final Intent intent) { - if (AlertService.DEBUG) { - Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); - } - closeNotificationShade(context); - } - - public static NotificationWrapper makeBasicNotification(Context context, String title, - String summaryText, long startMillis, long endMillis, long eventId, - int notificationId, boolean doPopup, int priority) { - Notification n = buildBasicNotification(new Notification.Builder(context), - context, title, summaryText, startMillis, endMillis, eventId, notificationId, - doPopup, priority, false); - return new NotificationWrapper(n, notificationId, eventId, startMillis, endMillis, doPopup); - } - - private static Notification buildBasicNotification(Notification.Builder notificationBuilder, - Context context, String title, String summaryText, long startMillis, long endMillis, - long eventId, int notificationId, boolean doPopup, int priority, - boolean addActionButtons) { - Resources resources = context.getResources(); - if (title == null || title.length() == 0) { - title = resources.getString(R.string.no_title_label); - } - - // Create the base notification. - notificationBuilder.setContentTitle(title); - notificationBuilder.setContentText(summaryText); - notificationBuilder.setSmallIcon(R.drawable.stat_notify_calendar); - if (Utils.isJellybeanOrLater()) { - // Turn off timestamp. - notificationBuilder.setWhen(0); - - // Should be one of the values in Notification (ie. Notification.PRIORITY_HIGH, etc). - // A higher priority will encourage notification manager to expand it. - notificationBuilder.setPriority(priority); - } - return notificationBuilder.getNotification(); - } - - private void closeNotificationShade(Context context) { - Intent closeNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - context.sendBroadcast(closeNotificationShadeIntent); - } -} diff --git a/src/com/android/calendar/alerts/AlertService.java b/src/com/android/calendar/alerts/AlertService.java deleted file mode 100644 index d2c994da..00000000 --- a/src/com/android/calendar/alerts/AlertService.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2008 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.alerts; - -import android.app.Notification; -import android.app.NotificationManager; -import android.app.Service; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.provider.CalendarContract; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.CalendarAlerts; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.TimeZone; - -/** - * This service is used to handle calendar event reminders. - */ -public class AlertService extends Service { - static final boolean DEBUG = true; - private static final String TAG = "AlertService"; - - private volatile Looper mServiceLooper; - - static final String[] ALERT_PROJECTION = new String[] { - CalendarAlerts._ID, // 0 - CalendarAlerts.EVENT_ID, // 1 - CalendarAlerts.STATE, // 2 - CalendarAlerts.TITLE, // 3 - CalendarAlerts.EVENT_LOCATION, // 4 - CalendarAlerts.SELF_ATTENDEE_STATUS, // 5 - CalendarAlerts.ALL_DAY, // 6 - CalendarAlerts.ALARM_TIME, // 7 - CalendarAlerts.MINUTES, // 8 - CalendarAlerts.BEGIN, // 9 - CalendarAlerts.END, // 10 - CalendarAlerts.DESCRIPTION, // 11 - }; - - private static final int ALERT_INDEX_ID = 0; - private static final int ALERT_INDEX_EVENT_ID = 1; - private static final int ALERT_INDEX_STATE = 2; - private static final int ALERT_INDEX_TITLE = 3; - private static final int ALERT_INDEX_EVENT_LOCATION = 4; - private static final int ALERT_INDEX_SELF_ATTENDEE_STATUS = 5; - private static final int ALERT_INDEX_ALL_DAY = 6; - private static final int ALERT_INDEX_ALARM_TIME = 7; - private static final int ALERT_INDEX_MINUTES = 8; - private static final int ALERT_INDEX_BEGIN = 9; - private static final int ALERT_INDEX_END = 10; - private static final int ALERT_INDEX_DESCRIPTION = 11; - - private static final String ACTIVE_ALERTS_SELECTION = "(" + CalendarAlerts.STATE + "=? OR " - + CalendarAlerts.STATE + "=?) AND " + CalendarAlerts.ALARM_TIME + "<="; - - private static final String[] ACTIVE_ALERTS_SELECTION_ARGS = new String[] { - Integer.toString(CalendarAlerts.STATE_FIRED), - Integer.toString(CalendarAlerts.STATE_SCHEDULED) - }; - - private static final String ACTIVE_ALERTS_SORT = "begin DESC, end DESC"; - - private static final String DISMISS_OLD_SELECTION = CalendarAlerts.END + "<? AND " - + CalendarAlerts.STATE + "=?"; - - private static final int MINUTE_MS = 60 * 1000; - - // The grace period before changing a notification's priority bucket. - private static final int MIN_DEPRIORITIZE_GRACE_PERIOD_MS = 15 * MINUTE_MS; - - // Hard limit to the number of notifications displayed. - public static final int MAX_NOTIFICATIONS = 20; - - // Added wrapper for testing - public static class NotificationWrapper { - Notification mNotification; - long mEventId; - long mBegin; - long mEnd; - ArrayList<NotificationWrapper> mNw; - - public NotificationWrapper(Notification n, int notificationId, long eventId, - long startMillis, long endMillis, boolean doPopup) { - mNotification = n; - mEventId = eventId; - mBegin = startMillis; - mEnd = endMillis; - - // popup? - // notification id? - } - - public NotificationWrapper(Notification n) { - mNotification = n; - } - - public void add(NotificationWrapper nw) { - if (mNw == null) { - mNw = new ArrayList<NotificationWrapper>(); - } - mNw.add(nw); - } - } - - // Added wrapper for testing - public static class NotificationMgrWrapper extends NotificationMgr { - NotificationManager mNm; - - public NotificationMgrWrapper(NotificationManager nm) { - mNm = nm; - } - - @Override - public void cancel(int id) { - mNm.cancel(id); - } - - @Override - public void notify(int id, NotificationWrapper nw) { - mNm.notify(id, nw.mNotification); - } - } - - static class NotificationInfo { - String eventName; - String location; - String description; - long startMillis; - long endMillis; - long eventId; - boolean allDay; - boolean newAlert; - - NotificationInfo(String eventName, String location, String description, long startMillis, - long endMillis, long eventId, boolean allDay, boolean newAlert) { - this.eventName = eventName; - this.location = location; - this.description = description; - this.startMillis = startMillis; - this.endMillis = endMillis; - this.eventId = eventId; - this.newAlert = newAlert; - this.allDay = allDay; - } - } - - @Override - public void onCreate() { - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - return START_REDELIVER_INTENT; - } - - @Override - public void onDestroy() { - mServiceLooper.quit(); - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } -} diff --git a/src/com/android/calendar/alerts/AlertUtils.java b/src/com/android/calendar/alerts/AlertUtils.java deleted file mode 100644 index b9aaec29..00000000 --- a/src/com/android/calendar/alerts/AlertUtils.java +++ /dev/null @@ -1,108 +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.alerts; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; -import android.provider.CalendarContract; -import android.provider.CalendarContract.CalendarAlerts; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; - -import com.android.calendar.EventInfoActivity; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -public class AlertUtils { - private static final String TAG = "AlertUtils"; - static final boolean DEBUG = true; - - public static final long SNOOZE_DELAY = 5 * 60 * 1000L; - - // We use one notification id for the expired events notification. All - // other notifications (the 'active' future/concurrent ones) use a unique ID. - public static final int EXPIRED_GROUP_NOTIFICATION_ID = 0; - - public static final String EVENT_ID_KEY = "eventid"; - public static final String EVENT_START_KEY = "eventstart"; - public static final String EVENT_END_KEY = "eventend"; - public static final String NOTIFICATION_ID_KEY = "notificationid"; - public static final String EVENT_IDS_KEY = "eventids"; - public static final String EVENT_STARTS_KEY = "starts"; - - // A flag for using local storage to save alert state instead of the alerts DB table. - // This allows the unbundled app to run alongside other calendar apps without eating - // alerts from other apps. - static boolean BYPASS_DB = true; - - /** - * Creates an AlarmManagerInterface that wraps a real AlarmManager. The alarm code - * was abstracted to an interface to make it testable. - */ - public static AlarmManagerInterface createAlarmManager(Context context) { - final AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - return new AlarmManagerInterface() { - @Override - public void set(int type, long triggerAtMillis, PendingIntent operation) { - if (Utils.isKeyLimePieOrLater()) { - mgr.setExact(type, triggerAtMillis, operation); - } else { - mgr.set(type, triggerAtMillis, operation); - } - } - }; - } - - /** - * Schedules an alarm intent with the system AlarmManager that will notify - * listeners when a reminder should be fired. The provider will keep - * scheduled reminders up to date but apps may use this to implement snooze - * functionality without modifying the reminders table. Scheduled alarms - * will generate an intent using AlertReceiver.EVENT_REMINDER_APP_ACTION. - * - * @param context A context for referencing system resources - * @param manager The AlarmManager to use or null - * @param alarmTime The time to fire the intent in UTC millis since epoch - */ - public static void scheduleAlarm(Context context, AlarmManagerInterface manager, - long alarmTime) { - } - - public static Intent buildEventViewIntent(Context c, long eventId, long begin, long end) { - Intent i = new Intent(Intent.ACTION_VIEW); - Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon(); - builder.appendEncodedPath("events/" + eventId); - i.setData(builder.build()); - i.setClass(c, EventInfoActivity.class); - i.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin); - i.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end); - return i; - } -} diff --git a/src/com/android/calendar/alerts/DismissAlarmsService.java b/src/com/android/calendar/alerts/DismissAlarmsService.java deleted file mode 100644 index 1ec3c22d..00000000 --- a/src/com/android/calendar/alerts/DismissAlarmsService.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2009 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.alerts; - -import android.app.IntentService; -import android.app.NotificationManager; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.IBinder; -import android.provider.CalendarContract.CalendarAlerts; -import androidx.core.app.TaskStackBuilder; - -import android.util.Log; -import com.android.calendar.EventInfoActivity; -import com.android.calendar.alerts.GlobalDismissManager.AlarmId; - -import java.util.LinkedList; -import java.util.List; - -/** - * Service for asynchronously marking fired alarms as dismissed. - */ -public class DismissAlarmsService extends IntentService { - private static final String TAG = "DismissAlarmsService"; - public static final String SHOW_ACTION = "com.android.calendar.SHOW"; - public static final String DISMISS_ACTION = "com.android.calendar.DISMISS"; - - private static final String[] PROJECTION = new String[] { - CalendarAlerts.STATE, - }; - private static final int COLUMN_INDEX_STATE = 0; - - public DismissAlarmsService() { - super("DismissAlarmsService"); - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onHandleIntent(Intent intent) { - if (AlertService.DEBUG) { - Log.d(TAG, "onReceive: a=" + intent.getAction() + " " + intent.toString()); - } - - long eventId = intent.getLongExtra(AlertUtils.EVENT_ID_KEY, -1); - long eventStart = intent.getLongExtra(AlertUtils.EVENT_START_KEY, -1); - long eventEnd = intent.getLongExtra(AlertUtils.EVENT_END_KEY, -1); - long[] eventIds = intent.getLongArrayExtra(AlertUtils.EVENT_IDS_KEY); - long[] eventStarts = intent.getLongArrayExtra(AlertUtils.EVENT_STARTS_KEY); - int notificationId = intent.getIntExtra(AlertUtils.NOTIFICATION_ID_KEY, -1); - List<AlarmId> alarmIds = new LinkedList<AlarmId>(); - - Uri uri = CalendarAlerts.CONTENT_URI; - String selection; - - // Dismiss a specific fired alarm if id is present, otherwise, dismiss all alarms - if (eventId != -1) { - alarmIds.add(new AlarmId(eventId, eventStart)); - selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED + " AND " + - CalendarAlerts.EVENT_ID + "=" + eventId; - } else if (eventIds != null && eventIds.length > 0 && - eventStarts != null && eventIds.length == eventStarts.length) { - selection = buildMultipleEventsQuery(eventIds); - for (int i = 0; i < eventIds.length; i++) { - alarmIds.add(new AlarmId(eventIds[i], eventStarts[i])); - } - } else { - // NOTE: I don't believe that this ever happens. - selection = CalendarAlerts.STATE + "=" + CalendarAlerts.STATE_FIRED; - } - - GlobalDismissManager.dismissGlobally(getApplicationContext(), alarmIds); - - ContentResolver resolver = getContentResolver(); - ContentValues values = new ContentValues(); - values.put(PROJECTION[COLUMN_INDEX_STATE], CalendarAlerts.STATE_DISMISSED); - resolver.update(uri, values, selection, null); - - // Remove from notification bar. - if (notificationId != -1) { - NotificationManager nm = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(notificationId); - } - - if (SHOW_ACTION.equals(intent.getAction())) { - // Show event on Calendar app by building an intent and task stack to start - // EventInfoActivity with AllInOneActivity as the parent activity rooted to home. - Intent i = AlertUtils.buildEventViewIntent(this, eventId, eventStart, eventEnd); - - TaskStackBuilder.create(this) - .addParentStack(EventInfoActivity.class).addNextIntent(i).startActivities(); - } - } - - private String buildMultipleEventsQuery(long[] eventIds) { - StringBuilder selection = new StringBuilder(); - selection.append(CalendarAlerts.STATE); - selection.append("="); - selection.append(CalendarAlerts.STATE_FIRED); - if (eventIds.length > 0) { - selection.append(" AND ("); - selection.append(CalendarAlerts.EVENT_ID); - selection.append("="); - selection.append(eventIds[0]); - for (int i = 1; i < eventIds.length; i++) { - selection.append(" OR "); - selection.append(CalendarAlerts.EVENT_ID); - selection.append("="); - selection.append(eventIds[i]); - } - selection.append(")"); - } - return selection.toString(); - } -} diff --git a/src/com/android/calendar/alerts/GlobalDismissManager.java b/src/com/android/calendar/alerts/GlobalDismissManager.java deleted file mode 100644 index 27b3e162..00000000 --- a/src/com/android/calendar/alerts/GlobalDismissManager.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2013 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.alerts; - -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.provider.CalendarContract.CalendarAlerts; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Events; -import android.util.Log; -import android.util.Pair; - -import com.android.calendar.R; - -import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Utilities for managing notification dismissal across devices. - */ -public class GlobalDismissManager extends BroadcastReceiver { - public static class AlarmId { - public long mEventId; - public long mStart; - - public AlarmId(long id, long start) { - mEventId = id; - mStart = start; - } - } - - /** - * Globally dismiss notifications that are backed by the same events. - * - * @param context application context - * @param alarmIds Unique identifiers for events that have been dismissed by the user. - * @return true if notification_sender_id is available - */ - public static void dismissGlobally(Context context, List<AlarmId> alarmIds) { - Set<Long> eventIds = new HashSet<Long>(alarmIds.size()); - for (AlarmId alarmId: alarmIds) { - eventIds.add(alarmId.mEventId); - } - } - - @Override - @SuppressWarnings("unchecked") - public void onReceive(Context context, Intent intent) { - new AsyncTask<Pair<Context, Intent>, Void, Void>() { - @Override - protected Void doInBackground(Pair<Context, Intent>... params) { - return null; - } - }.execute(new Pair<Context, Intent>(context, intent)); - } -} diff --git a/src/com/android/calendar/alerts/InitAlarmsService.java b/src/com/android/calendar/alerts/InitAlarmsService.java deleted file mode 100644 index 3a9b0b2c..00000000 --- a/src/com/android/calendar/alerts/InitAlarmsService.java +++ /dev/null @@ -1,62 +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.alerts; - -import android.app.IntentService; -import android.content.ContentValues; -import android.content.Intent; -import android.net.Uri; -import android.os.SystemClock; -import android.provider.CalendarContract; -import android.util.Log; - -/** - * Service for clearing all scheduled alerts from the CalendarAlerts table and - * rescheduling them. This is expected to be called only on boot up, to restore - * the AlarmManager alarms that were lost on device restart. - */ -public class InitAlarmsService extends IntentService { - private static final String TAG = "InitAlarmsService"; - private static final String SCHEDULE_ALARM_REMOVE_PATH = "schedule_alarms_remove"; - private static final Uri SCHEDULE_ALARM_REMOVE_URI = Uri.withAppendedPath( - CalendarContract.CONTENT_URI, SCHEDULE_ALARM_REMOVE_PATH); - - // Delay for rescheduling the alarms must be great enough to minimize race - // conditions with the provider's boot up actions. - private static final long DELAY_MS = 30000; - - public InitAlarmsService() { - super("InitAlarmsService"); - } - - @Override - protected void onHandleIntent(Intent intent) { - // Delay to avoid race condition of in-progress alarm scheduling in provider. - SystemClock.sleep(DELAY_MS); - Log.d(TAG, "Clearing and rescheduling alarms."); - try { - getContentResolver().update(SCHEDULE_ALARM_REMOVE_URI, new ContentValues(), null, - null); - } catch (java.lang.IllegalArgumentException e) { - // java.lang.IllegalArgumentException: - // Unknown URI content://com.android.calendar/schedule_alarms_remove - - // Until b/7742576 is resolved, just catch the exception so the app won't crash - Log.e(TAG, "update failed: " + e.toString()); - } - } -} diff --git a/src/com/android/calendar/alerts/NotificationMgr.java b/src/com/android/calendar/alerts/NotificationMgr.java deleted file mode 100644 index 0ab475c3..00000000 --- a/src/com/android/calendar/alerts/NotificationMgr.java +++ /dev/null @@ -1,41 +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.alerts; - -import com.android.calendar.alerts.AlertService.NotificationWrapper; - -public abstract class NotificationMgr { - public abstract void notify(int id, NotificationWrapper notification); - public abstract void cancel(int id); - - /** - * Don't actually use the notification framework's cancelAll since the SyncAdapter - * might post notifications and we don't want to affect those. - */ - public void cancelAll() { - cancelAllBetween(0, AlertService.MAX_NOTIFICATIONS); - } - - /** - * Cancels IDs between the specified bounds, inclusively. - */ - public void cancelAllBetween(int from, int to) { - for (int i = from; i <= to; i++) { - cancel(i); - } - } -} diff --git a/src/com/android/calendar/alerts/QuickResponseActivity.java b/src/com/android/calendar/alerts/QuickResponseActivity.java deleted file mode 100644 index 3d291d02..00000000 --- a/src/com/android/calendar/alerts/QuickResponseActivity.java +++ /dev/null @@ -1,108 +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.alerts; - -import android.app.ListActivity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.Toast; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.Arrays; - -/** - * Activity which displays when the user wants to email guests from notifications. - * - * This presents the user with list if quick responses to be populated in an email - * to minimize typing. - * - */ -public class QuickResponseActivity extends ListActivity implements OnItemClickListener { - private static final String TAG = "QuickResponseActivity"; - public static final String EXTRA_EVENT_ID = "eventId"; - - private String[] mResponses = null; - static long mEventId; - - @Override - protected void onCreate(Bundle icicle) { - super.onCreate(icicle); - - Intent intent = getIntent(); - if (intent == null) { - finish(); - return; - } - - mEventId = intent.getLongExtra(EXTRA_EVENT_ID, -1); - if (mEventId == -1) { - finish(); - return; - } - - // Set listener - getListView().setOnItemClickListener(QuickResponseActivity.this); - - // Populate responses - String[] responses = Utils.getQuickResponses(this); - Arrays.sort(responses); - - // Add "Custom response..." - mResponses = new String[responses.length + 1]; - int i; - for (i = 0; i < responses.length; i++) { - mResponses[i] = responses[i]; - } - mResponses[i] = getResources().getString(R.string.quick_response_custom_msg); - - setListAdapter(new ArrayAdapter<String>(this, R.layout.quick_response_item, mResponses)); - } - - // implements OnItemClickListener - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - - String body = null; - if (mResponses != null && position < mResponses.length - 1) { - body = mResponses[position]; - } - - // Start thread to query provider and send mail - new QueryThread(mEventId, body).start(); - } - - private class QueryThread extends Thread { - long mEventId; - String mBody; - - QueryThread(long eventId, String body) { - mEventId = eventId; - mBody = body; - } - - @Override - public void run() { - } - } -} diff --git a/src/com/android/calendar/month/MonthByWeekAdapter.java b/src/com/android/calendar/month/MonthByWeekAdapter.java deleted file mode 100644 index 45a1bea1..00000000 --- a/src/com/android/calendar/month/MonthByWeekAdapter.java +++ /dev/null @@ -1,406 +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.month; - -import android.content.Context; -import android.content.res.Configuration; -import android.os.Handler; -import android.os.Message; -import android.text.format.Time; -import android.util.Log; -import android.view.GestureDetector; -import android.view.HapticFeedbackConstants; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.AbsListView.LayoutParams; - -import com.android.calendar.CalendarController; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.HashMap; - -public class MonthByWeekAdapter extends SimpleWeeksAdapter { - private static final String TAG = "MonthByWeekAdapter"; - - public static final String WEEK_PARAMS_IS_MINI = "mini_month"; - protected static int DEFAULT_QUERY_DAYS = 7 * 8; // 8 weeks - private static final long ANIMATE_TODAY_TIMEOUT = 1000; - - protected CalendarController mController; - protected String mHomeTimeZone; - protected Time mTempTime; - protected Time mToday; - protected int mFirstJulianDay; - protected int mQueryDays; - protected boolean mIsMiniMonth = true; - protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; - private final boolean mShowAgendaWithMonth; - - protected ArrayList<ArrayList<Event>> mEventDayList = new ArrayList<ArrayList<Event>>(); - protected ArrayList<Event> mEvents = null; - - private boolean mAnimateToday = false; - private long mAnimateTime = 0; - - private Handler mEventDialogHandler; - - MonthWeekEventsView mClickedView; - MonthWeekEventsView mSingleTapUpView; - MonthWeekEventsView mLongClickedView; - - float mClickedXLocation; // Used to find which day was clicked - long mClickTime; // Used to calculate minimum click animation time - // Used to insure minimal time for seeing the click animation before switching views - private static final int mOnTapDelay = 100; - // Minimal time for a down touch action before stating the click animation, this insures that - // there is no click animation on flings - private static int mOnDownDelay; - private static int mTotalClickDelay; - // Minimal distance to move the finger in order to cancel the click animation - private static float mMovedPixelToCancel; - - public MonthByWeekAdapter(Context context, HashMap<String, Integer> params) { - super(context, params); - if (params.containsKey(WEEK_PARAMS_IS_MINI)) { - mIsMiniMonth = params.get(WEEK_PARAMS_IS_MINI) != 0; - } - mShowAgendaWithMonth = Utils.getConfigBool(context, R.bool.show_agenda_with_month); - ViewConfiguration vc = ViewConfiguration.get(context); - mOnDownDelay = ViewConfiguration.getTapTimeout(); - mMovedPixelToCancel = vc.getScaledTouchSlop(); - mTotalClickDelay = mOnDownDelay + mOnTapDelay; - } - - public void animateToday() { - mAnimateToday = true; - mAnimateTime = System.currentTimeMillis(); - } - - @Override - protected void init() { - super.init(); - mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); - mController = CalendarController.getInstance(mContext); - mHomeTimeZone = Utils.getTimeZone(mContext, null); - mSelectedDay.switchTimezone(mHomeTimeZone); - mToday = new Time(mHomeTimeZone); - mToday.setToNow(); - mTempTime = new Time(mHomeTimeZone); - } - - private void updateTimeZones() { - mSelectedDay.timezone = mHomeTimeZone; - mSelectedDay.normalize(true); - mToday.timezone = mHomeTimeZone; - mToday.setToNow(); - mTempTime.switchTimezone(mHomeTimeZone); - } - - @Override - public void setSelectedDay(Time selectedTime) { - mSelectedDay.set(selectedTime); - long millis = mSelectedDay.normalize(true); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); - notifyDataSetChanged(); - } - - public void setEvents(int firstJulianDay, int numDays, ArrayList<Event> events) { - if (mIsMiniMonth) { - if (Log.isLoggable(TAG, Log.ERROR)) { - Log.e(TAG, "Attempted to set events for mini view. Events only supported in full" - + " view."); - } - return; - } - mEvents = events; - mFirstJulianDay = firstJulianDay; - mQueryDays = numDays; - // Create a new list, this is necessary since the weeks are referencing - // pieces of the old list - ArrayList<ArrayList<Event>> eventDayList = new ArrayList<ArrayList<Event>>(); - for (int i = 0; i < numDays; i++) { - eventDayList.add(new ArrayList<Event>()); - } - - if (events == null || events.size() == 0) { - if(Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "No events. Returning early--go schedule something fun."); - } - mEventDayList = eventDayList; - refresh(); - return; - } - - // Compute the new set of days with events - for (Event event : events) { - int startDay = event.startDay - mFirstJulianDay; - int endDay = event.endDay - mFirstJulianDay + 1; - if (startDay < numDays || endDay >= 0) { - if (startDay < 0) { - startDay = 0; - } - if (startDay > numDays) { - continue; - } - if (endDay < 0) { - continue; - } - if (endDay > numDays) { - endDay = numDays; - } - for (int j = startDay; j < endDay; j++) { - eventDayList.get(j).add(event); - } - } - } - if(Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Processed " + events.size() + " events."); - } - mEventDayList = eventDayList; - refresh(); - } - - @SuppressWarnings("unchecked") - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (mIsMiniMonth) { - return super.getView(position, convertView, parent); - } - MonthWeekEventsView v; - LayoutParams params = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - HashMap<String, Integer> drawingParams = null; - boolean isAnimatingToday = false; - if (convertView != null) { - v = (MonthWeekEventsView) convertView; - // Checking updateToday uses the current params instead of the new - // params, so this is assuming the view is relatively stable - if (mAnimateToday && v.updateToday(mSelectedDay.timezone)) { - long currentTime = System.currentTimeMillis(); - // If it's been too long since we tried to start the animation - // don't show it. This can happen if the user stops a scroll - // before reaching today. - if (currentTime - mAnimateTime > ANIMATE_TODAY_TIMEOUT) { - mAnimateToday = false; - mAnimateTime = 0; - } else { - isAnimatingToday = true; - // There is a bug that causes invalidates to not work some - // of the time unless we recreate the view. - v = new MonthWeekEventsView(mContext); - } - } else { - drawingParams = (HashMap<String, Integer>) v.getTag(); - } - } else { - v = new MonthWeekEventsView(mContext); - } - if (drawingParams == null) { - drawingParams = new HashMap<String, Integer>(); - } - drawingParams.clear(); - - v.setLayoutParams(params); - v.setClickable(true); - v.setOnTouchListener(this); - - int selectedDay = -1; - if (mSelectedWeek == position) { - selectedDay = mSelectedDay.weekDay; - } - - drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, - (parent.getHeight() + parent.getTop()) / mNumWeeks); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); - drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ORIENTATION, mOrientation); - - if (isAnimatingToday) { - drawingParams.put(MonthWeekEventsView.VIEW_PARAMS_ANIMATE_TODAY, 1); - mAnimateToday = false; - } - - v.setWeekParams(drawingParams, mSelectedDay.timezone); - return v; - } - - @Override - protected void refresh() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - mHomeTimeZone = Utils.getTimeZone(mContext, null); - mOrientation = mContext.getResources().getConfiguration().orientation; - updateTimeZones(); - notifyDataSetChanged(); - } - - @Override - protected void onDayTapped(Time day) { - setDayParameters(day); - if (mShowAgendaWithMonth || mIsMiniMonth) { - // If agenda view is visible with month view , refresh the views - // with the selected day's info - mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, - ViewType.CURRENT, CalendarController.EXTRA_GOTO_DATE, null, null); - } else { - // Else , switch to the detailed view - mController.sendEvent(mContext, EventType.GO_TO, day, day, -1, - ViewType.DETAIL, - CalendarController.EXTRA_GOTO_DATE - | CalendarController.EXTRA_GOTO_BACK_TO_PREVIOUS, null, null); - } - } - - private void setDayParameters(Time day) { - day.timezone = mHomeTimeZone; - Time currTime = new Time(mHomeTimeZone); - currTime.set(mController.getTime()); - day.hour = currTime.hour; - day.minute = currTime.minute; - day.allDay = false; - day.normalize(true); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (!(v instanceof MonthWeekEventsView)) { - return super.onTouch(v, event); - } - - int action = event.getAction(); - - // Event was tapped - switch to the detailed view making sure the click animation - // is done first. - if (mGestureDetector.onTouchEvent(event)) { - mSingleTapUpView = (MonthWeekEventsView) v; - long delay = System.currentTimeMillis() - mClickTime; - // Make sure the animation is visible for at least mOnTapDelay - mOnDownDelay ms - mListView.postDelayed(mDoSingleTapUp, - delay > mTotalClickDelay ? 0 : mTotalClickDelay - delay); - return true; - } else { - // Animate a click - on down: show the selected day in the "clicked" color. - // On Up/scroll/move/cancel: hide the "clicked" color. - switch (action) { - case MotionEvent.ACTION_DOWN: - mClickedView = (MonthWeekEventsView)v; - mClickedXLocation = event.getX(); - mClickTime = System.currentTimeMillis(); - mListView.postDelayed(mDoClick, mOnDownDelay); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_SCROLL: - case MotionEvent.ACTION_CANCEL: - clearClickedView((MonthWeekEventsView)v); - break; - case MotionEvent.ACTION_MOVE: - // No need to cancel on vertical movement, ACTION_SCROLL will do that. - if (Math.abs(event.getX() - mClickedXLocation) > mMovedPixelToCancel) { - clearClickedView((MonthWeekEventsView)v); - } - break; - default: - break; - } - } - // Do not tell the frameworks we consumed the touch action so that fling actions can be - // processed by the fragment. - return false; - } - - /** - * This is here so we can identify events and process them - */ - protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onSingleTapUp(MotionEvent e) { - return true; - } - - @Override - public void onLongPress(MotionEvent e) { - if (mLongClickedView != null) { - Time day = mLongClickedView.getDayFromLocation(mClickedXLocation); - if (day != null) { - mLongClickedView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - Message message = new Message(); - message.obj = day; - } - mLongClickedView.clearClickedDay(); - mLongClickedView = null; - } - } - } - - // Clear the visual cues of the click animation and related running code. - private void clearClickedView(MonthWeekEventsView v) { - mListView.removeCallbacks(mDoClick); - synchronized(v) { - v.clearClickedDay(); - } - mClickedView = null; - } - - // Perform the tap animation in a runnable to allow a delay before showing the tap color. - // This is done to prevent a click animation when a fling is done. - private final Runnable mDoClick = new Runnable() { - @Override - public void run() { - if (mClickedView != null) { - synchronized(mClickedView) { - mClickedView.setClickedDay(mClickedXLocation); - } - mLongClickedView = mClickedView; - mClickedView = null; - // This is a workaround , sometimes the top item on the listview doesn't refresh on - // invalidate, so this forces a re-draw. - mListView.invalidate(); - } - } - }; - - // Performs the single tap operation: go to the tapped day. - // This is done in a runnable to allow the click animation to finish before switching views - private final Runnable mDoSingleTapUp = new Runnable() { - @Override - public void run() { - if (mSingleTapUpView != null) { - Time day = mSingleTapUpView.getDayFromLocation(mClickedXLocation); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Touched day at Row=" + mSingleTapUpView.mWeek + " day=" + day.toString()); - } - if (day != null) { - onDayTapped(day); - } - clearClickedView(mSingleTapUpView); - mSingleTapUpView = null; - } - } - }; -} diff --git a/src/com/android/calendar/month/MonthByWeekFragment.java b/src/com/android/calendar/month/MonthByWeekFragment.java deleted file mode 100644 index f8a518d3..00000000 --- a/src/com/android/calendar/month/MonthByWeekFragment.java +++ /dev/null @@ -1,494 +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.month; - -import android.app.Activity; -import android.app.FragmentManager; -import android.app.LoaderManager; -import android.content.ContentUris; -import android.content.CursorLoader; -import android.content.Loader; -import android.content.res.Resources; -import android.database.Cursor; -import android.graphics.drawable.StateListDrawable; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Instances; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; - -import com.android.calendar.CalendarController; -import com.android.calendar.CalendarController.EventInfo; -import com.android.calendar.CalendarController.EventType; -import com.android.calendar.CalendarController.ViewType; -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; - -public class MonthByWeekFragment extends SimpleDayPickerFragment implements - CalendarController.EventHandler, LoaderManager.LoaderCallbacks<Cursor>, OnScrollListener, - OnTouchListener { - private static final String TAG = "MonthFragment"; - private static final String TAG_EVENT_DIALOG = "event_dialog"; - - // Selection and selection args for adding event queries - private static final String WHERE_CALENDARS_VISIBLE = Calendars.VISIBLE + "=1"; - private static final String INSTANCES_SORT_ORDER = Instances.START_DAY + "," - + Instances.START_MINUTE + "," + Instances.TITLE; - protected static boolean mShowDetailsInMonth = false; - - protected float mMinimumTwoMonthFlingVelocity; - protected boolean mIsMiniMonth; - protected boolean mHideDeclined; - - protected int mFirstLoadedJulianDay; - protected int mLastLoadedJulianDay; - - private static final int WEEKS_BUFFER = 1; - // How long to wait after scroll stops before starting the loader - // Using scroll duration because scroll state changes don't update - // correctly when a scroll is triggered programmatically. - private static final int LOADER_DELAY = 200; - // The minimum time between requeries of the data if the db is - // changing - private static final int LOADER_THROTTLE_DELAY = 500; - - private CursorLoader mLoader; - private Uri mEventUri; - private final Time mDesiredDay = new Time(); - - private volatile boolean mShouldLoad = true; - private boolean mUserScrolled = false; - - private int mEventsLoadingDelay; - private boolean mShowCalendarControls; - private boolean mIsDetached; - - private final Runnable mTZUpdater = new Runnable() { - @Override - public void run() { - String tz = Utils.getTimeZone(mContext, mTZUpdater); - mSelectedDay.timezone = tz; - mSelectedDay.normalize(true); - mTempTime.timezone = tz; - mFirstDayOfMonth.timezone = tz; - mFirstDayOfMonth.normalize(true); - mFirstVisibleDay.timezone = tz; - mFirstVisibleDay.normalize(true); - if (mAdapter != null) { - mAdapter.refresh(); - } - } - }; - - - private final Runnable mUpdateLoader = new Runnable() { - @Override - public void run() { - synchronized (this) { - if (!mShouldLoad || mLoader == null) { - return; - } - // Stop any previous loads while we update the uri - stopLoader(); - - // Start the loader again - mEventUri = updateUri(); - - mLoader.setUri(mEventUri); - mLoader.startLoading(); - mLoader.onContentChanged(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Started loader with uri: " + mEventUri); - } - } - } - }; - // Used to load the events when a delay is needed - Runnable mLoadingRunnable = new Runnable() { - @Override - public void run() { - if (!mIsDetached) { - mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, - MonthByWeekFragment.this); - } - } - }; - - - /** - * Updates the uri used by the loader according to the current position of - * the listview. - * - * @return The new Uri to use - */ - private Uri updateUri() { - SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); - if (child != null) { - int julianDay = child.getFirstJulianDay(); - mFirstLoadedJulianDay = julianDay; - } - // -1 to ensure we get all day events from any time zone - mTempTime.setJulianDay(mFirstLoadedJulianDay - 1); - long start = mTempTime.toMillis(true); - mLastLoadedJulianDay = mFirstLoadedJulianDay + (mNumWeeks + 2 * WEEKS_BUFFER) * 7; - // +1 to ensure we get all day events from any time zone - mTempTime.setJulianDay(mLastLoadedJulianDay + 1); - long end = mTempTime.toMillis(true); - - // Create a new uri with the updated times - Uri.Builder builder = Instances.CONTENT_URI.buildUpon(); - ContentUris.appendId(builder, start); - ContentUris.appendId(builder, end); - return builder.build(); - } - - // Extract range of julian days from URI - private void updateLoadedDays() { - List<String> pathSegments = mEventUri.getPathSegments(); - int size = pathSegments.size(); - if (size <= 2) { - return; - } - long first = Long.parseLong(pathSegments.get(size - 2)); - long last = Long.parseLong(pathSegments.get(size - 1)); - mTempTime.set(first); - mFirstLoadedJulianDay = Time.getJulianDay(first, mTempTime.gmtoff); - mTempTime.set(last); - mLastLoadedJulianDay = Time.getJulianDay(last, mTempTime.gmtoff); - } - - protected String updateWhere() { - // TODO fix selection/selection args after b/3206641 is fixed - String where = WHERE_CALENDARS_VISIBLE; - if (mHideDeclined || !mShowDetailsInMonth) { - where += " AND " + Instances.SELF_ATTENDEE_STATUS + "!=" - + Attendees.ATTENDEE_STATUS_DECLINED; - } - return where; - } - - private void stopLoader() { - synchronized (mUpdateLoader) { - mHandler.removeCallbacks(mUpdateLoader); - if (mLoader != null) { - mLoader.stopLoading(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Stopped loader from loading"); - } - } - } - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mTZUpdater.run(); - if (mAdapter != null) { - mAdapter.setSelectedDay(mSelectedDay); - } - mIsDetached = false; - - ViewConfiguration viewConfig = ViewConfiguration.get(activity); - mMinimumTwoMonthFlingVelocity = viewConfig.getScaledMaximumFlingVelocity() / 2; - Resources res = activity.getResources(); - mShowCalendarControls = Utils.getConfigBool(activity, R.bool.show_calendar_controls); - // Synchronized the loading time of the month's events with the animation of the - // calendar controls. - if (mShowCalendarControls) { - mEventsLoadingDelay = res.getInteger(R.integer.calendar_controls_animation_time); - } - mShowDetailsInMonth = res.getBoolean(R.bool.show_details_in_month); - } - - @Override - public void onDetach() { - mIsDetached = true; - super.onDetach(); - if (mShowCalendarControls) { - if (mListView != null) { - mListView.removeCallbacks(mLoadingRunnable); - } - } - } - - @Override - protected void setUpAdapter() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - - HashMap<String, Integer> weekParams = new HashMap<String, Integer>(); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); - weekParams.put(MonthByWeekAdapter.WEEK_PARAMS_IS_MINI, mIsMiniMonth ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, - Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff)); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_DAYS_PER_WEEK, mDaysPerWeek); - if (mAdapter == null) { - mAdapter = new MonthByWeekAdapter(getActivity(), weekParams); - mAdapter.registerDataSetObserver(mObserver); - } else { - mAdapter.updateParams(weekParams); - } - mAdapter.notifyDataSetChanged(); - } - - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v; - if (mIsMiniMonth) { - v = inflater.inflate(R.layout.month_by_week, container, false); - } else { - v = inflater.inflate(R.layout.full_month_by_week, container, false); - } - mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); - return v; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - mListView.setSelector(new StateListDrawable()); - mListView.setOnTouchListener(this); - - if (!mIsMiniMonth) { - mListView.setBackgroundColor(getResources().getColor(R.color.month_bgcolor)); - } - - // To get a smoother transition when showing this fragment, delay loading of events until - // the fragment is expended fully and the calendar controls are gone. - if (mShowCalendarControls) { - mListView.postDelayed(mLoadingRunnable, mEventsLoadingDelay); - } else { - mLoader = (CursorLoader) getLoaderManager().initLoader(0, null, this); - } - mAdapter.setListView(mListView); - } - - public MonthByWeekFragment() { - this(System.currentTimeMillis(), true); - } - - public MonthByWeekFragment(long initialTime, boolean isMiniMonth) { - super(initialTime); - mIsMiniMonth = isMiniMonth; - } - - @Override - protected void setUpHeader() { - if (mIsMiniMonth) { - super.setUpHeader(); - return; - } - - mDayLabels = new String[7]; - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, - DateUtils.LENGTH_MEDIUM).toUpperCase(); - } - } - - // TODO - @Override - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - if (mIsMiniMonth) { - return null; - } - CursorLoader loader; - synchronized (mUpdateLoader) { - mFirstLoadedJulianDay = - Time.getJulianDay(mSelectedDay.toMillis(true), mSelectedDay.gmtoff) - - (mNumWeeks * 7 / 2); - mEventUri = updateUri(); - String where = updateWhere(); - - loader = new CursorLoader( - getActivity(), mEventUri, Event.EVENT_PROJECTION, where, - null /* WHERE_CALENDARS_SELECTED_ARGS */, INSTANCES_SORT_ORDER); - loader.setUpdateThrottle(LOADER_THROTTLE_DELAY); - } - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Returning new loader with uri: " + mEventUri); - } - return loader; - } - - @Override - public void doResumeUpdates() { - mFirstDayOfWeek = Utils.getFirstDayOfWeek(mContext); - mShowWeekNumber = Utils.getShowWeekNumber(mContext); - boolean prevHideDeclined = mHideDeclined; - mHideDeclined = Utils.getHideDeclinedEvents(mContext); - if (prevHideDeclined != mHideDeclined && mLoader != null) { - mLoader.setSelection(updateWhere()); - } - mDaysPerWeek = Utils.getDaysPerWeek(mContext); - updateHeader(); - mAdapter.setSelectedDay(mSelectedDay); - mTZUpdater.run(); - mTodayUpdater.run(); - goTo(mSelectedDay.toMillis(true), false, true, false); - } - - @Override - public void onLoadFinished(Loader<Cursor> loader, Cursor data) { - synchronized (mUpdateLoader) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Found " + data.getCount() + " cursor entries for uri " + mEventUri); - } - CursorLoader cLoader = (CursorLoader) loader; - if (mEventUri == null) { - mEventUri = cLoader.getUri(); - updateLoadedDays(); - } - if (cLoader.getUri().compareTo(mEventUri) != 0) { - // We've started a new query since this loader ran so ignore the - // result - return; - } - ArrayList<Event> events = new ArrayList<Event>(); - Event.buildEventsFromCursor( - events, data, mContext, mFirstLoadedJulianDay, mLastLoadedJulianDay); - ((MonthByWeekAdapter) mAdapter).setEvents(mFirstLoadedJulianDay, - mLastLoadedJulianDay - mFirstLoadedJulianDay + 1, events); - } - } - - @Override - public void onLoaderReset(Loader<Cursor> loader) { - } - - @Override - public void eventsChanged() { - // TODO remove this after b/3387924 is resolved - if (mLoader != null) { - mLoader.forceLoad(); - } - } - - @Override - public long getSupportedEventTypes() { - return EventType.GO_TO | EventType.EVENTS_CHANGED; - } - - @Override - public void handleEvent(EventInfo event) { - if (event.eventType == EventType.GO_TO) { - boolean animate = true; - if (mDaysPerWeek * mNumWeeks * 2 < Math.abs( - Time.getJulianDay(event.selectedTime.toMillis(true), event.selectedTime.gmtoff) - - Time.getJulianDay(mFirstVisibleDay.toMillis(true), mFirstVisibleDay.gmtoff) - - mDaysPerWeek * mNumWeeks / 2)) { - animate = false; - } - mDesiredDay.set(event.selectedTime); - mDesiredDay.normalize(true); - boolean animateToday = (event.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0; - boolean delayAnimation = goTo(event.selectedTime.toMillis(true), animate, true, false); - if (animateToday) { - // If we need to flash today start the animation after any - // movement from listView has ended. - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - ((MonthByWeekAdapter) mAdapter).animateToday(); - mAdapter.notifyDataSetChanged(); - } - }, delayAnimation ? GOTO_SCROLL_DURATION : 0); - } - } else if (event.eventType == EventType.EVENTS_CHANGED) { - eventsChanged(); - } - } - - @Override - protected void setMonthDisplayed(Time time, boolean updateHighlight) { - super.setMonthDisplayed(time, updateHighlight); - if (!mIsMiniMonth) { - boolean useSelected = false; - if (time.year == mDesiredDay.year && time.month == mDesiredDay.month) { - mSelectedDay.set(mDesiredDay); - mAdapter.setSelectedDay(mDesiredDay); - useSelected = true; - } else { - mSelectedDay.set(time); - mAdapter.setSelectedDay(time); - } - CalendarController controller = CalendarController.getInstance(mContext); - if (mSelectedDay.minute >= 30) { - mSelectedDay.minute = 30; - } else { - mSelectedDay.minute = 0; - } - long newTime = mSelectedDay.normalize(true); - if (newTime != controller.getTime() && mUserScrolled) { - long offset = useSelected ? 0 : DateUtils.WEEK_IN_MILLIS * mNumWeeks / 3; - controller.setTime(newTime + offset); - } - controller.sendEvent(this, EventType.UPDATE_TITLE, time, time, time, -1, - ViewType.CURRENT, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY - | DateUtils.FORMAT_SHOW_YEAR, null, null); - } - } - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - - synchronized (mUpdateLoader) { - if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) { - mShouldLoad = false; - stopLoader(); - mDesiredDay.setToNow(); - } else { - mHandler.removeCallbacks(mUpdateLoader); - mShouldLoad = true; - mHandler.postDelayed(mUpdateLoader, LOADER_DELAY); - } - } - if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { - mUserScrolled = true; - } - - mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - mDesiredDay.setToNow(); - return false; - } -} diff --git a/src/com/android/calendar/month/MonthListView.java b/src/com/android/calendar/month/MonthListView.java deleted file mode 100644 index f2621ccb..00000000 --- a/src/com/android/calendar/month/MonthListView.java +++ /dev/null @@ -1,51 +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.month; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.MotionEvent; -import android.widget.ListView; - -import com.android.calendar.Utils; - -public class MonthListView extends ListView { - - private static final String TAG = "MonthListView"; - - public MonthListView(Context context) { - super(context); - } - - public MonthListView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public MonthListView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - return super.onTouchEvent(ev); - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - return super.onInterceptTouchEvent(ev); - } -} diff --git a/src/com/android/calendar/month/MonthWeekEventsView.java b/src/com/android/calendar/month/MonthWeekEventsView.java deleted file mode 100644 index e1c78c67..00000000 --- a/src/com/android/calendar/month/MonthWeekEventsView.java +++ /dev/null @@ -1,1110 +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.month; - -import com.android.calendar.Event; -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.app.Service; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Paint.Style; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.provider.CalendarContract.Attendees; -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.util.Log; -import android.view.MotionEvent; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Formatter; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; - -public class MonthWeekEventsView extends SimpleWeekView { - - private static final String TAG = "MonthView"; - - private static final boolean DEBUG_LAYOUT = false; - - public static final String VIEW_PARAMS_ORIENTATION = "orientation"; - public static final String VIEW_PARAMS_ANIMATE_TODAY = "animate_today"; - - /* NOTE: these are not constants, and may be multiplied by a scale factor */ - private static int TEXT_SIZE_MONTH_NUMBER = 32; - private static int TEXT_SIZE_EVENT = 12; - private static int TEXT_SIZE_EVENT_TITLE = 14; - private static int TEXT_SIZE_MORE_EVENTS = 12; - private static int TEXT_SIZE_MONTH_NAME = 14; - private static int TEXT_SIZE_WEEK_NUM = 12; - - private static int DNA_MARGIN = 4; - private static int DNA_ALL_DAY_HEIGHT = 4; - private static int DNA_MIN_SEGMENT_HEIGHT = 4; - private static int DNA_WIDTH = 8; - private static int DNA_ALL_DAY_WIDTH = 32; - private static int DNA_SIDE_PADDING = 6; - private static int CONFLICT_COLOR = Color.BLACK; - private static int EVENT_TEXT_COLOR = Color.WHITE; - - private static int DEFAULT_EDGE_SPACING = 0; - private static int SIDE_PADDING_MONTH_NUMBER = 4; - private static int TOP_PADDING_MONTH_NUMBER = 4; - private static int TOP_PADDING_WEEK_NUMBER = 4; - private static int SIDE_PADDING_WEEK_NUMBER = 20; - private static int DAY_SEPARATOR_OUTER_WIDTH = 0; - private static int DAY_SEPARATOR_INNER_WIDTH = 1; - private static int DAY_SEPARATOR_VERTICAL_LENGTH = 53; - private static int DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT = 64; - private static int MIN_WEEK_WIDTH = 50; - - private static int EVENT_X_OFFSET_LANDSCAPE = 38; - private static int EVENT_Y_OFFSET_LANDSCAPE = 8; - private static int EVENT_Y_OFFSET_PORTRAIT = 7; - private static int EVENT_SQUARE_WIDTH = 10; - private static int EVENT_SQUARE_BORDER = 2; - private static int EVENT_LINE_PADDING = 2; - private static int EVENT_RIGHT_PADDING = 4; - private static int EVENT_BOTTOM_PADDING = 3; - - private static int TODAY_HIGHLIGHT_WIDTH = 2; - - private static int SPACING_WEEK_NUMBER = 24; - private static boolean mInitialized = false; - private static boolean mShowDetailsInMonth; - - protected Time mToday = new Time(); - protected boolean mHasToday = false; - protected int mTodayIndex = -1; - protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; - protected List<ArrayList<Event>> mEvents = null; - protected ArrayList<Event> mUnsortedEvents = null; - HashMap<Integer, Utils.DNAStrand> mDna = null; - // This is for drawing the outlines around event chips and supports up to 10 - // events being drawn on each day. The code will expand this if necessary. - protected FloatRef mEventOutlines = new FloatRef(10 * 4 * 4 * 7); - - - - protected static StringBuilder mStringBuilder = new StringBuilder(50); - // TODO recreate formatter when locale changes - protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); - - protected Paint mMonthNamePaint; - protected TextPaint mEventPaint; - protected TextPaint mSolidBackgroundEventPaint; - protected TextPaint mFramedEventPaint; - protected TextPaint mDeclinedEventPaint; - protected TextPaint mEventExtrasPaint; - protected TextPaint mEventDeclinedExtrasPaint; - protected Paint mWeekNumPaint; - protected Paint mDNAAllDayPaint; - protected Paint mDNATimePaint; - protected Paint mEventSquarePaint; - - - protected Drawable mTodayDrawable; - - protected int mMonthNumHeight; - protected int mMonthNumAscentHeight; - protected int mEventHeight; - protected int mEventAscentHeight; - protected int mExtrasHeight; - protected int mExtrasAscentHeight; - protected int mExtrasDescent; - protected int mWeekNumAscentHeight; - - protected int mMonthBGColor; - protected int mMonthBGOtherColor; - protected int mMonthBGTodayColor; - protected int mMonthNumColor; - protected int mMonthNumOtherColor; - protected int mMonthNumTodayColor; - protected int mMonthNameColor; - protected int mMonthNameOtherColor; - protected int mMonthEventColor; - protected int mMonthDeclinedEventColor; - protected int mMonthDeclinedExtrasColor; - protected int mMonthEventExtraColor; - protected int mMonthEventOtherColor; - protected int mMonthEventExtraOtherColor; - protected int mMonthWeekNumColor; - protected int mMonthBusyBitsBgColor; - protected int mMonthBusyBitsBusyTimeColor; - protected int mMonthBusyBitsConflictTimeColor; - private int mClickedDayIndex = -1; - private int mClickedDayColor; - private static final int mClickedAlpha = 128; - - protected int mEventChipOutlineColor = 0xFFFFFFFF; - protected int mDaySeparatorInnerColor; - protected int mTodayAnimateColor; - - private boolean mAnimateToday; - private int mAnimateTodayAlpha = 0; - private ObjectAnimator mTodayAnimator = null; - - private final TodayAnimatorListener mAnimatorListener = 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(MonthWeekEventsView.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; - } - - } - - private int[] mDayXs; - - /** - * This provides a reference to a float array which allows for easy size - * checking and reallocation. Used for drawing lines. - */ - private class FloatRef { - float[] array; - - public FloatRef(int size) { - array = new float[size]; - } - - public void ensureSize(int newSize) { - if (newSize >= array.length) { - // Add enough space for 7 more boxes to be drawn - array = Arrays.copyOf(array, newSize + 16 * 7); - } - } - } - - /** - * Shows up as an error if we don't include this. - */ - public MonthWeekEventsView(Context context) { - super(context); - } - - // Sets the list of events for this week. Takes a sorted list of arrays - // divided up by day for generating the large month version and the full - // arraylist sorted by start time to generate the dna version. - public void setEvents(List<ArrayList<Event>> sortedEvents, ArrayList<Event> unsortedEvents) { - setEvents(sortedEvents); - // The MIN_WEEK_WIDTH is a hack to prevent the view from trying to - // generate dna bits before its width has been fixed. - createDna(unsortedEvents); - } - - /** - * Sets up the dna bits for the view. This will return early if the view - * isn't in a state that will create a valid set of dna yet (such as the - * views width not being set correctly yet). - */ - public void createDna(ArrayList<Event> unsortedEvents) { - if (unsortedEvents == null || mWidth <= MIN_WEEK_WIDTH || getContext() == null) { - // Stash the list of events for use when this view is ready, or - // just clear it if a null set has been passed to this view - mUnsortedEvents = unsortedEvents; - mDna = null; - return; - } else { - // clear the cached set of events since we're ready to build it now - mUnsortedEvents = null; - } - // Create the drawing coordinates for dna - if (!mShowDetailsInMonth) { - int numDays = mEvents.size(); - int effectiveWidth = mWidth - mPadding * 2; - if (mShowWeekNum) { - effectiveWidth -= SPACING_WEEK_NUMBER; - } - DNA_ALL_DAY_WIDTH = effectiveWidth / numDays - 2 * DNA_SIDE_PADDING; - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); - mDayXs = new int[numDays]; - for (int day = 0; day < numDays; day++) { - mDayXs[day] = computeDayLeftPosition(day) + DNA_WIDTH / 2 + DNA_SIDE_PADDING; - - } - - int top = DAY_SEPARATOR_INNER_WIDTH + DNA_MARGIN + DNA_ALL_DAY_HEIGHT + 1; - int bottom = mHeight - DNA_MARGIN; - mDna = Utils.createDNAStrands(mFirstJulianDay, unsortedEvents, top, bottom, - DNA_MIN_SEGMENT_HEIGHT, mDayXs, getContext()); - } - } - - public void setEvents(List<ArrayList<Event>> sortedEvents) { - mEvents = sortedEvents; - if (sortedEvents == null) { - return; - } - if (sortedEvents.size() != mNumDays) { - if (Log.isLoggable(TAG, Log.ERROR)) { - Log.wtf(TAG, "Events size must be same as days displayed: size=" - + sortedEvents.size() + " days=" + mNumDays); - } - mEvents = null; - return; - } - } - - protected void loadColors(Context context) { - Resources res = context.getResources(); - mMonthWeekNumColor = res.getColor(R.color.month_week_num_color); - mMonthNumColor = res.getColor(R.color.month_day_number); - mMonthNumOtherColor = res.getColor(R.color.month_day_number_other); - mMonthNumTodayColor = res.getColor(R.color.month_today_number); - mMonthNameColor = mMonthNumColor; - mMonthNameOtherColor = mMonthNumOtherColor; - mMonthEventColor = res.getColor(R.color.month_event_color); - mMonthDeclinedEventColor = res.getColor(R.color.agenda_item_declined_color); - mMonthDeclinedExtrasColor = res.getColor(R.color.agenda_item_where_declined_text_color); - mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color); - mMonthEventOtherColor = res.getColor(R.color.month_event_other_color); - mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color); - mMonthBGTodayColor = res.getColor(R.color.month_today_bgcolor); - mMonthBGOtherColor = res.getColor(R.color.month_other_bgcolor); - mMonthBGColor = res.getColor(R.color.month_bgcolor); - mDaySeparatorInnerColor = res.getColor(R.color.month_grid_lines); - mTodayAnimateColor = res.getColor(R.color.today_highlight_color); - mClickedDayColor = res.getColor(R.color.day_clicked_background_color); - mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light); - } - - /** - * Sets up the text and style properties for painting. Override this if you - * want to use a different paint. - */ - @Override - protected void initView() { - super.initView(); - - if (!mInitialized) { - Resources resources = getContext().getResources(); - mShowDetailsInMonth = Utils.getConfigBool(getContext(), R.bool.show_details_in_month); - TEXT_SIZE_EVENT_TITLE = resources.getInteger(R.integer.text_size_event_title); - TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number); - SIDE_PADDING_MONTH_NUMBER = resources.getInteger(R.integer.month_day_number_margin); - CONFLICT_COLOR = resources.getColor(R.color.month_dna_conflict_time_color); - EVENT_TEXT_COLOR = resources.getColor(R.color.calendar_event_text_color); - if (mScale != 1) { - TOP_PADDING_MONTH_NUMBER *= mScale; - TOP_PADDING_WEEK_NUMBER *= mScale; - SIDE_PADDING_MONTH_NUMBER *= mScale; - SIDE_PADDING_WEEK_NUMBER *= mScale; - SPACING_WEEK_NUMBER *= mScale; - TEXT_SIZE_MONTH_NUMBER *= mScale; - TEXT_SIZE_EVENT *= mScale; - TEXT_SIZE_EVENT_TITLE *= mScale; - TEXT_SIZE_MORE_EVENTS *= mScale; - TEXT_SIZE_MONTH_NAME *= mScale; - TEXT_SIZE_WEEK_NUM *= mScale; - DAY_SEPARATOR_OUTER_WIDTH *= mScale; - DAY_SEPARATOR_INNER_WIDTH *= mScale; - DAY_SEPARATOR_VERTICAL_LENGTH *= mScale; - DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT *= mScale; - EVENT_X_OFFSET_LANDSCAPE *= mScale; - EVENT_Y_OFFSET_LANDSCAPE *= mScale; - EVENT_Y_OFFSET_PORTRAIT *= mScale; - EVENT_SQUARE_WIDTH *= mScale; - EVENT_SQUARE_BORDER *= mScale; - EVENT_LINE_PADDING *= mScale; - EVENT_BOTTOM_PADDING *= mScale; - EVENT_RIGHT_PADDING *= mScale; - DNA_MARGIN *= mScale; - DNA_WIDTH *= mScale; - DNA_ALL_DAY_HEIGHT *= mScale; - DNA_MIN_SEGMENT_HEIGHT *= mScale; - DNA_SIDE_PADDING *= mScale; - DEFAULT_EDGE_SPACING *= mScale; - DNA_ALL_DAY_WIDTH *= mScale; - TODAY_HIGHLIGHT_WIDTH *= mScale; - } - if (!mShowDetailsInMonth) { - TOP_PADDING_MONTH_NUMBER += DNA_ALL_DAY_HEIGHT + DNA_MARGIN; - } - mInitialized = true; - } - mPadding = DEFAULT_EDGE_SPACING; - loadColors(getContext()); - // TODO modify paint properties depending on isMini - - mMonthNumPaint = new Paint(); - mMonthNumPaint.setFakeBoldText(false); - mMonthNumPaint.setAntiAlias(true); - mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER); - mMonthNumPaint.setColor(mMonthNumColor); - mMonthNumPaint.setStyle(Style.FILL); - mMonthNumPaint.setTextAlign(Align.RIGHT); - mMonthNumPaint.setTypeface(Typeface.DEFAULT); - - mMonthNumAscentHeight = (int) (-mMonthNumPaint.ascent() + 0.5f); - mMonthNumHeight = (int) (mMonthNumPaint.descent() - mMonthNumPaint.ascent() + 0.5f); - - mEventPaint = new TextPaint(); - mEventPaint.setFakeBoldText(true); - mEventPaint.setAntiAlias(true); - mEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); - mEventPaint.setColor(mMonthEventColor); - - mSolidBackgroundEventPaint = new TextPaint(mEventPaint); - mSolidBackgroundEventPaint.setColor(EVENT_TEXT_COLOR); - mFramedEventPaint = new TextPaint(mSolidBackgroundEventPaint); - - mDeclinedEventPaint = new TextPaint(); - mDeclinedEventPaint.setFakeBoldText(true); - mDeclinedEventPaint.setAntiAlias(true); - mDeclinedEventPaint.setTextSize(TEXT_SIZE_EVENT_TITLE); - mDeclinedEventPaint.setColor(mMonthDeclinedEventColor); - - mEventAscentHeight = (int) (-mEventPaint.ascent() + 0.5f); - mEventHeight = (int) (mEventPaint.descent() - mEventPaint.ascent() + 0.5f); - - mEventExtrasPaint = new TextPaint(); - mEventExtrasPaint.setFakeBoldText(false); - mEventExtrasPaint.setAntiAlias(true); - mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT); - mEventExtrasPaint.setColor(mMonthEventExtraColor); - mEventExtrasPaint.setStyle(Style.FILL); - mEventExtrasPaint.setTextAlign(Align.LEFT); - mExtrasHeight = (int)(mEventExtrasPaint.descent() - mEventExtrasPaint.ascent() + 0.5f); - mExtrasAscentHeight = (int)(-mEventExtrasPaint.ascent() + 0.5f); - mExtrasDescent = (int)(mEventExtrasPaint.descent() + 0.5f); - - mEventDeclinedExtrasPaint = new TextPaint(); - mEventDeclinedExtrasPaint.setFakeBoldText(false); - mEventDeclinedExtrasPaint.setAntiAlias(true); - mEventDeclinedExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventDeclinedExtrasPaint.setTextSize(TEXT_SIZE_EVENT); - mEventDeclinedExtrasPaint.setColor(mMonthDeclinedExtrasColor); - mEventDeclinedExtrasPaint.setStyle(Style.FILL); - mEventDeclinedExtrasPaint.setTextAlign(Align.LEFT); - - mWeekNumPaint = new Paint(); - mWeekNumPaint.setFakeBoldText(false); - mWeekNumPaint.setAntiAlias(true); - mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM); - mWeekNumPaint.setColor(mWeekNumColor); - mWeekNumPaint.setStyle(Style.FILL); - mWeekNumPaint.setTextAlign(Align.RIGHT); - - mWeekNumAscentHeight = (int) (-mWeekNumPaint.ascent() + 0.5f); - - mDNAAllDayPaint = new Paint(); - mDNATimePaint = new Paint(); - mDNATimePaint.setColor(mMonthBusyBitsBusyTimeColor); - mDNATimePaint.setStyle(Style.FILL_AND_STROKE); - mDNATimePaint.setStrokeWidth(DNA_WIDTH); - mDNATimePaint.setAntiAlias(false); - mDNAAllDayPaint.setColor(mMonthBusyBitsConflictTimeColor); - mDNAAllDayPaint.setStyle(Style.FILL_AND_STROKE); - mDNAAllDayPaint.setStrokeWidth(DNA_ALL_DAY_WIDTH); - mDNAAllDayPaint.setAntiAlias(false); - - mEventSquarePaint = new Paint(); - mEventSquarePaint.setStrokeWidth(EVENT_SQUARE_BORDER); - mEventSquarePaint.setAntiAlias(false); - - if (DEBUG_LAYOUT) { - Log.d("EXTRA", "mScale=" + mScale); - Log.d("EXTRA", "mMonthNumPaint ascent=" + mMonthNumPaint.ascent() - + " descent=" + mMonthNumPaint.descent() + " int height=" + mMonthNumHeight); - Log.d("EXTRA", "mEventPaint ascent=" + mEventPaint.ascent() - + " descent=" + mEventPaint.descent() + " int height=" + mEventHeight - + " int ascent=" + mEventAscentHeight); - Log.d("EXTRA", "mEventExtrasPaint ascent=" + mEventExtrasPaint.ascent() - + " descent=" + mEventExtrasPaint.descent() + " int height=" + mExtrasHeight); - Log.d("EXTRA", "mWeekNumPaint ascent=" + mWeekNumPaint.ascent() - + " descent=" + mWeekNumPaint.descent()); - } - } - - @Override - public void setWeekParams(HashMap<String, Integer> params, String tz) { - super.setWeekParams(params, tz); - - if (params.containsKey(VIEW_PARAMS_ORIENTATION)) { - mOrientation = params.get(VIEW_PARAMS_ORIENTATION); - } - - updateToday(tz); - mNumCells = mNumDays + 1; - - if (params.containsKey(VIEW_PARAMS_ANIMATE_TODAY) && mHasToday) { - synchronized (mAnimatorListener) { - if (mTodayAnimator != null) { - mTodayAnimator.removeAllListeners(); - mTodayAnimator.cancel(); - } - mTodayAnimator = ObjectAnimator.ofInt(this, "animateTodayAlpha", - Math.max(mAnimateTodayAlpha, 80), 255); - mTodayAnimator.setDuration(150); - mAnimatorListener.setAnimator(mTodayAnimator); - mAnimatorListener.setFadingIn(true); - mTodayAnimator.addListener(mAnimatorListener); - mAnimateToday = true; - mTodayAnimator.start(); - } - } - } - - /** - * @param tz - */ - public boolean updateToday(String tz) { - mToday.timezone = tz; - mToday.setToNow(); - mToday.normalize(true); - int julianToday = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff); - if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) { - mHasToday = true; - mTodayIndex = julianToday - mFirstJulianDay; - } else { - mHasToday = false; - mTodayIndex = -1; - } - return mHasToday; - } - - public void setAnimateTodayAlpha(int alpha) { - mAnimateTodayAlpha = alpha; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - drawBackground(canvas); - drawWeekNums(canvas); - drawDaySeparators(canvas); - if (mHasToday && mAnimateToday) { - drawToday(canvas); - } - if (mShowDetailsInMonth) { - drawEvents(canvas); - } else { - if (mDna == null && mUnsortedEvents != null) { - createDna(mUnsortedEvents); - } - drawDNA(canvas); - } - drawClick(canvas); - } - - protected void drawToday(Canvas canvas) { - r.top = DAY_SEPARATOR_INNER_WIDTH + (TODAY_HIGHLIGHT_WIDTH / 2); - r.bottom = mHeight - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); - p.setStyle(Style.STROKE); - p.setStrokeWidth(TODAY_HIGHLIGHT_WIDTH); - r.left = computeDayLeftPosition(mTodayIndex) + (TODAY_HIGHLIGHT_WIDTH / 2); - r.right = computeDayLeftPosition(mTodayIndex + 1) - - (int) Math.ceil(TODAY_HIGHLIGHT_WIDTH / 2.0f); - p.setColor(mTodayAnimateColor | (mAnimateTodayAlpha << 24)); - canvas.drawRect(r, p); - p.setStyle(Style.FILL); - } - - // TODO move into SimpleWeekView - // Computes the x position for the left side of the given day - private int computeDayLeftPosition(int day) { - int effectiveWidth = mWidth; - int x = 0; - int xOffset = 0; - if (mShowWeekNum) { - xOffset = SPACING_WEEK_NUMBER + mPadding; - effectiveWidth -= xOffset; - } - x = day * effectiveWidth / mNumDays + xOffset; - return x; - } - - @Override - protected void drawDaySeparators(Canvas canvas) { - float lines[] = new float[8 * 4]; - int count = 6 * 4; - int wkNumOffset = 0; - int i = 0; - if (mShowWeekNum) { - // This adds the first line separating the week number - int xOffset = SPACING_WEEK_NUMBER + mPadding; - count += 4; - lines[i++] = xOffset; - lines[i++] = 0; - lines[i++] = xOffset; - lines[i++] = mHeight; - wkNumOffset++; - } - count += 4; - lines[i++] = 0; - lines[i++] = 0; - lines[i++] = mWidth; - lines[i++] = 0; - int y0 = 0; - int y1 = mHeight; - - while (i < count) { - int x = computeDayLeftPosition(i / 4 - wkNumOffset); - lines[i++] = x; - lines[i++] = y0; - lines[i++] = x; - lines[i++] = y1; - } - p.setColor(mDaySeparatorInnerColor); - p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH); - canvas.drawLines(lines, 0, count, p); - } - - @Override - protected void drawBackground(Canvas canvas) { - int i = 0; - int offset = 0; - r.top = DAY_SEPARATOR_INNER_WIDTH; - r.bottom = mHeight; - if (mShowWeekNum) { - i++; - offset++; - } - if (!mOddMonth[i]) { - while (++i < mOddMonth.length && !mOddMonth[i]) - ; - r.right = computeDayLeftPosition(i - offset); - r.left = 0; - p.setColor(mMonthBGOtherColor); - canvas.drawRect(r, p); - // compute left edge for i, set up r, draw - } else if (!mOddMonth[(i = mOddMonth.length - 1)]) { - while (--i >= offset && !mOddMonth[i]) - ; - i++; - // compute left edge for i, set up r, draw - r.right = mWidth; - r.left = computeDayLeftPosition(i - offset); - p.setColor(mMonthBGOtherColor); - canvas.drawRect(r, p); - } - if (mHasToday) { - p.setColor(mMonthBGTodayColor); - r.left = computeDayLeftPosition(mTodayIndex); - r.right = computeDayLeftPosition(mTodayIndex + 1); - canvas.drawRect(r, p); - } - } - - // Draw the "clicked" color on the tapped day - private void drawClick(Canvas canvas) { - if (mClickedDayIndex != -1) { - int alpha = p.getAlpha(); - p.setColor(mClickedDayColor); - p.setAlpha(mClickedAlpha); - r.left = computeDayLeftPosition(mClickedDayIndex); - r.right = computeDayLeftPosition(mClickedDayIndex + 1); - r.top = DAY_SEPARATOR_INNER_WIDTH; - r.bottom = mHeight; - canvas.drawRect(r, p); - p.setAlpha(alpha); - } - } - - @Override - protected void drawWeekNums(Canvas canvas) { - int y; - - int i = 0; - int offset = -1; - int todayIndex = mTodayIndex; - int x = 0; - int numCount = mNumDays; - if (mShowWeekNum) { - x = SIDE_PADDING_WEEK_NUMBER + mPadding; - y = mWeekNumAscentHeight + TOP_PADDING_WEEK_NUMBER; - canvas.drawText(mDayNumbers[0], x, y, mWeekNumPaint); - numCount++; - i++; - todayIndex++; - offset++; - - } - - y = mMonthNumAscentHeight + TOP_PADDING_MONTH_NUMBER; - - boolean isFocusMonth = mFocusDay[i]; - boolean isBold = false; - mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); - for (; i < numCount; i++) { - if (mHasToday && todayIndex == i) { - mMonthNumPaint.setColor(mMonthNumTodayColor); - mMonthNumPaint.setFakeBoldText(isBold = true); - if (i + 1 < numCount) { - // Make sure the color will be set back on the next - // iteration - isFocusMonth = !mFocusDay[i + 1]; - } - } else if (mFocusDay[i] != isFocusMonth) { - isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); - } - x = computeDayLeftPosition(i - offset) - (SIDE_PADDING_MONTH_NUMBER); - canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); - if (isBold) { - mMonthNumPaint.setFakeBoldText(isBold = false); - } - } - } - - protected void drawEvents(Canvas canvas) { - if (mEvents == null) { - return; - } - - int day = -1; - for (ArrayList<Event> eventDay : mEvents) { - day++; - if (eventDay == null || eventDay.size() == 0) { - continue; - } - int ySquare; - int xSquare = computeDayLeftPosition(day) + SIDE_PADDING_MONTH_NUMBER + 1; - int rightEdge = computeDayLeftPosition(day + 1); - - if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { - ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + TOP_PADDING_MONTH_NUMBER; - rightEdge -= SIDE_PADDING_MONTH_NUMBER + 1; - } else { - ySquare = EVENT_Y_OFFSET_LANDSCAPE; - rightEdge -= EVENT_X_OFFSET_LANDSCAPE; - } - - // Determine if everything will fit when time ranges are shown. - boolean showTimes = true; - Iterator<Event> iter = eventDay.iterator(); - int yTest = ySquare; - while (iter.hasNext()) { - Event event = iter.next(); - int newY = drawEvent(canvas, event, xSquare, yTest, rightEdge, iter.hasNext(), - showTimes, /*doDraw*/ false); - if (newY == yTest) { - showTimes = false; - break; - } - yTest = newY; - } - - int eventCount = 0; - iter = eventDay.iterator(); - while (iter.hasNext()) { - Event event = iter.next(); - int newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext(), - showTimes, /*doDraw*/ true); - if (newY == ySquare) { - break; - } - eventCount++; - ySquare = newY; - } - - int remaining = eventDay.size() - eventCount; - if (remaining > 0) { - drawMoreEvents(canvas, remaining, xSquare); - } - } - } - - protected int addChipOutline(FloatRef lines, int count, int x, int y) { - lines.ensureSize(count + 16); - // top of box - lines.array[count++] = x; - lines.array[count++] = y; - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y; - // right side of box - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y; - lines.array[count++] = x + EVENT_SQUARE_WIDTH; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - // left side of box - lines.array[count++] = x; - lines.array[count++] = y; - lines.array[count++] = x; - lines.array[count++] = y + EVENT_SQUARE_WIDTH + 1; - // bottom of box - lines.array[count++] = x; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - lines.array[count++] = x + EVENT_SQUARE_WIDTH + 1; - lines.array[count++] = y + EVENT_SQUARE_WIDTH; - - return count; - } - - /** - * Attempts to draw the given event. Returns the y for the next event or the - * original y if the event will not fit. An event is considered to not fit - * if the event and its extras won't fit or if there are more events and the - * more events line would not fit after drawing this event. - * - * @param canvas the canvas to draw on - * @param event the event to draw - * @param x the top left corner for this event's color chip - * @param y the top left corner for this event's color chip - * @param rightEdge the rightmost point we're allowed to draw on (exclusive) - * @param moreEvents indicates whether additional events will follow this one - * @param showTimes if set, a second line with a time range will be displayed for non-all-day - * events - * @param doDraw if set, do the actual drawing; otherwise this just computes the height - * and returns - * @return the y for the next event or the original y if it won't fit - */ - protected int drawEvent(Canvas canvas, Event event, int x, int y, int rightEdge, - boolean moreEvents, boolean showTimes, boolean doDraw) { - /* - * Vertical layout: - * (top of box) - * a. EVENT_Y_OFFSET_LANDSCAPE or portrait equivalent - * b. Event title: mEventHeight for a normal event, + 2xBORDER_SPACE for all-day event - * c. [optional] Time range (mExtrasHeight) - * d. EVENT_LINE_PADDING - * - * Repeat (b,c,d) as needed and space allows. If we have more events than fit, we need - * to leave room for something like "+2" at the bottom: - * - * e. "+ more" line (mExtrasHeight) - * - * f. EVENT_BOTTOM_PADDING (overlaps EVENT_LINE_PADDING) - * (bottom of box) - */ - final int BORDER_SPACE = EVENT_SQUARE_BORDER + 1; // want a 1-pixel gap inside border - final int STROKE_WIDTH_ADJ = EVENT_SQUARE_BORDER / 2; // adjust bounds for stroke width - boolean allDay = event.allDay; - int eventRequiredSpace = mEventHeight; - if (allDay) { - // Add a few pixels for the box we draw around all-day events. - eventRequiredSpace += BORDER_SPACE * 2; - } else if (showTimes) { - // Need room for the "1pm - 2pm" line. - eventRequiredSpace += mExtrasHeight; - } - int reservedSpace = EVENT_BOTTOM_PADDING; // leave a bit of room at the bottom - if (moreEvents) { - // More events follow. Leave a bit of space between events. - eventRequiredSpace += EVENT_LINE_PADDING; - - // Make sure we have room for the "+ more" line. (The "+ more" line is expected - // to be <= the height of an event line, so we won't show "+1" when we could be - // showing the event.) - reservedSpace += mExtrasHeight; - } - - if (y + eventRequiredSpace + reservedSpace > mHeight) { - // Not enough space, return original y - return y; - } else if (!doDraw) { - return y + eventRequiredSpace; - } - - boolean isDeclined = event.selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED; - int color = event.color; - if (isDeclined) { - color = Utils.getDeclinedColorFromColor(color); - } - - int textX, textY, textRightEdge; - - if (allDay) { - // We shift the render offset "inward", because drawRect with a stroke width greater - // than 1 draws outside the specified bounds. (We don't adjust the left edge, since - // we want to match the existing appearance of the "event square".) - r.left = x; - r.right = rightEdge - STROKE_WIDTH_ADJ; - r.top = y + STROKE_WIDTH_ADJ; - r.bottom = y + mEventHeight + BORDER_SPACE * 2 - STROKE_WIDTH_ADJ; - textX = x + BORDER_SPACE; - textY = y + mEventAscentHeight + BORDER_SPACE; - textRightEdge = rightEdge - BORDER_SPACE; - } else { - r.left = x; - r.right = x + EVENT_SQUARE_WIDTH; - r.bottom = y + mEventAscentHeight; - r.top = r.bottom - EVENT_SQUARE_WIDTH; - textX = x + EVENT_SQUARE_WIDTH + EVENT_RIGHT_PADDING; - textY = y + mEventAscentHeight; - textRightEdge = rightEdge; - } - - Style boxStyle = Style.STROKE; - boolean solidBackground = false; - if (event.selfAttendeeStatus != Attendees.ATTENDEE_STATUS_INVITED) { - boxStyle = Style.FILL_AND_STROKE; - if (allDay) { - solidBackground = true; - } - } - mEventSquarePaint.setStyle(boxStyle); - mEventSquarePaint.setColor(color); - canvas.drawRect(r, mEventSquarePaint); - - float avail = textRightEdge - textX; - CharSequence text = TextUtils.ellipsize( - event.title, mEventPaint, avail, TextUtils.TruncateAt.END); - Paint textPaint; - if (solidBackground) { - // Text color needs to contrast with solid background. - textPaint = mSolidBackgroundEventPaint; - } else if (isDeclined) { - // Use "declined event" color. - textPaint = mDeclinedEventPaint; - } else if (allDay) { - // Text inside frame is same color as frame. - mFramedEventPaint.setColor(color); - textPaint = mFramedEventPaint; - } else { - // Use generic event text color. - textPaint = mEventPaint; - } - canvas.drawText(text.toString(), textX, textY, textPaint); - y += mEventHeight; - if (allDay) { - y += BORDER_SPACE * 2; - } - - if (showTimes && !allDay) { - // show start/end time, e.g. "1pm - 2pm" - textY = y + mExtrasAscentHeight; - mStringBuilder.setLength(0); - text = DateUtils.formatDateRange(getContext(), mFormatter, event.startMillis, - event.endMillis, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL, - Utils.getTimeZone(getContext(), null)).toString(); - text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END); - canvas.drawText(text.toString(), textX, textY, isDeclined ? mEventDeclinedExtrasPaint - : mEventExtrasPaint); - y += mExtrasHeight; - } - - y += EVENT_LINE_PADDING; - - return y; - } - - protected void drawMoreEvents(Canvas canvas, int remainingEvents, int x) { - int y = mHeight - (mExtrasDescent + EVENT_BOTTOM_PADDING); - String text = getContext().getResources().getQuantityString( - R.plurals.month_more_events, remainingEvents); - mEventExtrasPaint.setAntiAlias(true); - mEventExtrasPaint.setFakeBoldText(true); - canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint); - mEventExtrasPaint.setFakeBoldText(false); - } - - /** - * Draws a line showing busy times in each day of week The method draws - * non-conflicting times in the event color and times with conflicting - * events in the dna conflict color defined in colors. - * - * @param canvas - */ - protected void drawDNA(Canvas canvas) { - // Draw event and conflict times - if (mDna != null) { - for (Utils.DNAStrand strand : mDna.values()) { - if (strand.color == CONFLICT_COLOR || strand.points == null - || strand.points.length == 0) { - continue; - } - mDNATimePaint.setColor(strand.color); - canvas.drawLines(strand.points, mDNATimePaint); - } - // Draw black last to make sure it's on top - Utils.DNAStrand strand = mDna.get(CONFLICT_COLOR); - if (strand != null && strand.points != null && strand.points.length != 0) { - mDNATimePaint.setColor(strand.color); - canvas.drawLines(strand.points, mDNATimePaint); - } - if (mDayXs == null) { - return; - } - int numDays = mDayXs.length; - int xOffset = (DNA_ALL_DAY_WIDTH - DNA_WIDTH) / 2; - if (strand != null && strand.allDays != null && strand.allDays.length == numDays) { - for (int i = 0; i < numDays; i++) { - // this adds at most 7 draws. We could sort it by color and - // build an array instead but this is easier. - if (strand.allDays[i] != 0) { - mDNAAllDayPaint.setColor(strand.allDays[i]); - canvas.drawLine(mDayXs[i] + xOffset, DNA_MARGIN, mDayXs[i] + xOffset, - DNA_MARGIN + DNA_ALL_DAY_HEIGHT, mDNAAllDayPaint); - } - } - } - } - } - - @Override - protected void updateSelectionPositions() { - if (mHasSelectedDay) { - int selectedPosition = mSelectedDay - mWeekStart; - if (selectedPosition < 0) { - selectedPosition += 7; - } - int effectiveWidth = mWidth - mPadding * 2; - effectiveWidth -= SPACING_WEEK_NUMBER; - mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding; - mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding; - mSelectedLeft += SPACING_WEEK_NUMBER; - mSelectedRight += SPACING_WEEK_NUMBER; - } - } - - public int getDayIndexFromLocation(float x) { - int dayStart = mShowWeekNum ? SPACING_WEEK_NUMBER + mPadding : mPadding; - if (x < dayStart || x > mWidth - mPadding) { - return -1; - } - // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels - return ((int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding))); - } - - @Override - public Time getDayFromLocation(float x) { - int dayPosition = getDayIndexFromLocation(x); - if (dayPosition == -1) { - return null; - } - int day = mFirstJulianDay + dayPosition; - - Time time = new Time(mTimeZone); - if (mWeek == 0) { - // This week is weird... - if (day < Time.EPOCH_JULIAN_DAY) { - day++; - } else if (day == Time.EPOCH_JULIAN_DAY) { - time.set(1, 0, 1970); - time.normalize(true); - return time; - } - } - - time.setJulianDay(day); - return time; - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - Context context = getContext(); - // only send accessibility events if accessibility and exploration are - // on. - AccessibilityManager am = (AccessibilityManager) context - .getSystemService(Service.ACCESSIBILITY_SERVICE); - if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { - return super.onHoverEvent(event); - } - if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { - Time hover = getDayFromLocation(event.getX()); - if (hover != null - && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { - Long millis = hover.toMillis(true); - String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE); - AccessibilityEvent accessEvent = AccessibilityEvent - .obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - accessEvent.getText().add(date); - if (mShowDetailsInMonth && mEvents != null) { - int dayStart = SPACING_WEEK_NUMBER + mPadding; - int dayPosition = (int) ((event.getX() - dayStart) * mNumDays / (mWidth - - dayStart - mPadding)); - ArrayList<Event> events = mEvents.get(dayPosition); - List<CharSequence> text = accessEvent.getText(); - for (Event e : events) { - text.add(e.getTitleAndLocation() + ". "); - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; - if (!e.allDay) { - flags |= DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(context)) { - flags |= DateUtils.FORMAT_24HOUR; - } - } else { - flags |= DateUtils.FORMAT_UTC; - } - text.add(Utils.formatDateRange(context, e.startMillis, e.endMillis, - flags) + ". "); - } - } - sendAccessibilityEventUnchecked(accessEvent); - mLastHoverTime = hover; - } - } - return true; - } - - public void setClickedDay(float xLocation) { - mClickedDayIndex = getDayIndexFromLocation(xLocation); - invalidate(); - } - public void clearClickedDay() { - mClickedDayIndex = -1; - invalidate(); - } -} diff --git a/src/com/android/calendar/month/SimpleDayPickerFragment.java b/src/com/android/calendar/month/SimpleDayPickerFragment.java deleted file mode 100644 index 2efae6a9..00000000 --- a/src/com/android/calendar/month/SimpleDayPickerFragment.java +++ /dev/null @@ -1,612 +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.month; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.app.Activity; -import android.app.ListFragment; -import android.content.Context; -import android.content.res.Resources; -import android.database.DataSetObserver; -import android.os.Bundle; -import android.os.Handler; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.ListView; -import android.widget.TextView; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Locale; - -/** - * <p> - * This displays a titled list of weeks with selectable days. It can be - * configured to display the week number, start the week on a given day, show a - * reduced number of days, or display an arbitrary number of weeks at a time. By - * overriding methods and changing variables this fragment can be customized to - * easily display a month selection component in a given style. - * </p> - */ -public class SimpleDayPickerFragment extends ListFragment implements OnScrollListener { - - private static final String TAG = "MonthFragment"; - private static final String KEY_CURRENT_TIME = "current_time"; - - // Affects when the month selection will change while scrolling up - protected static final int SCROLL_HYST_WEEKS = 2; - // How long the GoTo fling animation should last - protected static final int GOTO_SCROLL_DURATION = 500; - // How long to wait after receiving an onScrollStateChanged notification - // before acting on it - protected static final int SCROLL_CHANGE_DELAY = 40; - // The number of days to display in each week - public static final int DAYS_PER_WEEK = 7; - // The size of the month name displayed above the week list - protected static final int MINI_MONTH_NAME_TEXT_SIZE = 18; - public static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator - protected int WEEK_MIN_VISIBLE_HEIGHT = 12; - protected int BOTTOM_BUFFER = 20; - protected int mSaturdayColor = 0; - protected int mSundayColor = 0; - protected int mDayNameColor = 0; - - // You can override these numbers to get a different appearance - protected int mNumWeeks = 6; - protected boolean mShowWeekNumber = false; - protected int mDaysPerWeek = 7; - - // These affect the scroll speed and feel - protected float mFriction = 1.0f; - - protected Context mContext; - protected Handler mHandler; - - protected float mMinimumFlingVelocity; - - // highlighted time - protected Time mSelectedDay = new Time(); - protected SimpleWeeksAdapter mAdapter; - protected ListView mListView; - protected ViewGroup mDayNamesHeader; - protected String[] mDayLabels; - - // disposable variable used for time calculations - protected Time mTempTime = new Time(); - - private static float mScale = 0; - // When the week starts; numbered like Time.<WEEKDAY> (e.g. SUNDAY=0). - protected int mFirstDayOfWeek; - // The first day of the focus month - protected Time mFirstDayOfMonth = new Time(); - // The first day that is visible in the view - protected Time mFirstVisibleDay = new Time(); - // The name of the month to display - protected TextView mMonthName; - // The last name announced by accessibility - protected CharSequence mPrevMonthName; - // which month should be displayed/highlighted [0-11] - protected int mCurrentMonthDisplayed; - // used for tracking during a scroll - protected long mPreviousScrollPosition; - // used for tracking which direction the view is scrolling - protected boolean mIsScrollingUp = false; - // used for tracking what state listview is in - protected int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE; - // used for tracking what state listview is in - protected int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE; - - // This causes an update of the view at midnight - protected Runnable mTodayUpdater = new Runnable() { - @Override - public void run() { - Time midnight = new Time(mFirstVisibleDay.timezone); - midnight.setToNow(); - long currentMillis = midnight.toMillis(true); - - midnight.hour = 0; - midnight.minute = 0; - midnight.second = 0; - midnight.monthDay++; - long millisToMidnight = midnight.normalize(true) - currentMillis; - mHandler.postDelayed(this, millisToMidnight); - - if (mAdapter != null) { - mAdapter.notifyDataSetChanged(); - } - } - }; - - // This allows us to update our position when a day is tapped - protected DataSetObserver mObserver = new DataSetObserver() { - @Override - public void onChanged() { - Time day = mAdapter.getSelectedDay(); - if (day.year != mSelectedDay.year || day.yearDay != mSelectedDay.yearDay) { - goTo(day.toMillis(true), true, true, false); - } - } - }; - - public SimpleDayPickerFragment(long initialTime) { - goTo(initialTime, false, true, true); - mHandler = new Handler(); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mContext = activity; - String tz = Time.getCurrentTimezone(); - ViewConfiguration viewConfig = ViewConfiguration.get(activity); - mMinimumFlingVelocity = viewConfig.getScaledMinimumFlingVelocity(); - - // Ensure we're in the correct time zone - mSelectedDay.switchTimezone(tz); - mSelectedDay.normalize(true); - mFirstDayOfMonth.timezone = tz; - mFirstDayOfMonth.normalize(true); - mFirstVisibleDay.timezone = tz; - mFirstVisibleDay.normalize(true); - mTempTime.timezone = tz; - - Resources res = activity.getResources(); - mSaturdayColor = res.getColor(R.color.month_saturday); - mSundayColor = res.getColor(R.color.month_sunday); - mDayNameColor = res.getColor(R.color.month_day_names_color); - - // Adjust sizes for screen density - if (mScale == 0) { - mScale = activity.getResources().getDisplayMetrics().density; - if (mScale != 1) { - WEEK_MIN_VISIBLE_HEIGHT *= mScale; - BOTTOM_BUFFER *= mScale; - LIST_TOP_OFFSET *= mScale; - } - } - setUpAdapter(); - setListAdapter(mAdapter); - } - - /** - * Creates a new adapter if necessary and sets up its parameters. Override - * this method to provide a custom adapter. - */ - protected void setUpAdapter() { - HashMap<String, Integer> weekParams = new HashMap<String, Integer>(); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_NUM_WEEKS, mNumWeeks); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_SHOW_WEEK, mShowWeekNumber ? 1 : 0); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_WEEK_START, mFirstDayOfWeek); - weekParams.put(SimpleWeeksAdapter.WEEK_PARAMS_JULIAN_DAY, - Time.getJulianDay(mSelectedDay.toMillis(false), mSelectedDay.gmtoff)); - if (mAdapter == null) { - mAdapter = new SimpleWeeksAdapter(getActivity(), weekParams); - mAdapter.registerDataSetObserver(mObserver); - } else { - mAdapter.updateParams(weekParams); - } - // refresh the view with the new parameters - mAdapter.notifyDataSetChanged(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - setUpListView(); - setUpHeader(); - - mMonthName = (TextView) getView().findViewById(R.id.month_name); - SimpleWeekView child = (SimpleWeekView) mListView.getChildAt(0); - if (child == null) { - return; - } - int julianDay = child.getFirstJulianDay(); - mFirstVisibleDay.setJulianDay(julianDay); - // set the title to the month of the second week - mTempTime.setJulianDay(julianDay + DAYS_PER_WEEK); - setMonthDisplayed(mTempTime, true); - } - - /** - * Sets up the strings to be used by the header. Override this method to use - * different strings or modify the view params. - */ - protected void setUpHeader() { - mDayLabels = new String[7]; - for (int i = Calendar.SUNDAY; i <= Calendar.SATURDAY; i++) { - mDayLabels[i - Calendar.SUNDAY] = DateUtils.getDayOfWeekString(i, - DateUtils.LENGTH_SHORTEST).toUpperCase(); - } - } - - /** - * Sets all the required fields for the list view. Override this method to - * set a different list view behavior. - */ - protected void setUpListView() { - // Configure the listview - mListView = getListView(); - // Transparent background on scroll - mListView.setCacheColorHint(0); - // No dividers - mListView.setDivider(null); - // Items are clickable - mListView.setItemsCanFocus(true); - // The thumb gets in the way, so disable it - mListView.setFastScrollEnabled(false); - mListView.setVerticalScrollBarEnabled(false); - mListView.setOnScrollListener(this); - mListView.setFadingEdgeLength(0); - // Make the scrolling behavior nicer - mListView.setFriction(ViewConfiguration.getScrollFriction() * mFriction); - } - - @Override - public void onResume() { - super.onResume(); - setUpAdapter(); - doResumeUpdates(); - } - - @Override - public void onPause() { - super.onPause(); - mHandler.removeCallbacks(mTodayUpdater); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putLong(KEY_CURRENT_TIME, mSelectedDay.toMillis(true)); - } - - /** - * Updates the user preference fields. Override this to use a different - * preference space. - */ - protected void doResumeUpdates() { - // Get default week start based on locale, subtracting one for use with android Time. - Calendar cal = Calendar.getInstance(Locale.getDefault()); - mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; - - mShowWeekNumber = false; - - updateHeader(); - goTo(mSelectedDay.toMillis(true), false, false, false); - mAdapter.setSelectedDay(mSelectedDay); - mTodayUpdater.run(); - } - - /** - * Fixes the day names header to provide correct spacing and updates the - * label text. Override this to set up a custom header. - */ - protected void updateHeader() { - TextView label = (TextView) mDayNamesHeader.findViewById(R.id.wk_label); - if (mShowWeekNumber) { - label.setVisibility(View.VISIBLE); - } else { - label.setVisibility(View.GONE); - } - int offset = mFirstDayOfWeek - 1; - for (int i = 1; i < 8; i++) { - label = (TextView) mDayNamesHeader.getChildAt(i); - if (i < mDaysPerWeek + 1) { - int position = (offset + i) % 7; - label.setText(mDayLabels[position]); - label.setVisibility(View.VISIBLE); - if (position == Time.SATURDAY) { - label.setTextColor(mSaturdayColor); - } else if (position == Time.SUNDAY) { - label.setTextColor(mSundayColor); - } else { - label.setTextColor(mDayNameColor); - } - } else { - label.setVisibility(View.GONE); - } - } - mDayNamesHeader.invalidate(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.month_by_week, - container, false); - mDayNamesHeader = (ViewGroup) v.findViewById(R.id.day_names); - return v; - } - - /** - * Returns the UTC millis since epoch representation of the currently - * selected time. - * - * @return - */ - public long getSelectedTime() { - return mSelectedDay.toMillis(true); - } - - /** - * This moves to the specified time in the view. If the time is not already - * in range it will move the list so that the first of the month containing - * the time is at the top of the view. If the new time is already in view - * the list will not be scrolled unless forceScroll is true. This time may - * optionally be highlighted as selected as well. - * - * @param time The time to move to - * @param animate Whether to scroll to the given time or just redraw at the - * new location - * @param setSelected Whether to set the given time as selected - * @param forceScroll Whether to recenter even if the time is already - * visible - * @return Whether or not the view animated to the new location - */ - public boolean goTo(long time, boolean animate, boolean setSelected, boolean forceScroll) { - if (time == -1) { - Log.e(TAG, "time is invalid"); - return false; - } - - // Set the selected day - if (setSelected) { - mSelectedDay.set(time); - mSelectedDay.normalize(true); - } - - // If this view isn't returned yet we won't be able to load the lists - // current position, so return after setting the selected day. - if (!isResumed()) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "We're not visible yet"); - } - return false; - } - - mTempTime.set(time); - long millis = mTempTime.normalize(true); - // Get the week we're going to - // TODO push Util function into Calendar public api. - int position = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mTempTime.gmtoff), mFirstDayOfWeek); - - View child; - int i = 0; - int top = 0; - // Find a child that's completely in the view - do { - child = mListView.getChildAt(i++); - if (child == null) { - break; - } - top = child.getTop(); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "child at " + (i-1) + " has top " + top); - } - } while (top < 0); - - // Compute the first and last position visible - int firstPosition; - if (child != null) { - firstPosition = mListView.getPositionForView(child); - } else { - firstPosition = 0; - } - int lastPosition = firstPosition + mNumWeeks - 1; - if (top > BOTTOM_BUFFER) { - lastPosition--; - } - - if (setSelected) { - mAdapter.setSelectedDay(mSelectedDay); - } - - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "GoTo position " + position); - } - // Check if the selected day is now outside of our visible range - // and if so scroll to the month that contains it - if (position < firstPosition || position > lastPosition || forceScroll) { - mFirstDayOfMonth.set(mTempTime); - mFirstDayOfMonth.monthDay = 1; - millis = mFirstDayOfMonth.normalize(true); - setMonthDisplayed(mFirstDayOfMonth, true); - position = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mFirstDayOfMonth.gmtoff), mFirstDayOfWeek); - - mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING; - if (animate) { - mListView.smoothScrollToPositionFromTop( - position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION); - return true; - } else { - mListView.setSelectionFromTop(position, LIST_TOP_OFFSET); - // Perform any after scroll operations that are needed - onScrollStateChanged(mListView, OnScrollListener.SCROLL_STATE_IDLE); - } - } else if (setSelected) { - // Otherwise just set the selection - setMonthDisplayed(mSelectedDay, true); - } - return false; - } - - /** - * Updates the title and selected month if the view has moved to a new - * month. - */ - @Override - public void onScroll( - AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - SimpleWeekView child = (SimpleWeekView)view.getChildAt(0); - if (child == null) { - return; - } - - // Figure out where we are - long currScroll = view.getFirstVisiblePosition() * child.getHeight() - child.getBottom(); - mFirstVisibleDay.setJulianDay(child.getFirstJulianDay()); - - // If we have moved since our last call update the direction - if (currScroll < mPreviousScrollPosition) { - mIsScrollingUp = true; - } else if (currScroll > mPreviousScrollPosition) { - mIsScrollingUp = false; - } else { - return; - } - - mPreviousScrollPosition = currScroll; - mPreviousScrollState = mCurrentScrollState; - - updateMonthHighlight(mListView); - } - - /** - * Figures out if the month being shown has changed and updates the - * highlight if needed - * - * @param view The ListView containing the weeks - */ - private void updateMonthHighlight(AbsListView view) { - SimpleWeekView child = (SimpleWeekView) view.getChildAt(0); - if (child == null) { - return; - } - - // Figure out where we are - int offset = child.getBottom() < WEEK_MIN_VISIBLE_HEIGHT ? 1 : 0; - // Use some hysteresis for checking which month to highlight. This - // causes the month to transition when two full weeks of a month are - // visible. - child = (SimpleWeekView) view.getChildAt(SCROLL_HYST_WEEKS + offset); - - if (child == null) { - return; - } - - // Find out which month we're moving into - int month; - if (mIsScrollingUp) { - month = child.getFirstMonth(); - } else { - month = child.getLastMonth(); - } - - // And how it relates to our current highlighted month - int monthDiff; - if (mCurrentMonthDisplayed == 11 && month == 0) { - monthDiff = 1; - } else if (mCurrentMonthDisplayed == 0 && month == 11) { - monthDiff = -1; - } else { - monthDiff = month - mCurrentMonthDisplayed; - } - - // Only switch months if we're scrolling away from the currently - // selected month - if (monthDiff != 0) { - int julianDay = child.getFirstJulianDay(); - if (mIsScrollingUp) { - // Takes the start of the week - } else { - // Takes the start of the following week - julianDay += DAYS_PER_WEEK; - } - mTempTime.setJulianDay(julianDay); - setMonthDisplayed(mTempTime, false); - } - } - - /** - * Sets the month displayed at the top of this view based on time. Override - * to add custom events when the title is changed. - * - * @param time A day in the new focus month. - * @param updateHighlight TODO(epastern): - */ - protected void setMonthDisplayed(Time time, boolean updateHighlight) { - CharSequence oldMonth = mMonthName.getText(); - mMonthName.setText(Utils.formatMonthYear(mContext, time)); - mMonthName.invalidate(); - if (!TextUtils.equals(oldMonth, mMonthName.getText())) { - mMonthName.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); - } - mCurrentMonthDisplayed = time.month; - if (updateHighlight) { - mAdapter.updateFocusMonth(mCurrentMonthDisplayed); - } - } - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - // use a post to prevent re-entering onScrollStateChanged before it - // exits - mScrollStateChangedRunnable.doScrollStateChange(view, scrollState); - } - - protected ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(); - - protected class ScrollStateRunnable implements Runnable { - private int mNewState; - - /** - * Sets up the runnable with a short delay in case the scroll state - * immediately changes again. - * - * @param view The list view that changed state - * @param scrollState The new state it changed to - */ - public void doScrollStateChange(AbsListView view, int scrollState) { - mHandler.removeCallbacks(this); - mNewState = scrollState; - mHandler.postDelayed(this, SCROLL_CHANGE_DELAY); - } - - public void run() { - mCurrentScrollState = mNewState; - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, - "new scroll state: " + mNewState + " old state: " + mPreviousScrollState); - } - // Fix the position after a scroll or a fling ends - if (mNewState == OnScrollListener.SCROLL_STATE_IDLE - && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE) { - mPreviousScrollState = mNewState; - mAdapter.updateFocusMonth(mCurrentMonthDisplayed); - } else { - mPreviousScrollState = mNewState; - } - } - } -} diff --git a/src/com/android/calendar/month/SimpleWeekView.java b/src/com/android/calendar/month/SimpleWeekView.java deleted file mode 100644 index 4d0c09f4..00000000 --- a/src/com/android/calendar/month/SimpleWeekView.java +++ /dev/null @@ -1,551 +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.month; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.app.Service; -import android.content.Context; -import android.content.res.Resources; -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.drawable.Drawable; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.view.MotionEvent; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import java.security.InvalidParameterException; -import java.util.HashMap; - -/** - * <p> - * This is a dynamic view for drawing a single week. It can be configured to - * display the week number, start the week on a given day, or show a reduced - * number of days. It is intended for use as a single view within a ListView. - * See {@link SimpleWeeksAdapter} for usage. - * </p> - */ -public class SimpleWeekView extends View { - private static final String TAG = "MonthView"; - - /** - * These params can be passed into the view to control how it appears. - * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default - * values are unlikely to fit most layouts correctly. - */ - /** - * This sets the height of this week in pixels - */ - public static final String VIEW_PARAMS_HEIGHT = "height"; - /** - * This specifies the position (or weeks since the epoch) of this week, - * calculated using {@link Utils#getWeeksSinceEpochFromJulianDay} - */ - public static final String VIEW_PARAMS_WEEK = "week"; - /** - * This sets one of the days in this view as selected {@link Time#SUNDAY} - * through {@link Time#SATURDAY}. - */ - public static final String VIEW_PARAMS_SELECTED_DAY = "selected_day"; - /** - * Which day the week should start on. {@link Time#SUNDAY} through - * {@link Time#SATURDAY}. - */ - public static final String VIEW_PARAMS_WEEK_START = "week_start"; - /** - * How many days to display at a time. Days will be displayed starting with - * {@link #mWeekStart}. - */ - public static final String VIEW_PARAMS_NUM_DAYS = "num_days"; - /** - * Which month is currently in focus, as defined by {@link Time#month} - * [0-11]. - */ - public static final String VIEW_PARAMS_FOCUS_MONTH = "focus_month"; - /** - * If this month should display week numbers. false if 0, true otherwise. - */ - public static final String VIEW_PARAMS_SHOW_WK_NUM = "show_wk_num"; - - protected static int DEFAULT_HEIGHT = 32; - protected static int MIN_HEIGHT = 10; - protected static final int DEFAULT_SELECTED_DAY = -1; - protected static final int DEFAULT_WEEK_START = Time.SUNDAY; - protected static final int DEFAULT_NUM_DAYS = 7; - protected static final int DEFAULT_SHOW_WK_NUM = 0; - protected static final int DEFAULT_FOCUS_MONTH = -1; - - protected static int DAY_SEPARATOR_WIDTH = 1; - - protected static int MINI_DAY_NUMBER_TEXT_SIZE = 14; - protected static int MINI_WK_NUMBER_TEXT_SIZE = 12; - protected static int MINI_TODAY_NUMBER_TEXT_SIZE = 18; - protected static int MINI_TODAY_OUTLINE_WIDTH = 2; - protected static int WEEK_NUM_MARGIN_BOTTOM = 4; - - // used for scaling to the device density - protected static float mScale = 0; - - // affects the padding on the sides of this view - protected int mPadding = 0; - - protected Rect r = new Rect(); - protected Paint p = new Paint(); - protected Paint mMonthNumPaint; - protected Drawable mSelectedDayLine; - - // Cache the number strings so we don't have to recompute them each time - protected String[] mDayNumbers; - // Quick lookup for checking which days are in the focus month - protected boolean[] mFocusDay; - // Quick lookup for checking which days are in an odd month (to set a different background) - protected boolean[] mOddMonth; - // The Julian day of the first day displayed by this item - protected int mFirstJulianDay = -1; - // The month of the first day in this week - protected int mFirstMonth = -1; - // The month of the last day in this week - protected int mLastMonth = -1; - // The position of this week, equivalent to weeks since the week of Jan 1st, - // 1970 - protected int mWeek = -1; - // Quick reference to the width of this view, matches parent - protected int mWidth; - // The height this view should draw at in pixels, set by height param - protected int mHeight = DEFAULT_HEIGHT; - // Whether the week number should be shown - protected boolean mShowWeekNum = false; - // If this view contains the selected day - protected boolean mHasSelectedDay = false; - // If this view contains the today - protected boolean mHasToday = false; - // Which day is selected [0-6] or -1 if no day is selected - protected int mSelectedDay = DEFAULT_SELECTED_DAY; - // Which day is today [0-6] or -1 if no day is today - protected int mToday = DEFAULT_SELECTED_DAY; - // Which day of the week to start on [0-6] - protected int mWeekStart = DEFAULT_WEEK_START; - // How many days to display - protected int mNumDays = DEFAULT_NUM_DAYS; - // The number of days + a spot for week number if it is displayed - protected int mNumCells = mNumDays; - // The left edge of the selected day - protected int mSelectedLeft = -1; - // The right edge of the selected day - protected int mSelectedRight = -1; - // The timezone to display times/dates in (used for determining when Today - // is) - protected String mTimeZone = Time.getCurrentTimezone(); - - protected int mBGColor; - protected int mSelectedWeekBGColor; - protected int mFocusMonthColor; - protected int mOtherMonthColor; - protected int mDaySeparatorColor; - protected int mTodayOutlineColor; - protected int mWeekNumColor; - - public SimpleWeekView(Context context) { - super(context); - - Resources res = context.getResources(); - - mBGColor = res.getColor(R.color.month_bgcolor); - mSelectedWeekBGColor = res.getColor(R.color.month_selected_week_bgcolor); - mFocusMonthColor = res.getColor(R.color.month_mini_day_number); - mOtherMonthColor = res.getColor(R.color.month_other_month_day_number); - mDaySeparatorColor = res.getColor(R.color.month_grid_lines); - mTodayOutlineColor = res.getColor(R.color.mini_month_today_outline_color); - mWeekNumColor = res.getColor(R.color.month_week_num_color); - mSelectedDayLine = res.getDrawable(R.drawable.dayline_minical_holo_light); - - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - DEFAULT_HEIGHT *= mScale; - MIN_HEIGHT *= mScale; - MINI_DAY_NUMBER_TEXT_SIZE *= mScale; - MINI_TODAY_NUMBER_TEXT_SIZE *= mScale; - MINI_TODAY_OUTLINE_WIDTH *= mScale; - WEEK_NUM_MARGIN_BOTTOM *= mScale; - DAY_SEPARATOR_WIDTH *= mScale; - MINI_WK_NUMBER_TEXT_SIZE *= mScale; - } - } - - // Sets up any standard paints that will be used - initView(); - } - - /** - * Sets all the parameters for displaying this week. The only required - * parameter is the week number. Other parameters have a default value and - * will only update if a new value is included, except for focus month, - * which will always default to no focus month if no value is passed in. See - * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters. - * - * @param params A map of the new parameters, see - * {@link #VIEW_PARAMS_HEIGHT} - * @param tz The time zone this view should reference times in - */ - public void setWeekParams(HashMap<String, Integer> params, String tz) { - if (!params.containsKey(VIEW_PARAMS_WEEK)) { - throw new InvalidParameterException("You must specify the week number for this view"); - } - setTag(params); - mTimeZone = tz; - // We keep the current value for any params not present - if (params.containsKey(VIEW_PARAMS_HEIGHT)) { - mHeight = params.get(VIEW_PARAMS_HEIGHT); - if (mHeight < MIN_HEIGHT) { - mHeight = MIN_HEIGHT; - } - } - if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) { - mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY); - } - mHasSelectedDay = mSelectedDay != -1; - if (params.containsKey(VIEW_PARAMS_NUM_DAYS)) { - mNumDays = params.get(VIEW_PARAMS_NUM_DAYS); - } - if (params.containsKey(VIEW_PARAMS_SHOW_WK_NUM)) { - if (params.get(VIEW_PARAMS_SHOW_WK_NUM) != 0) { - mShowWeekNum = true; - } else { - mShowWeekNum = false; - } - } - mNumCells = mShowWeekNum ? mNumDays + 1 : mNumDays; - - // Allocate space for caching the day numbers and focus values - mDayNumbers = new String[mNumCells]; - mFocusDay = new boolean[mNumCells]; - mOddMonth = new boolean[mNumCells]; - mWeek = params.get(VIEW_PARAMS_WEEK); - int julianMonday = Utils.getJulianMondayFromWeeksSinceEpoch(mWeek); - Time time = new Time(tz); - time.setJulianDay(julianMonday); - - // If we're showing the week number calculate it based on Monday - int i = 0; - if (mShowWeekNum) { - mDayNumbers[0] = Integer.toString(time.getWeekNumber()); - i++; - } - - if (params.containsKey(VIEW_PARAMS_WEEK_START)) { - mWeekStart = params.get(VIEW_PARAMS_WEEK_START); - } - - // Now adjust our starting day based on the start day of the week - // If the week is set to start on a Saturday the first week will be - // Dec 27th 1969 -Jan 2nd, 1970 - if (time.weekDay != mWeekStart) { - int diff = time.weekDay - mWeekStart; - if (diff < 0) { - diff += 7; - } - time.monthDay -= diff; - time.normalize(true); - } - - mFirstJulianDay = Time.getJulianDay(time.toMillis(true), time.gmtoff); - mFirstMonth = time.month; - - // Figure out what day today is - Time today = new Time(tz); - today.setToNow(); - mHasToday = false; - mToday = -1; - - int focusMonth = params.containsKey(VIEW_PARAMS_FOCUS_MONTH) ? params.get( - VIEW_PARAMS_FOCUS_MONTH) - : DEFAULT_FOCUS_MONTH; - - for (; i < mNumCells; i++) { - if (time.monthDay == 1) { - mFirstMonth = time.month; - } - mOddMonth [i] = (time.month %2) == 1; - if (time.month == focusMonth) { - mFocusDay[i] = true; - } else { - mFocusDay[i] = false; - } - if (time.year == today.year && time.yearDay == today.yearDay) { - mHasToday = true; - mToday = i; - } - mDayNumbers[i] = Integer.toString(time.monthDay++); - time.normalize(true); - } - // We do one extra add at the end of the loop, if that pushed us to a - // new month undo it - if (time.monthDay == 1) { - time.monthDay--; - time.normalize(true); - } - mLastMonth = time.month; - - updateSelectionPositions(); - } - - /** - * Sets up the text and style properties for painting. Override this if you - * want to use a different paint. - */ - protected void initView() { - p.setFakeBoldText(false); - p.setAntiAlias(true); - p.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - p.setStyle(Style.FILL); - - mMonthNumPaint = new Paint(); - mMonthNumPaint.setFakeBoldText(true); - mMonthNumPaint.setAntiAlias(true); - mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setColor(mFocusMonthColor); - mMonthNumPaint.setStyle(Style.FILL); - mMonthNumPaint.setTextAlign(Align.CENTER); - } - - /** - * Returns the month of the first day in this week - * - * @return The month the first day of this view is in - */ - public int getFirstMonth() { - return mFirstMonth; - } - - /** - * Returns the month of the last day in this week - * - * @return The month the last day of this view is in - */ - public int getLastMonth() { - return mLastMonth; - } - - /** - * Returns the julian day of the first day in this view. - * - * @return The julian day of the first day in the view. - */ - public int getFirstJulianDay() { - return mFirstJulianDay; - } - - /** - * Calculates the day that the given x position is in, accounting for week - * number. Returns a Time referencing that day or null if - * - * @param x The x position of the touch event - * @return A time object for the tapped day or null if the position wasn't - * in a day - */ - public Time getDayFromLocation(float x) { - int dayStart = mShowWeekNum ? (mWidth - mPadding * 2) / mNumCells + mPadding : mPadding; - if (x < dayStart || x > mWidth - mPadding) { - return null; - } - // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels - int dayPosition = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); - int day = mFirstJulianDay + dayPosition; - - Time time = new Time(mTimeZone); - if (mWeek == 0) { - // This week is weird... - if (day < Time.EPOCH_JULIAN_DAY) { - day++; - } else if (day == Time.EPOCH_JULIAN_DAY) { - time.set(1, 0, 1970); - time.normalize(true); - return time; - } - } - - time.setJulianDay(day); - return time; - } - - @Override - protected void onDraw(Canvas canvas) { - drawBackground(canvas); - drawWeekNums(canvas); - drawDaySeparators(canvas); - } - - /** - * This draws the selection highlight if a day is selected in this week. - * Override this method if you wish to have a different background drawn. - * - * @param canvas The canvas to draw on - */ - protected void drawBackground(Canvas canvas) { - if (mHasSelectedDay) { - p.setColor(mSelectedWeekBGColor); - p.setStyle(Style.FILL); - } else { - return; - } - r.top = 1; - r.bottom = mHeight - 1; - r.left = mPadding; - r.right = mSelectedLeft; - canvas.drawRect(r, p); - r.left = mSelectedRight; - r.right = mWidth - mPadding; - canvas.drawRect(r, p); - } - - /** - * Draws the week and month day numbers for this week. Override this method - * if you need different placement. - * - * @param canvas The canvas to draw on - */ - protected void drawWeekNums(Canvas canvas) { - int y = ((mHeight + MINI_DAY_NUMBER_TEXT_SIZE) / 2) - DAY_SEPARATOR_WIDTH; - int nDays = mNumCells; - - int i = 0; - int divisor = 2 * nDays; - if (mShowWeekNum) { - p.setTextSize(MINI_WK_NUMBER_TEXT_SIZE); - p.setStyle(Style.FILL); - p.setTextAlign(Align.CENTER); - p.setAntiAlias(true); - p.setColor(mWeekNumColor); - int x = (mWidth - mPadding * 2) / divisor + mPadding; - canvas.drawText(mDayNumbers[0], x, y, p); - i++; - } - - boolean isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); - mMonthNumPaint.setFakeBoldText(false); - for (; i < nDays; i++) { - if (mFocusDay[i] != isFocusMonth) { - isFocusMonth = mFocusDay[i]; - mMonthNumPaint.setColor(isFocusMonth ? mFocusMonthColor : mOtherMonthColor); - } - if (mHasToday && mToday == i) { - mMonthNumPaint.setTextSize(MINI_TODAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setFakeBoldText(true); - } - int x = (2 * i + 1) * (mWidth - mPadding * 2) / (divisor) + mPadding; - canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); - if (mHasToday && mToday == i) { - mMonthNumPaint.setTextSize(MINI_DAY_NUMBER_TEXT_SIZE); - mMonthNumPaint.setFakeBoldText(false); - } - } - } - - /** - * Draws a horizontal line for separating the weeks. Override this method if - * you want custom separators. - * - * @param canvas The canvas to draw on - */ - protected void drawDaySeparators(Canvas canvas) { - if (mHasSelectedDay) { - r.top = 1; - r.bottom = mHeight - 1; - r.left = mSelectedLeft + 1; - r.right = mSelectedRight - 1; - p.setStrokeWidth(MINI_TODAY_OUTLINE_WIDTH); - p.setStyle(Style.STROKE); - p.setColor(mTodayOutlineColor); - canvas.drawRect(r, p); - } - if (mShowWeekNum) { - p.setColor(mDaySeparatorColor); - p.setStrokeWidth(DAY_SEPARATOR_WIDTH); - - int x = (mWidth - mPadding * 2) / mNumCells + mPadding; - canvas.drawLine(x, 0, x, mHeight, p); - } - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - mWidth = w; - updateSelectionPositions(); - } - - /** - * This calculates the positions for the selected day lines. - */ - protected void updateSelectionPositions() { - if (mHasSelectedDay) { - int selectedPosition = mSelectedDay - mWeekStart; - if (selectedPosition < 0) { - selectedPosition += 7; - } - if (mShowWeekNum) { - selectedPosition++; - } - mSelectedLeft = selectedPosition * (mWidth - mPadding * 2) / mNumCells - + mPadding; - mSelectedRight = (selectedPosition + 1) * (mWidth - mPadding * 2) / mNumCells - + mPadding; - } - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mHeight); - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - Context context = getContext(); - // only send accessibility events if accessibility and exploration are - // on. - AccessibilityManager am = (AccessibilityManager) context - .getSystemService(Service.ACCESSIBILITY_SERVICE); - if (!am.isEnabled() || !am.isTouchExplorationEnabled()) { - return super.onHoverEvent(event); - } - if (event.getAction() != MotionEvent.ACTION_HOVER_EXIT) { - Time hover = getDayFromLocation(event.getX()); - if (hover != null - && (mLastHoverTime == null || Time.compare(hover, mLastHoverTime) != 0)) { - Long millis = hover.toMillis(true); - String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_SHOW_DATE); - AccessibilityEvent accessEvent = - AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - accessEvent.getText().add(date); - sendAccessibilityEventUnchecked(accessEvent); - mLastHoverTime = hover; - } - } - return true; - } - - Time mLastHoverTime = null; -}
\ No newline at end of file diff --git a/src/com/android/calendar/month/SimpleWeeksAdapter.java b/src/com/android/calendar/month/SimpleWeeksAdapter.java deleted file mode 100644 index d29b2622..00000000 --- a/src/com/android/calendar/month/SimpleWeeksAdapter.java +++ /dev/null @@ -1,302 +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.month; - -// TODO Remove calendar imports when the required methods have been -// refactored into the public api -import com.android.calendar.CalendarController; -import com.android.calendar.Utils; - -import android.content.Context; -import android.text.format.Time; -import android.util.Log; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewGroup; -import android.widget.AbsListView.LayoutParams; -import android.widget.BaseAdapter; -import android.widget.ListView; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.Locale; - -/** - * <p> - * This is a specialized adapter for creating a list of weeks with selectable - * days. It can be configured to display the week number, start the week on a - * given day, show a reduced number of days, or display an arbitrary number of - * weeks at a time. See {@link SimpleDayPickerFragment} for usage. - * </p> - */ -public class SimpleWeeksAdapter extends BaseAdapter implements OnTouchListener { - - private static final String TAG = "MonthByWeek"; - - /** - * The number of weeks to display at a time. - */ - public static final String WEEK_PARAMS_NUM_WEEKS = "num_weeks"; - /** - * Which month should be in focus currently. - */ - public static final String WEEK_PARAMS_FOCUS_MONTH = "focus_month"; - /** - * Whether the week number should be shown. Non-zero to show them. - */ - public static final String WEEK_PARAMS_SHOW_WEEK = "week_numbers"; - /** - * Which day the week should start on. {@link Time#SUNDAY} through - * {@link Time#SATURDAY}. - */ - public static final String WEEK_PARAMS_WEEK_START = "week_start"; - /** - * The Julian day to highlight as selected. - */ - public static final String WEEK_PARAMS_JULIAN_DAY = "selected_day"; - /** - * How many days of the week to display [1-7]. - */ - public static final String WEEK_PARAMS_DAYS_PER_WEEK = "days_per_week"; - - protected static final int WEEK_COUNT = CalendarController.MAX_CALENDAR_WEEK - - CalendarController.MIN_CALENDAR_WEEK; - protected static int DEFAULT_NUM_WEEKS = 6; - protected static int DEFAULT_MONTH_FOCUS = 0; - protected static int DEFAULT_DAYS_PER_WEEK = 7; - protected static int DEFAULT_WEEK_HEIGHT = 32; - protected static int WEEK_7_OVERHANG_HEIGHT = 7; - - protected static float mScale = 0; - protected Context mContext; - // The day to highlight as selected - protected Time mSelectedDay; - // The week since 1970 that the selected day is in - protected int mSelectedWeek; - // When the week starts; numbered like Time.<WEEKDAY> (e.g. SUNDAY=0). - protected int mFirstDayOfWeek; - protected boolean mShowWeekNumber = false; - protected GestureDetector mGestureDetector; - protected int mNumWeeks = DEFAULT_NUM_WEEKS; - protected int mDaysPerWeek = DEFAULT_DAYS_PER_WEEK; - protected int mFocusMonth = DEFAULT_MONTH_FOCUS; - - public SimpleWeeksAdapter(Context context, HashMap<String, Integer> params) { - mContext = context; - - // Get default week start based on locale, subtracting one for use with android Time. - Calendar cal = Calendar.getInstance(Locale.getDefault()); - mFirstDayOfWeek = cal.getFirstDayOfWeek() - 1; - - if (mScale == 0) { - mScale = context.getResources().getDisplayMetrics().density; - if (mScale != 1) { - WEEK_7_OVERHANG_HEIGHT *= mScale; - } - } - init(); - updateParams(params); - } - - /** - * Set up the gesture detector and selected time - */ - protected void init() { - mGestureDetector = new GestureDetector(mContext, new CalendarGestureListener()); - mSelectedDay = new Time(); - mSelectedDay.setToNow(); - } - - /** - * Parse the parameters and set any necessary fields. See - * {@link #WEEK_PARAMS_NUM_WEEKS} for parameter details. - * - * @param params A list of parameters for this adapter - */ - public void updateParams(HashMap<String, Integer> params) { - if (params == null) { - Log.e(TAG, "WeekParameters are null! Cannot update adapter."); - return; - } - if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mFocusMonth = params.get(WEEK_PARAMS_FOCUS_MONTH); - } - if (params.containsKey(WEEK_PARAMS_FOCUS_MONTH)) { - mNumWeeks = params.get(WEEK_PARAMS_NUM_WEEKS); - } - if (params.containsKey(WEEK_PARAMS_SHOW_WEEK)) { - mShowWeekNumber = params.get(WEEK_PARAMS_SHOW_WEEK) != 0; - } - if (params.containsKey(WEEK_PARAMS_WEEK_START)) { - mFirstDayOfWeek = params.get(WEEK_PARAMS_WEEK_START); - } - if (params.containsKey(WEEK_PARAMS_JULIAN_DAY)) { - int julianDay = params.get(WEEK_PARAMS_JULIAN_DAY); - mSelectedDay.setJulianDay(julianDay); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay(julianDay, mFirstDayOfWeek); - } - if (params.containsKey(WEEK_PARAMS_DAYS_PER_WEEK)) { - mDaysPerWeek = params.get(WEEK_PARAMS_DAYS_PER_WEEK); - } - refresh(); - } - - /** - * Updates the selected day and related parameters. - * - * @param selectedTime The time to highlight - */ - public void setSelectedDay(Time selectedTime) { - mSelectedDay.set(selectedTime); - long millis = mSelectedDay.normalize(true); - mSelectedWeek = Utils.getWeeksSinceEpochFromJulianDay( - Time.getJulianDay(millis, mSelectedDay.gmtoff), mFirstDayOfWeek); - notifyDataSetChanged(); - } - - /** - * Returns the currently highlighted day - * - * @return - */ - public Time getSelectedDay() { - return mSelectedDay; - } - - /** - * updates any config options that may have changed and refreshes the view - */ - protected void refresh() { - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return WEEK_COUNT; - } - - @Override - public Object getItem(int position) { - return null; - } - - @Override - public long getItemId(int position) { - return position; - } - - @SuppressWarnings("unchecked") - @Override - public View getView(int position, View convertView, ViewGroup parent) { - SimpleWeekView v; - HashMap<String, Integer> drawingParams = null; - if (convertView != null) { - v = (SimpleWeekView) convertView; - // We store the drawing parameters in the view so it can be recycled - drawingParams = (HashMap<String, Integer>) v.getTag(); - } else { - v = new SimpleWeekView(mContext); - // Set up the new view - LayoutParams params = new LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - v.setLayoutParams(params); - v.setClickable(true); - v.setOnTouchListener(this); - } - if (drawingParams == null) { - drawingParams = new HashMap<String, Integer>(); - } - drawingParams.clear(); - - int selectedDay = -1; - if (mSelectedWeek == position) { - selectedDay = mSelectedDay.weekDay; - } - - // pass in all the view parameters - drawingParams.put(SimpleWeekView.VIEW_PARAMS_HEIGHT, - (parent.getHeight() - WEEK_7_OVERHANG_HEIGHT) / mNumWeeks); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SELECTED_DAY, selectedDay); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_SHOW_WK_NUM, mShowWeekNumber ? 1 : 0); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK_START, mFirstDayOfWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_NUM_DAYS, mDaysPerWeek); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_WEEK, position); - drawingParams.put(SimpleWeekView.VIEW_PARAMS_FOCUS_MONTH, mFocusMonth); - v.setWeekParams(drawingParams, mSelectedDay.timezone); - v.invalidate(); - - return v; - } - - /** - * Changes which month is in focus and updates the view. - * - * @param month The month to show as in focus [0-11] - */ - public void updateFocusMonth(int month) { - mFocusMonth = month; - notifyDataSetChanged(); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (mGestureDetector.onTouchEvent(event)) { - SimpleWeekView view = (SimpleWeekView) v; - Time day = ((SimpleWeekView)v).getDayFromLocation(event.getX()); - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Touched day at Row=" + view.mWeek + " day=" + day.toString()); - } - if (day != null) { - onDayTapped(day); - } - return true; - } - return false; - } - - /** - * Maintains the same hour/min/sec but moves the day to the tapped day. - * - * @param day The day that was tapped - */ - protected void onDayTapped(Time day) { - day.hour = mSelectedDay.hour; - day.minute = mSelectedDay.minute; - day.second = mSelectedDay.second; - setSelectedDay(day); - } - - - /** - * This is here so we can identify single tap events and set the selected - * day correctly - */ - protected class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener { - @Override - public boolean onSingleTapUp(MotionEvent e) { - return true; - } - } - - ListView mListView; - - public void setListView(ListView lv) { - mListView = lv; - } -} diff --git a/src/com/android/calendar/widget/CalendarAppWidgetModel.java b/src/com/android/calendar/widget/CalendarAppWidgetModel.java deleted file mode 100644 index a989e18b..00000000 --- a/src/com/android/calendar/widget/CalendarAppWidgetModel.java +++ /dev/null @@ -1,430 +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.widget; - -import com.android.calendar.R; -import com.android.calendar.Utils; - -import android.content.Context; -import android.database.Cursor; -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.View; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.TimeZone; - -class CalendarAppWidgetModel { - private static final String TAG = CalendarAppWidgetModel.class.getSimpleName(); - private static final boolean LOGD = false; - - private String mHomeTZName; - private boolean mShowTZ; - /** - * {@link RowInfo} is a class that represents a single row in the widget. It - * is actually only a pointer to either a {@link DayInfo} or an - * {@link EventInfo} instance, since a row in the widget might be either a - * day header or an event. - */ - static class RowInfo { - static final int TYPE_DAY = 0; - static final int TYPE_MEETING = 1; - - /** - * mType is either a day header (TYPE_DAY) or an event (TYPE_MEETING) - */ - final int mType; - - /** - * If mType is TYPE_DAY, then mData is the index into day infos. - * Otherwise mType is TYPE_MEETING and mData is the index into event - * infos. - */ - final int mIndex; - - RowInfo(int type, int index) { - mType = type; - mIndex = index; - } - } - - /** - * {@link EventInfo} is a class that represents an event in the widget. It - * contains all of the data necessary to display that event, including the - * properly localized strings and visibility settings. - */ - static class EventInfo { - int visibWhen; // Visibility value for When textview (View.GONE or View.VISIBLE) - String when; - int visibWhere; // Visibility value for Where textview (View.GONE or View.VISIBLE) - String where; - int visibTitle; // Visibility value for Title textview (View.GONE or View.VISIBLE) - String title; - int selfAttendeeStatus; - - long id; - long start; - long end; - boolean allDay; - int color; - - public EventInfo() { - visibWhen = View.GONE; - visibWhere = View.GONE; - visibTitle = View.GONE; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("EventInfo [visibTitle="); - builder.append(visibTitle); - builder.append(", title="); - builder.append(title); - builder.append(", visibWhen="); - builder.append(visibWhen); - builder.append(", id="); - builder.append(id); - builder.append(", when="); - builder.append(when); - builder.append(", visibWhere="); - builder.append(visibWhere); - builder.append(", where="); - builder.append(where); - builder.append(", color="); - builder.append(String.format("0x%x", color)); - builder.append(", selfAttendeeStatus="); - builder.append(selfAttendeeStatus); - builder.append("]"); - return builder.toString(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (allDay ? 1231 : 1237); - result = prime * result + (int) (id ^ (id >>> 32)); - result = prime * result + (int) (end ^ (end >>> 32)); - result = prime * result + (int) (start ^ (start >>> 32)); - result = prime * result + ((title == null) ? 0 : title.hashCode()); - result = prime * result + visibTitle; - result = prime * result + visibWhen; - result = prime * result + visibWhere; - result = prime * result + ((when == null) ? 0 : when.hashCode()); - result = prime * result + ((where == null) ? 0 : where.hashCode()); - result = prime * result + color; - result = prime * result + selfAttendeeStatus; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - EventInfo other = (EventInfo) obj; - if (id != other.id) - return false; - if (allDay != other.allDay) - return false; - if (end != other.end) - return false; - if (start != other.start) - return false; - if (title == null) { - if (other.title != null) - return false; - } else if (!title.equals(other.title)) - return false; - if (visibTitle != other.visibTitle) - return false; - if (visibWhen != other.visibWhen) - return false; - if (visibWhere != other.visibWhere) - return false; - if (when == null) { - if (other.when != null) - return false; - } else if (!when.equals(other.when)) { - return false; - } - if (where == null) { - if (other.where != null) - return false; - } else if (!where.equals(other.where)) { - return false; - } - if (color != other.color) { - return false; - } - if (selfAttendeeStatus != other.selfAttendeeStatus) { - return false; - } - return true; - } - } - - /** - * {@link DayInfo} is a class that represents a day header in the widget. It - * contains all of the data necessary to display that day header, including - * the properly localized string. - */ - static class DayInfo { - - /** The Julian day */ - final int mJulianDay; - - /** The string representation of this day header, to be displayed */ - final String mDayLabel; - - DayInfo(int julianDay, String label) { - mJulianDay = julianDay; - mDayLabel = label; - } - - @Override - public String toString() { - return mDayLabel; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((mDayLabel == null) ? 0 : mDayLabel.hashCode()); - result = prime * result + mJulianDay; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - DayInfo other = (DayInfo) obj; - if (mDayLabel == null) { - if (other.mDayLabel != null) - return false; - } else if (!mDayLabel.equals(other.mDayLabel)) - return false; - if (mJulianDay != other.mJulianDay) - return false; - return true; - } - - } - - final List<RowInfo> mRowInfos; - final List<EventInfo> mEventInfos; - final List<DayInfo> mDayInfos; - final Context mContext; - final long mNow; - final int mTodayJulianDay; - final int mMaxJulianDay; - - public CalendarAppWidgetModel(Context context, String timeZone) { - mNow = System.currentTimeMillis(); - Time time = new Time(timeZone); - time.setToNow(); // This is needed for gmtoff to be set - mTodayJulianDay = Time.getJulianDay(mNow, time.gmtoff); - mMaxJulianDay = mTodayJulianDay + CalendarAppWidgetService.MAX_DAYS - 1; - mEventInfos = new ArrayList<EventInfo>(50); - mRowInfos = new ArrayList<RowInfo>(50); - mDayInfos = new ArrayList<DayInfo>(8); - mContext = context; - } - - public void buildFromCursor(Cursor cursor, String timeZone) { - final Time recycle = new Time(timeZone); - final ArrayList<LinkedList<RowInfo>> mBuckets = - new ArrayList<LinkedList<RowInfo>>(CalendarAppWidgetService.MAX_DAYS); - for (int i = 0; i < CalendarAppWidgetService.MAX_DAYS; i++) { - mBuckets.add(new LinkedList<RowInfo>()); - } - recycle.setToNow(); - mShowTZ = !TextUtils.equals(timeZone, Time.getCurrentTimezone()); - if (mShowTZ) { - mHomeTZName = TimeZone.getTimeZone(timeZone).getDisplayName(recycle.isDst != 0, - TimeZone.SHORT); - } - - cursor.moveToPosition(-1); - String tz = Utils.getTimeZone(mContext, null); - while (cursor.moveToNext()) { - final int rowId = cursor.getPosition(); - final long eventId = cursor.getLong(CalendarAppWidgetService.INDEX_EVENT_ID); - final boolean allDay = cursor.getInt(CalendarAppWidgetService.INDEX_ALL_DAY) != 0; - long start = cursor.getLong(CalendarAppWidgetService.INDEX_BEGIN); - long end = cursor.getLong(CalendarAppWidgetService.INDEX_END); - final String title = cursor.getString(CalendarAppWidgetService.INDEX_TITLE); - final String location = - cursor.getString(CalendarAppWidgetService.INDEX_EVENT_LOCATION); - // we don't compute these ourselves because it seems to produce the - // wrong endDay for all day events - final int startDay = cursor.getInt(CalendarAppWidgetService.INDEX_START_DAY); - final int endDay = cursor.getInt(CalendarAppWidgetService.INDEX_END_DAY); - final int color = cursor.getInt(CalendarAppWidgetService.INDEX_COLOR); - final int selfStatus = cursor - .getInt(CalendarAppWidgetService.INDEX_SELF_ATTENDEE_STATUS); - - // Adjust all-day times into local timezone - if (allDay) { - start = Utils.convertAlldayUtcToLocal(recycle, start, tz); - end = Utils.convertAlldayUtcToLocal(recycle, end, tz); - } - - if (LOGD) { - Log.d(TAG, "Row #" + rowId + " allDay:" + allDay + " start:" + start - + " end:" + end + " eventId:" + eventId); - } - - // we might get some extra events when querying, in order to - // deal with all-day events - if (end < mNow) { - continue; - } - - int i = mEventInfos.size(); - mEventInfos.add(populateEventInfo(eventId, allDay, start, end, startDay, endDay, title, - location, color, selfStatus)); - // populate the day buckets that this event falls into - int from = Math.max(startDay, mTodayJulianDay); - int to = Math.min(endDay, mMaxJulianDay); - for (int day = from; day <= to; day++) { - LinkedList<RowInfo> bucket = mBuckets.get(day - mTodayJulianDay); - RowInfo rowInfo = new RowInfo(RowInfo.TYPE_MEETING, i); - if (allDay) { - bucket.addFirst(rowInfo); - } else { - bucket.add(rowInfo); - } - } - } - - int day = mTodayJulianDay; - int count = 0; - for (LinkedList<RowInfo> bucket : mBuckets) { - if (!bucket.isEmpty()) { - // We don't show day header in today - if (day != mTodayJulianDay) { - final DayInfo dayInfo = populateDayInfo(day, recycle); - // Add the day header - final int dayIndex = mDayInfos.size(); - mDayInfos.add(dayInfo); - mRowInfos.add(new RowInfo(RowInfo.TYPE_DAY, dayIndex)); - } - - // Add the event row infos - mRowInfos.addAll(bucket); - count += bucket.size(); - } - day++; - if (count >= CalendarAppWidgetService.EVENT_MIN_COUNT) { - break; - } - } - } - - private EventInfo populateEventInfo(long eventId, boolean allDay, long start, long end, - int startDay, int endDay, String title, String location, int color, int selfStatus) { - EventInfo eventInfo = new EventInfo(); - - // Compute a human-readable string for the start time of the event - StringBuilder whenString = new StringBuilder(); - int visibWhen; - int flags = DateUtils.FORMAT_ABBREV_ALL; - visibWhen = View.VISIBLE; - if (allDay) { - flags |= DateUtils.FORMAT_SHOW_DATE; - whenString.append(Utils.formatDateRange(mContext, start, end, flags)); - } else { - flags |= DateUtils.FORMAT_SHOW_TIME; - if (DateFormat.is24HourFormat(mContext)) { - flags |= DateUtils.FORMAT_24HOUR; - } - if (endDay > startDay) { - flags |= DateUtils.FORMAT_SHOW_DATE; - } - whenString.append(Utils.formatDateRange(mContext, start, end, flags)); - - if (mShowTZ) { - whenString.append(" ").append(mHomeTZName); - } - } - eventInfo.id = eventId; - eventInfo.start = start; - eventInfo.end = end; - eventInfo.allDay = allDay; - eventInfo.when = whenString.toString(); - eventInfo.visibWhen = visibWhen; - eventInfo.color = color; - eventInfo.selfAttendeeStatus = selfStatus; - - // What - if (TextUtils.isEmpty(title)) { - eventInfo.title = mContext.getString(R.string.no_title_label); - } else { - eventInfo.title = title; - } - eventInfo.visibTitle = View.VISIBLE; - - // Where - if (!TextUtils.isEmpty(location)) { - eventInfo.visibWhere = View.VISIBLE; - eventInfo.where = location; - } else { - eventInfo.visibWhere = View.GONE; - } - return eventInfo; - } - - private DayInfo populateDayInfo(int julianDay, Time recycle) { - long millis = recycle.setJulianDay(julianDay); - int flags = DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE; - - String label; - if (julianDay == mTodayJulianDay + 1) { - label = mContext.getString(R.string.agenda_tomorrow, - Utils.formatDateRange(mContext, millis, millis, flags).toString()); - } else { - flags |= DateUtils.FORMAT_SHOW_WEEKDAY; - label = Utils.formatDateRange(mContext, millis, millis, flags); - } - return new DayInfo(julianDay, label); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("\nCalendarAppWidgetModel [eventInfos="); - builder.append(mEventInfos); - builder.append("]"); - return builder.toString(); - } -}
\ No newline at end of file diff --git a/src/com/android/calendar/widget/CalendarAppWidgetProvider.java b/src/com/android/calendar/widget/CalendarAppWidgetProvider.java deleted file mode 100644 index 3a69efd3..00000000 --- a/src/com/android/calendar/widget/CalendarAppWidgetProvider.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2009 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.widget; - -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 android.app.AlarmManager; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.provider.CalendarContract; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.widget.RemoteViews; - -import com.android.calendar.AllInOneActivity; -import com.android.calendar.EventInfoActivity; -import com.android.calendar.R; -import com.android.calendar.Utils; - -/** - * Simple widget to show next upcoming calendar event. - */ -public class CalendarAppWidgetProvider extends AppWidgetProvider { - static final String TAG = "CalendarAppWidgetProvider"; - static final boolean LOGD = false; - - // TODO Move these to Calendar.java - static final String EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS"; - - /** - * {@inheritDoc} - */ - @Override - public void onReceive(Context context, Intent intent) { - // Handle calendar-specific updates ourselves because they might be - // coming in without extras, which AppWidgetProvider then blocks. - final String action = intent.getAction(); - if (LOGD) - Log.d(TAG, "AppWidgetProvider got the intent: " + intent.toString()); - if (Utils.getWidgetUpdateAction(context).equals(action)) { - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - performUpdate(context, appWidgetManager, - appWidgetManager.getAppWidgetIds(getComponentName(context)), - null /* no eventIds */); - } else if (action != null && (action.equals(Intent.ACTION_PROVIDER_CHANGED) - || action.equals(Intent.ACTION_TIME_CHANGED) - || action.equals(Intent.ACTION_TIMEZONE_CHANGED) - || action.equals(Intent.ACTION_DATE_CHANGED) - || action.equals(Utils.getWidgetScheduledUpdateAction(context)))) { - Intent service = new Intent(context, CalendarAppWidgetService.class); - context.startService(service); - } else { - super.onReceive(context, intent); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onDisabled(Context context) { - // Unsubscribe from all AlarmManager updates - AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pendingUpdate = getUpdateIntent(context); - am.cancel(pendingUpdate); - } - - /** - * {@inheritDoc} - */ - @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */); - } - - - /** - * Build {@link ComponentName} describing this specific - * {@link AppWidgetProvider} - */ - static ComponentName getComponentName(Context context) { - return new ComponentName(context, CalendarAppWidgetProvider.class); - } - - /** - * Process and push out an update for the given appWidgetIds. This call - * actually fires an intent to start {@link CalendarAppWidgetService} as a - * background service which handles the actual update, to prevent ANR'ing - * during database queries. - * - * @param context Context to use when starting {@link CalendarAppWidgetService}. - * @param appWidgetIds List of specific appWidgetIds to update, or null for - * all. - * @param changedEventIds Specific events known to be changed. If present, - * we use it to decide if an update is necessary. - */ - private void performUpdate(Context context, - AppWidgetManager appWidgetManager, int[] appWidgetIds, - long[] changedEventIds) { - // Launch over to service so it can perform update - for (int appWidgetId : appWidgetIds) { - if (LOGD) Log.d(TAG, "Building widget update..."); - Intent updateIntent = new Intent(context, CalendarAppWidgetService.class); - updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); - if (changedEventIds != null) { - updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds); - } - updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))); - - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget); - // Calendar header - Time time = new Time(Utils.getTimeZone(context, null)); - time.setToNow(); - long millis = time.toMillis(true); - final String dayOfWeek = DateUtils.getDayOfWeekString(time.weekDay + 1, - DateUtils.LENGTH_MEDIUM); - final String date = Utils.formatDateRange(context, millis, millis, - DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_NO_YEAR); - views.setTextViewText(R.id.day_of_week, dayOfWeek); - views.setTextViewText(R.id.date, date); - // Attach to list of events - views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent); - appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list); - - - // Launch calendar app when the user taps on the header - final Intent launchCalendarIntent = new Intent(Intent.ACTION_VIEW); - launchCalendarIntent.setClass(context, AllInOneActivity.class); - launchCalendarIntent - .setData(Uri.parse("content://com.android.calendar/time/" + millis)); - final PendingIntent launchCalendarPendingIntent = PendingIntent.getActivity( - context, 0 /* no requestCode */, launchCalendarIntent, 0 /* no flags */); - views.setOnClickPendingIntent(R.id.header, launchCalendarPendingIntent); - - // Each list item will call setOnClickExtra() to let the list know - // which item - // is selected by a user. - final PendingIntent updateEventIntent = getLaunchPendingIntentTemplate(context); - views.setPendingIntentTemplate(R.id.events_list, updateEventIntent); - - appWidgetManager.updateAppWidget(appWidgetId, views); - } - } - - /** - * Build the {@link PendingIntent} used to trigger an update of all calendar - * widgets. Uses {@link Utils#getWidgetScheduledUpdateAction(Context)} to - * directly target all widgets instead of using - * {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}. - * - * @param context Context to use when building broadcast. - */ - static PendingIntent getUpdateIntent(Context context) { - Intent intent = new Intent(Utils.getWidgetScheduledUpdateAction(context)); - intent.setDataAndType(CalendarContract.CONTENT_URI, Utils.APPWIDGET_DATA_TYPE); - return PendingIntent.getBroadcast(context, 0 /* no requestCode */, intent, - 0 /* no flags */); - } - - /** - * Build a {@link PendingIntent} to launch the Calendar app. This should be used - * in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}. - */ - static PendingIntent getLaunchPendingIntentTemplate(Context context) { - Intent launchIntent = new Intent(); - launchIntent.setAction(Intent.ACTION_VIEW); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); - launchIntent.setClass(context, AllInOneActivity.class); - return PendingIntent.getActivity(context, 0 /* no requestCode */, launchIntent, - PendingIntent.FLAG_UPDATE_CURRENT); - } - - /** - * Build an {@link Intent} available as FillInIntent to launch the Calendar app. - * This should be used in combination with - * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. - * If the go to time is 0, then calendar will be launched without a starting time. - * - * @param goToTime time that calendar should take the user to, or 0 to - * indicate no specific start time. - */ - static Intent getLaunchFillInIntent(Context context, long id, long start, long end, - boolean allDay) { - final Intent fillInIntent = new Intent(); - String dataString = "content://com.android.calendar/events"; - if (id != 0) { - fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true); - fillInIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | - Intent.FLAG_ACTIVITY_TASK_ON_HOME); - - dataString += "/" + id; - // If we have an event id - start the event info activity - fillInIntent.setClass(context, EventInfoActivity.class); - } else { - // If we do not have an event id - start AllInOne - fillInIntent.setClass(context, AllInOneActivity.class); - } - Uri data = Uri.parse(dataString); - fillInIntent.setData(data); - fillInIntent.putExtra(EXTRA_EVENT_BEGIN_TIME, start); - fillInIntent.putExtra(EXTRA_EVENT_END_TIME, end); - fillInIntent.putExtra(EXTRA_EVENT_ALL_DAY, allDay); - - return fillInIntent; - } -} diff --git a/src/com/android/calendar/widget/CalendarAppWidgetService.java b/src/com/android/calendar/widget/CalendarAppWidgetService.java deleted file mode 100644 index ec702c7c..00000000 --- a/src/com/android/calendar/widget/CalendarAppWidgetService.java +++ /dev/null @@ -1,626 +0,0 @@ -/* - * Copyright (C) 2009 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.widget; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.CursorLoader; -import android.content.Intent; -import android.content.Loader; -import android.content.res.Resources; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; -import android.os.Handler; -import android.provider.CalendarContract.Attendees; -import android.provider.CalendarContract.Calendars; -import android.provider.CalendarContract.Instances; -import android.text.format.DateUtils; -import android.text.format.Time; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; -import android.widget.RemoteViewsService; - -import com.android.calendar.R; -import com.android.calendar.Utils; -import com.android.calendar.widget.CalendarAppWidgetModel.DayInfo; -import com.android.calendar.widget.CalendarAppWidgetModel.EventInfo; -import com.android.calendar.widget.CalendarAppWidgetModel.RowInfo; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; - - -public class CalendarAppWidgetService extends RemoteViewsService { - private static final String TAG = "CalendarWidget"; - - static final int EVENT_MIN_COUNT = 20; - static final int EVENT_MAX_COUNT = 100; - // Minimum delay between queries on the database for widget updates in ms - static final int WIDGET_UPDATE_THROTTLE = 500; - - private static final String EVENT_SORT_ORDER = Instances.START_DAY + " ASC, " - + Instances.START_MINUTE + " ASC, " + Instances.END_DAY + " ASC, " - + Instances.END_MINUTE + " ASC LIMIT " + EVENT_MAX_COUNT; - - private static final String EVENT_SELECTION = Calendars.VISIBLE + "=1"; - private static final String EVENT_SELECTION_HIDE_DECLINED = Calendars.VISIBLE + "=1 AND " - + Instances.SELF_ATTENDEE_STATUS + "!=" + Attendees.ATTENDEE_STATUS_DECLINED; - - static final String[] EVENT_PROJECTION = new String[] { - Instances.ALL_DAY, - Instances.BEGIN, - Instances.END, - Instances.TITLE, - Instances.EVENT_LOCATION, - Instances.EVENT_ID, - Instances.START_DAY, - Instances.END_DAY, - Instances.DISPLAY_COLOR, // If SDK < 16, set to Instances.CALENDAR_COLOR. - Instances.SELF_ATTENDEE_STATUS, - }; - - static final int INDEX_ALL_DAY = 0; - static final int INDEX_BEGIN = 1; - static final int INDEX_END = 2; - static final int INDEX_TITLE = 3; - static final int INDEX_EVENT_LOCATION = 4; - static final int INDEX_EVENT_ID = 5; - static final int INDEX_START_DAY = 6; - static final int INDEX_END_DAY = 7; - static final int INDEX_COLOR = 8; - static final int INDEX_SELF_ATTENDEE_STATUS = 9; - - static { - if (!Utils.isJellybeanOrLater()) { - EVENT_PROJECTION[INDEX_COLOR] = Instances.CALENDAR_COLOR; - } - } - static final int MAX_DAYS = 7; - - private static final long SEARCH_DURATION = MAX_DAYS * DateUtils.DAY_IN_MILLIS; - - /** - * Update interval used when no next-update calculated, or bad trigger time in past. - * Unit: milliseconds. - */ - private static final long UPDATE_TIME_NO_EVENTS = DateUtils.HOUR_IN_MILLIS * 6; - - @Override - public RemoteViewsFactory onGetViewFactory(Intent intent) { - return new CalendarFactory(getApplicationContext(), intent); - } - - public static class CalendarFactory extends BroadcastReceiver implements - RemoteViewsService.RemoteViewsFactory, Loader.OnLoadCompleteListener<Cursor> { - private static final boolean LOGD = false; - - // Suppress unnecessary logging about update time. Need to be static as this object is - // re-instanciated frequently. - // TODO: It seems loadData() is called via onCreate() four times, which should mean - // unnecessary CalendarFactory object is created and dropped. It is not efficient. - private static long sLastUpdateTime = UPDATE_TIME_NO_EVENTS; - - private Context mContext; - private Resources mResources; - private static CalendarAppWidgetModel mModel; - private static Object mLock = new Object(); - private static volatile int mSerialNum = 0; - private int mLastSerialNum = -1; - private CursorLoader mLoader; - private final Handler mHandler = new Handler(); - private static final AtomicInteger currentVersion = new AtomicInteger(0); - private final ExecutorService executor = Executors.newSingleThreadExecutor(); - private int mAppWidgetId; - private int mDeclinedColor; - private int mStandardColor; - private int mAllDayColor; - - private final Runnable mTimezoneChanged = new Runnable() { - @Override - public void run() { - if (mLoader != null) { - mLoader.forceLoad(); - } - } - }; - - private Runnable createUpdateLoaderRunnable(final String selection, - final PendingResult result, final int version) { - return new Runnable() { - @Override - public void run() { - // If there is a newer load request in the queue, skip loading. - if (mLoader != null && version >= currentVersion.get()) { - Uri uri = createLoaderUri(); - mLoader.setUri(uri); - mLoader.setSelection(selection); - synchronized (mLock) { - mLastSerialNum = ++mSerialNum; - } - mLoader.forceLoad(); - } - result.finish(); - } - }; - } - - protected CalendarFactory(Context context, Intent intent) { - mContext = context; - mResources = context.getResources(); - mAppWidgetId = intent.getIntExtra( - AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - - mDeclinedColor = mResources.getColor(R.color.appwidget_item_declined_color); - mStandardColor = mResources.getColor(R.color.appwidget_item_standard_color); - mAllDayColor = mResources.getColor(R.color.appwidget_item_allday_color); - } - - public CalendarFactory() { - // This is being created as part of onReceive - - } - - @Override - public void onCreate() { - String selection = queryForSelection(); - initLoader(selection); - } - - @Override - public void onDataSetChanged() { - } - - @Override - public void onDestroy() { - if (mLoader != null) { - mLoader.reset(); - } - } - - @Override - public RemoteViews getLoadingView() { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_loading); - return views; - } - - @Override - public RemoteViews getViewAt(int position) { - // we use getCount here so that it doesn't return null when empty - if (position < 0 || position >= getCount()) { - return null; - } - - if (mModel == null) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_loading); - final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, - 0, 0, false); - views.setOnClickFillInIntent(R.id.appwidget_loading, intent); - return views; - - } - if (mModel.mEventInfos.isEmpty() || mModel.mRowInfos.isEmpty()) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_no_events); - final Intent intent = CalendarAppWidgetProvider.getLaunchFillInIntent(mContext, 0, - 0, 0, false); - views.setOnClickFillInIntent(R.id.appwidget_no_events, intent); - return views; - } - - RowInfo rowInfo = mModel.mRowInfos.get(position); - if (rowInfo.mType == RowInfo.TYPE_DAY) { - RemoteViews views = new RemoteViews(mContext.getPackageName(), - R.layout.appwidget_day); - DayInfo dayInfo = mModel.mDayInfos.get(rowInfo.mIndex); - updateTextView(views, R.id.date, View.VISIBLE, dayInfo.mDayLabel); - return views; - } else { - RemoteViews views; - final EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); - if (eventInfo.allDay) { - views = new RemoteViews(mContext.getPackageName(), - R.layout.widget_all_day_item); - } else { - views = new RemoteViews(mContext.getPackageName(), R.layout.widget_item); - } - int displayColor = Utils.getDisplayColorFromColor(eventInfo.color); - - final long now = System.currentTimeMillis(); - if (!eventInfo.allDay && eventInfo.start <= now && now <= eventInfo.end) { - views.setInt(R.id.widget_row, "setBackgroundResource", - R.drawable.agenda_item_bg_secondary); - } else { - views.setInt(R.id.widget_row, "setBackgroundResource", - R.drawable.agenda_item_bg_primary); - } - - if (!eventInfo.allDay) { - updateTextView(views, R.id.when, eventInfo.visibWhen, eventInfo.when); - updateTextView(views, R.id.where, eventInfo.visibWhere, eventInfo.where); - } - updateTextView(views, R.id.title, eventInfo.visibTitle, eventInfo.title); - - views.setViewVisibility(R.id.agenda_item_color, View.VISIBLE); - - int selfAttendeeStatus = eventInfo.selfAttendeeStatus; - if (eventInfo.allDay) { - if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_not_responded_bg); - views.setInt(R.id.title, "setTextColor", displayColor); - } else { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); - views.setInt(R.id.title, "setTextColor", mAllDayColor); - } - if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { - // 40% opacity - views.setInt(R.id.agenda_item_color, "setColorFilter", - Utils.getDeclinedColorFromColor(displayColor)); - } else { - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); - } - } else if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_DECLINED) { - views.setInt(R.id.title, "setTextColor", mDeclinedColor); - views.setInt(R.id.when, "setTextColor", mDeclinedColor); - views.setInt(R.id.where, "setTextColor", mDeclinedColor); - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); - // 40% opacity - views.setInt(R.id.agenda_item_color, "setColorFilter", - Utils.getDeclinedColorFromColor(displayColor)); - } else { - views.setInt(R.id.title, "setTextColor", mStandardColor); - views.setInt(R.id.when, "setTextColor", mStandardColor); - views.setInt(R.id.where, "setTextColor", mStandardColor); - if (selfAttendeeStatus == Attendees.ATTENDEE_STATUS_INVITED) { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_not_responded_bg); - } else { - views.setInt(R.id.agenda_item_color, "setImageResource", - R.drawable.widget_chip_responded_bg); - } - views.setInt(R.id.agenda_item_color, "setColorFilter", displayColor); - } - - long start = eventInfo.start; - long end = eventInfo.end; - // An element in ListView. - if (eventInfo.allDay) { - String tz = Utils.getTimeZone(mContext, null); - Time recycle = new Time(); - start = Utils.convertAlldayLocalToUTC(recycle, start, tz); - end = Utils.convertAlldayLocalToUTC(recycle, end, tz); - } - final Intent fillInIntent = CalendarAppWidgetProvider.getLaunchFillInIntent( - mContext, eventInfo.id, start, end, eventInfo.allDay); - views.setOnClickFillInIntent(R.id.widget_row, fillInIntent); - return views; - } - } - - @Override - public int getViewTypeCount() { - return 5; - } - - @Override - public int getCount() { - // if there are no events, we still return 1 to represent the "no - // events" view - if (mModel == null) { - return 1; - } - return Math.max(1, mModel.mRowInfos.size()); - } - - @Override - public long getItemId(int position) { - if (mModel == null || mModel.mRowInfos.isEmpty() || position >= getCount()) { - return 0; - } - RowInfo rowInfo = mModel.mRowInfos.get(position); - if (rowInfo.mType == RowInfo.TYPE_DAY) { - return rowInfo.mIndex; - } - EventInfo eventInfo = mModel.mEventInfos.get(rowInfo.mIndex); - long prime = 31; - long result = 1; - result = prime * result + (int) (eventInfo.id ^ (eventInfo.id >>> 32)); - result = prime * result + (int) (eventInfo.start ^ (eventInfo.start >>> 32)); - return result; - } - - @Override - public boolean hasStableIds() { - return true; - } - - /** - * Query across all calendars for upcoming event instances from now - * until some time in the future. Widen the time range that we query by - * one day on each end so that we can catch all-day events. All-day - * events are stored starting at midnight in UTC but should be included - * in the list of events starting at midnight local time. This may fetch - * more events than we actually want, so we filter them out later. - * - * @param selection The selection string for the loader to filter the query with. - */ - public void initLoader(String selection) { - if (LOGD) - Log.d(TAG, "Querying for widget events..."); - - // Search for events from now until some time in the future - Uri uri = createLoaderUri(); - mLoader = new CursorLoader(mContext, uri, EVENT_PROJECTION, selection, null, - EVENT_SORT_ORDER); - mLoader.setUpdateThrottle(WIDGET_UPDATE_THROTTLE); - synchronized (mLock) { - mLastSerialNum = ++mSerialNum; - } - mLoader.registerListener(mAppWidgetId, this); - mLoader.startLoading(); - - } - - /** - * This gets the selection string for the loader. This ends up doing a query in the - * shared preferences. - */ - private String queryForSelection() { - return Utils.getHideDeclinedEvents(mContext) ? EVENT_SELECTION_HIDE_DECLINED - : EVENT_SELECTION; - } - - /** - * @return The uri for the loader - */ - private Uri createLoaderUri() { - long now = System.currentTimeMillis(); - // Add a day on either side to catch all-day events - long begin = now - DateUtils.DAY_IN_MILLIS; - long end = now + SEARCH_DURATION + DateUtils.DAY_IN_MILLIS; - - Uri uri = Uri.withAppendedPath(Instances.CONTENT_URI, Long.toString(begin) + "/" + end); - return uri; - } - - /* @VisibleForTesting */ - protected static CalendarAppWidgetModel buildAppWidgetModel( - Context context, Cursor cursor, String timeZone) { - CalendarAppWidgetModel model = new CalendarAppWidgetModel(context, timeZone); - model.buildFromCursor(cursor, timeZone); - return model; - } - - /** - * Calculates and returns the next time we should push widget updates. - */ - private long calculateUpdateTime(CalendarAppWidgetModel model, long now, String timeZone) { - // Make sure an update happens at midnight or earlier - long minUpdateTime = getNextMidnightTimeMillis(timeZone); - for (EventInfo event : model.mEventInfos) { - final long start; - final long end; - start = event.start; - end = event.end; - - // We want to update widget when we enter/exit time range of an event. - if (now < start) { - minUpdateTime = Math.min(minUpdateTime, start); - } else if (now < end) { - minUpdateTime = Math.min(minUpdateTime, end); - } - } - return minUpdateTime; - } - - private static long getNextMidnightTimeMillis(String timezone) { - Time time = new Time(); - time.setToNow(); - time.monthDay++; - time.hour = 0; - time.minute = 0; - time.second = 0; - long midnightDeviceTz = time.normalize(true); - - time.timezone = timezone; - time.setToNow(); - time.monthDay++; - time.hour = 0; - time.minute = 0; - time.second = 0; - long midnightHomeTz = time.normalize(true); - - return Math.min(midnightDeviceTz, midnightHomeTz); - } - - static void updateTextView(RemoteViews views, int id, int visibility, String string) { - views.setViewVisibility(id, visibility); - if (visibility == View.VISIBLE) { - views.setTextViewText(id, string); - } - } - - /* - * (non-Javadoc) - * @see - * android.content.Loader.OnLoadCompleteListener#onLoadComplete(android - * .content.Loader, java.lang.Object) - */ - @Override - public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) { - if (cursor == null) { - return; - } - // If a newer update has happened since we started clean up and - // return - synchronized (mLock) { - if (cursor.isClosed()) { - Log.wtf(TAG, "Got a closed cursor from onLoadComplete"); - return; - } - - if (mLastSerialNum != mSerialNum) { - return; - } - - final long now = System.currentTimeMillis(); - String tz = Utils.getTimeZone(mContext, mTimezoneChanged); - - // Copy it to a local static cursor. - MatrixCursor matrixCursor = Utils.matrixCursorFromCursor(cursor); - try { - mModel = buildAppWidgetModel(mContext, matrixCursor, tz); - } finally { - if (matrixCursor != null) { - matrixCursor.close(); - } - - if (cursor != null) { - cursor.close(); - } - } - - // Schedule an alarm to wake ourselves up for the next update. - // We also cancel - // all existing wake-ups because PendingIntents don't match - // against extras. - long triggerTime = calculateUpdateTime(mModel, now, tz); - - // If no next-update calculated, or bad trigger time in past, - // schedule - // update about six hours from now. - if (triggerTime < now) { - Log.w(TAG, "Encountered bad trigger time " + formatDebugTime(triggerTime, now)); - triggerTime = now + UPDATE_TIME_NO_EVENTS; - } - - final AlarmManager alertManager = (AlarmManager) mContext - .getSystemService(Context.ALARM_SERVICE); - final PendingIntent pendingUpdate = CalendarAppWidgetProvider - .getUpdateIntent(mContext); - - alertManager.cancel(pendingUpdate); - alertManager.set(AlarmManager.RTC, triggerTime, pendingUpdate); - Time time = new Time(Utils.getTimeZone(mContext, null)); - time.setToNow(); - - if (time.normalize(true) != sLastUpdateTime) { - Time time2 = new Time(Utils.getTimeZone(mContext, null)); - time2.set(sLastUpdateTime); - time2.normalize(true); - if (time.year != time2.year || time.yearDay != time2.yearDay) { - final Intent updateIntent = new Intent( - Utils.getWidgetUpdateAction(mContext)); - mContext.sendBroadcast(updateIntent); - } - - sLastUpdateTime = time.toMillis(true); - } - - AppWidgetManager widgetManager = AppWidgetManager.getInstance(mContext); - if (widgetManager == null) { - return; - } - if (mAppWidgetId == -1) { - int[] ids = widgetManager.getAppWidgetIds(CalendarAppWidgetProvider - .getComponentName(mContext)); - - widgetManager.notifyAppWidgetViewDataChanged(ids, R.id.events_list); - } else { - widgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId, R.id.events_list); - } - } - } - - @Override - public void onReceive(Context context, Intent intent) { - if (LOGD) - Log.d(TAG, "AppWidgetService received an intent. It was " + intent.toString()); - mContext = context; - - // We cannot do any queries from the UI thread, so push the 'selection' query - // to a background thread. However the implementation of the latter query - // (cursor loading) uses CursorLoader which must be initiated from the UI thread, - // so there is some convoluted handshaking here. - // - // Note that as currently implemented, this must run in a single threaded executor - // or else the loads may be run out of order. - // - // TODO: Remove use of mHandler and CursorLoader, and do all the work synchronously - // in the background thread. All the handshaking going on here between the UI and - // background thread with using goAsync, mHandler, and CursorLoader is confusing. - final PendingResult result = goAsync(); - executor.submit(new Runnable() { - @Override - public void run() { - // We always complete queryForSelection() even if the load task ends up being - // canceled because of a more recent one. Optimizing this to allow - // canceling would require keeping track of all the PendingResults - // (from goAsync) to abort them. Defer this until it becomes a problem. - final String selection = queryForSelection(); - - if (mLoader == null) { - mAppWidgetId = -1; - mHandler.post(new Runnable() { - @Override - public void run() { - initLoader(selection); - result.finish(); - } - }); - } else { - mHandler.post(createUpdateLoaderRunnable(selection, result, - currentVersion.incrementAndGet())); - } - } - }); - } - } - - /** - * Format given time for debugging output. - * - * @param unixTime Target time to report. - * @param now Current system time from {@link System#currentTimeMillis()} - * for calculating time difference. - */ - static String formatDebugTime(long unixTime, long now) { - Time time = new Time(); - time.set(unixTime); - - long delta = unixTime - now; - if (delta > DateUtils.MINUTE_IN_MILLIS) { - delta /= DateUtils.MINUTE_IN_MILLIS; - return String.format("[%d] %s (%+d mins)", unixTime, - time.format("%H:%M:%S"), delta); - } else { - delta /= DateUtils.SECOND_IN_MILLIS; - return String.format("[%d] %s (%+d secs)", unixTime, - time.format("%H:%M:%S"), delta); - } - } -} |