/*
* 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 com.android.calendar.CalendarController.EventType;
import com.android.calendar.CalendarController.ViewType;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
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 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;
private static final int MENU_AGENDA = 2;
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;
/**
* 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 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 UpdateCurrentTime mUpdateCurrentTime = new UpdateCurrentTime();
private int mTodayJulianDay;
private Typeface mBold = Typeface.DEFAULT_BOLD;
private int mFirstJulianDay;
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 CharSequence[] mLongPressItems;
private String mLongPressTitle;
protected static StringBuilder mStringBuilder = new StringBuilder(50);
// TODO recreate formatter when locale changes
protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
private Runnable mTZUpdater = new Runnable() {
@Override
public void run() {
String tz = Utils.getTimeZone(mContext, this);
mBaseDate.timezone = tz;
mBaseDate.normalize(true);
mCurrentTime.switchTimezone(tz);
invalidate();
}
};
private 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 !