Home | History | Annotate | Download | only in calendar
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.providers.calendar;
     18 
     19 import android.accounts.Account;
     20 import android.accounts.AccountManager;
     21 import android.content.ContentResolver;
     22 import android.content.ContentUris;
     23 import android.content.ContentValues;
     24 import android.content.Context;
     25 import android.database.Cursor;
     26 import android.net.Uri;
     27 import android.os.SystemClock;
     28 import android.provider.CalendarContract;
     29 import android.test.SyncBaseInstrumentation;
     30 import android.text.format.DateUtils;
     31 import android.text.format.Time;
     32 import android.util.Log;
     33 
     34 import com.google.android.collect.Maps;
     35 
     36 import java.util.HashSet;
     37 import java.util.Map;
     38 import java.util.Set;
     39 
     40 public class CalendarSyncTestingBase extends SyncBaseInstrumentation {
     41     protected AccountManager mAccountManager;
     42     protected Context mTargetContext;
     43     protected String mAccount;
     44     protected ContentResolver mResolver;
     45     protected Uri mEventsUri = CalendarContract.Events.CONTENT_URI;
     46 
     47     static final String TAG = "calendar";
     48     static final String DEFAULT_TIMEZONE = "America/Los_Angeles";
     49     static final Set<String> EVENT_COLUMNS_TO_SKIP = new HashSet<String>();
     50     static final Set<String> ATTENDEES_COLUMNS_TO_SKIP = new HashSet<String>();
     51     static final Set<String> CALENDARS_COLUMNS_TO_SKIP = new HashSet<String>();
     52     static final Set<String> INSTANCES_COLUMNS_TO_SKIP = new HashSet<String>();
     53 
     54     static {
     55         EVENT_COLUMNS_TO_SKIP.add(CalendarContract.Events._ID);
     56         EVENT_COLUMNS_TO_SKIP.add(CalendarContract.Events.SYNC_DATA5);
     57         EVENT_COLUMNS_TO_SKIP.add(CalendarContract.Events.SYNC_DATA4);
     58         EVENT_COLUMNS_TO_SKIP.add(CalendarContract.Events.SYNC_DATA2);
     59         EVENT_COLUMNS_TO_SKIP.add(CalendarContract.Events.DIRTY);
     60         EVENT_COLUMNS_TO_SKIP.add(CalendarContract.Events.SYNC_DATA8);
     61         ATTENDEES_COLUMNS_TO_SKIP.add(CalendarContract.Attendees._ID);
     62         CALENDARS_COLUMNS_TO_SKIP.add(CalendarContract.Calendars._ID);
     63         CALENDARS_COLUMNS_TO_SKIP.add(CalendarContract.Calendars.CAL_SYNC8);
     64         CALENDARS_COLUMNS_TO_SKIP.add(CalendarContract.Calendars.CAL_SYNC7);
     65         CALENDARS_COLUMNS_TO_SKIP.add(CalendarContract.Calendars.DIRTY);
     66         CALENDARS_COLUMNS_TO_SKIP.add(CalendarContract.Calendars.CAL_SYNC6);
     67         INSTANCES_COLUMNS_TO_SKIP.add(CalendarContract.Instances._ID);
     68     }
     69 
     70     @Override
     71     protected void setUp() throws Exception {
     72         super.setUp();
     73         mTargetContext = getInstrumentation().getTargetContext();
     74 
     75         mAccountManager = AccountManager.get(mTargetContext);
     76         mAccount = getAccount();
     77         mResolver = mTargetContext.getContentResolver();
     78     }
     79 
     80     /**
     81      * A simple method that syncs the calendar provider.
     82      * @throws Exception
     83      */
     84     protected void syncCalendar() throws Exception {
     85         cancelSyncsandDisableAutoSync();
     86         syncProvider(CalendarContract.CONTENT_URI, mAccount, CalendarContract.AUTHORITY);
     87     }
     88 
     89     /**
     90      * Creates a new event in the default calendar.
     91      * @param event Event to be created.
     92      * @return Uri of the created event.
     93      * @throws Exception
     94      */
     95     protected Uri insertEvent(EventInfo event) throws Exception {
     96         return insertEvent(getDefaultCalendarId(), event);
     97     }
     98 
     99     /**
    100      * Creates a new event in the given calendarId.
    101      * @param calendarId Calendar to be used.
    102      * @param event Event to be created.
    103      * @return Uri of the event created.
    104      * @throws Exception
    105      */
    106     protected Uri insertEvent(int calendarId, EventInfo event) throws Exception{
    107         ContentValues m = new ContentValues();
    108         m.put(CalendarContract.Events.CALENDAR_ID, calendarId);
    109         m.put(CalendarContract.Events.TITLE, event.mTitle);
    110         m.put(CalendarContract.Events.DTSTART, event.mDtstart);
    111         m.put(CalendarContract.Events.ALL_DAY, event.mAllDay ? 1 : 0);
    112 
    113         if (event.mRrule == null) {
    114             // This is a normal event
    115             m.put(CalendarContract.Events.DTEND, event.mDtend);
    116         } else {
    117             // This is a repeating event
    118             m.put(CalendarContract.Events.RRULE, event.mRrule);
    119             m.put(CalendarContract.Events.DURATION, event.mDuration);
    120         }
    121 
    122         if (event.mDescription != null) {
    123             m.put(CalendarContract.Events.DESCRIPTION, event.mDescription);
    124         }
    125         if (event.mTimezone != null) {
    126             m.put(CalendarContract.Events.EVENT_TIMEZONE, event.mTimezone);
    127         }
    128 
    129         Uri url = mResolver.insert(mEventsUri, m);
    130         syncCalendar();
    131         return url;
    132     }
    133 
    134     /**
    135      * Edits the given event.
    136      * @param eventId EventID of the event to be edited.
    137      * @param event Edited event details.
    138      * @throws Exception
    139      */
    140     protected void editEvent(long eventId, EventInfo event) throws Exception {
    141         ContentValues values = new ContentValues();
    142         values.put(CalendarContract.Events.TITLE, event.mTitle);
    143         values.put(CalendarContract.Events.DTSTART, event.mDtstart);
    144         values.put(CalendarContract.Events.DTEND, event.mDtend);
    145         values.put(CalendarContract.Events.ALL_DAY, event.mAllDay ? 1 : 0);
    146 
    147         if (event.mDescription != null) {
    148             values.put(CalendarContract.Events.DESCRIPTION, event.mDescription);
    149         }
    150 
    151         Uri uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId);
    152         mResolver.update(uri, values, null, null);
    153         syncCalendar();
    154     }
    155 
    156     /**
    157      * Deletes a given event.
    158      * @param uri
    159      * @throws Exception
    160      */
    161     protected void deleteEvent(Uri uri) throws Exception {
    162         mResolver.delete(uri, null, null);
    163         syncCalendar();
    164     }
    165 
    166     /**
    167      * Inserts a new calendar.
    168      * @param name
    169      * @param timezone
    170      * @param calendarUrl
    171      * @throws Exception
    172      */
    173     protected void insertCalendar(String name, String timezone, String calendarUrl)
    174             throws Exception {
    175         ContentValues values = new ContentValues();
    176 
    177         values.put(CalendarContract.Calendars.ACCOUNT_NAME, getAccount());
    178         values.put(CalendarContract.Calendars.CAL_SYNC1, calendarUrl);
    179         values.put(CalendarContract.Calendars.NAME, name);
    180         values.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, name);
    181 
    182         values.put(CalendarContract.Calendars.SYNC_EVENTS, 1);
    183         values.put(CalendarContract.Calendars.VISIBLE, 1);
    184         values.put(CalendarContract.Calendars.CALENDAR_COLOR, -14069085 /* blue */);
    185         values.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL,
    186                 CalendarContract.Calendars.CAL_ACCESS_OWNER);
    187 
    188         values.put(CalendarContract.Calendars.CALENDAR_COLOR, "0xff123456");
    189         values.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timezone);
    190         mResolver.insert(CalendarContract.Calendars.CONTENT_URI, values);
    191         syncCalendar();
    192     }
    193 
    194     /**
    195      * Returns a fresh count of events.
    196      * @return
    197      */
    198     protected int getEventsCount() {
    199         Cursor cursor;
    200         cursor = mResolver.query(mEventsUri, null, null, null, null);
    201         return cursor.getCount();
    202     }
    203 
    204     /**
    205      * Returns the ID of the default calendar.
    206      * @return
    207      */
    208     protected int getDefaultCalendarId() {
    209         Cursor calendarsCursor;
    210         calendarsCursor = mResolver.query(CalendarContract.Calendars.CONTENT_URI, null, null, null,
    211                 null);
    212         calendarsCursor.moveToNext();
    213         return calendarsCursor.getInt(calendarsCursor.getColumnIndex("_id"));
    214     }
    215 
    216     /**
    217      * This class stores all the useful information about an event.
    218      */
    219     protected class EventInfo {
    220         String mTitle;
    221         String mDescription;
    222         String mTimezone;
    223         boolean mAllDay;
    224         long mDtstart;
    225         long mDtend;
    226         String mRrule;
    227         String mDuration;
    228         String mOriginalTitle;
    229         long mOriginalInstance;
    230         int mSyncId;
    231 
    232         // Constructor for normal events, using the default timezone
    233         public EventInfo(String title, String startDate, String endDate,
    234                 boolean allDay) {
    235             init(title, startDate, endDate, allDay, DEFAULT_TIMEZONE);
    236         }
    237 
    238         public EventInfo(String title, long startDate, long endDate,
    239                 boolean allDay) {
    240             mTitle = title;
    241             mTimezone = DEFAULT_TIMEZONE;
    242             mDtstart = startDate;
    243             mDtend = endDate;
    244             mDuration = null;
    245             mRrule = null;
    246             mAllDay = allDay;
    247         }
    248 
    249         public EventInfo(String title, long startDate, long endDate,
    250                 boolean allDay, String description) {
    251             mTitle = title;
    252             mTimezone = DEFAULT_TIMEZONE;
    253             mDtstart = startDate;
    254             mDtend = endDate;
    255             mDuration = null;
    256             mRrule = null;
    257             mAllDay = allDay;
    258             mDescription = description;
    259         }
    260 
    261         // Constructor for normal events, specifying the timezone
    262         public EventInfo(String title, String startDate, String endDate,
    263                 boolean allDay, String timezone) {
    264             init(title, startDate, endDate, allDay, timezone);
    265         }
    266 
    267         public void init(String title, String startDate, String endDate,
    268                 boolean allDay, String timezone) {
    269             mTitle = title;
    270             Time time = new Time();
    271             if (allDay) {
    272                 time.timezone = Time.TIMEZONE_UTC;
    273             } else if (timezone != null) {
    274                 time.timezone = timezone;
    275             }
    276             mTimezone = time.timezone;
    277             time.parse3339(startDate);
    278             mDtstart = time.toMillis(false /* use isDst */);
    279             time.parse3339(endDate);
    280             mDtend = time.toMillis(false /* use isDst */);
    281             mDuration = null;
    282             mRrule = null;
    283             mAllDay = allDay;
    284         }
    285 
    286         // Constructor for repeating events, using the default timezone
    287         public EventInfo(String title, String description, String startDate, String endDate,
    288                 String rrule, boolean allDay) {
    289             init(title, description, startDate, endDate, rrule, allDay, DEFAULT_TIMEZONE);
    290         }
    291 
    292         // Constructor for repeating events, specifying the timezone
    293         public EventInfo(String title, String description, String startDate, String endDate,
    294                 String rrule, boolean allDay, String timezone) {
    295             init(title, description, startDate, endDate, rrule, allDay, timezone);
    296         }
    297 
    298         public void init(String title, String description, String startDate, String endDate,
    299                 String rrule, boolean allDay, String timezone) {
    300             mTitle = title;
    301             mDescription = description;
    302             Time time = new Time();
    303             if (allDay) {
    304                 time.timezone = Time.TIMEZONE_UTC;
    305             } else if (timezone != null) {
    306                 time.timezone = timezone;
    307             }
    308             mTimezone = time.timezone;
    309             time.parse3339(startDate);
    310             mDtstart = time.toMillis(false /* use isDst */);
    311             if (endDate != null) {
    312                 time.parse3339(endDate);
    313                 mDtend = time.toMillis(false /* use isDst */);
    314             }
    315             if (allDay) {
    316                 long days = 1;
    317                 if (endDate != null) {
    318                     days = (mDtend - mDtstart) / DateUtils.DAY_IN_MILLIS;
    319                 }
    320                 mDuration = "P" + days + "D";
    321             } else {
    322                 long seconds = (mDtend - mDtstart) / DateUtils.SECOND_IN_MILLIS;
    323                 mDuration = "P" + seconds + "S";
    324             }
    325             mRrule = rrule;
    326             mAllDay = allDay;
    327         }
    328 
    329         // Constructor for recurrence exceptions, using the default timezone
    330         public EventInfo(String originalTitle, String originalInstance, String title,
    331                 String description, String startDate, String endDate, boolean allDay) {
    332             init(originalTitle, originalInstance,
    333                     title, description, startDate, endDate, allDay, DEFAULT_TIMEZONE);
    334         }
    335 
    336         public void init(String originalTitle, String originalInstance,
    337                 String title, String description, String startDate, String endDate,
    338                 boolean allDay, String timezone) {
    339             mOriginalTitle = originalTitle;
    340             Time time = new Time(timezone);
    341             time.parse3339(originalInstance);
    342             mOriginalInstance = time.toMillis(false /* use isDst */);
    343             init(title, description, startDate, endDate, null /* rrule */, allDay, timezone);
    344         }
    345     }
    346 
    347     /**
    348      * Returns the default account on the device.
    349      * @return
    350      */
    351     protected String getAccount() {
    352         Account[] accounts = mAccountManager.getAccountsByType("com.google");
    353 
    354         assertTrue("Didn't find any Google accounts", accounts.length > 0);
    355 
    356         Account account = accounts[accounts.length - 1];
    357         Log.v(TAG, "Found " + accounts.length + " accounts; using the last one, " + account.name);
    358         return account.name;
    359     }
    360 
    361     /**
    362      * Compares two cursors
    363      */
    364     protected void compareCursors(Cursor cursor1, Cursor cursor2,
    365                                   Set<String> columnsToSkip, String tableName) {
    366         String[] cols = cursor1.getColumnNames();
    367         int length = cols.length;
    368 
    369         assertEquals(tableName + " count failed to match", cursor1.getCount(),
    370                 cursor2.getCount());
    371         Map<String, String> row = Maps.newHashMap();
    372         while (cursor1.moveToNext() && cursor2.moveToNext()) {
    373             for (int i = 0; i < length; i++) {
    374                 String col = cols[i];
    375                 if (columnsToSkip != null && columnsToSkip.contains(col)) {
    376                     continue;
    377                 }
    378                 row.put(col, cursor1.getString(i));
    379 
    380                 assertEquals("Row: " + row + " Table: " + tableName + ": " + cols[i] +
    381                         " failed to match", cursor1.getString(i),
    382                         cursor2.getString(i));
    383             }
    384         }
    385     }
    386 }
    387