am ba79889d: (-s ours) Disallow empty eventTimezone values. Do not merge
* commit 'ba79889dd64ed6ab2ca8d25d97c30e6168f55f6b':
Disallow empty eventTimezone values. Do not merge
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
new file mode 100644
index 0000000..9fe4148
--- /dev/null
+++ b/res/values-be/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="calendar_storage" msgid="5387668002987562770">"Сховішча каляндара"</string>
+ <string name="upgrade_msg" msgid="2792831029435070926">"Абнаўленне базы дадзеных Calendar."</string>
+ <string name="calendar_default_name" msgid="6924293766625167275">"Па змаўчанні"</string>
+ <string name="calendar_info" msgid="6687678621418059281">"Інфармацыя пра каляндар"</string>
+ <string name="calendar_info_error" msgid="5575162446528419982">"Памылка"</string>
+ <string name="calendar_info_no_calendars" msgid="4287534468186704433">"Няма календароў"</string>
+ <string name="calendar_info_events" msgid="1805502308105103803">"Мерапрыемствы: <xliff:g id="EVENTS">%1$d</xliff:g>"</string>
+ <string name="calendar_info_events_dirty" msgid="8879392112564499515">"Мерапрыемствы: <xliff:g id="EVENTS_0">%1$d</xliff:g>; незахаваных: <xliff:g id="DIRTY_EVENTS">%2$d</xliff:g>"</string>
+ <string name="provider_label" msgid="2306513350843464739">"Calendar"</string>
+ <string name="debug_tool_delete_button" msgid="5052706251268452090">"Выдаліць"</string>
+ <string name="debug_tool_start_button" msgid="5384780896342913563">"Пачаць"</string>
+ <string name="debug_tool_message" msgid="4414152820946316089">"Вы збіраецеся: 1) зрабіць копію базы дадзеных свайго календара на SD-карту ці USB-назапашвальнiк, якія можа прачытаць любое прыкладанне, і 2) адправіць яе па электроннай пошце. Абавязкова выдаліце яе, як толькі яна будзе паспяхова скапіявана з прылады ці будзе атрыманы электронны ліст."</string>
+ <string name="debug_tool_email_sender_picker" msgid="4418621476577347562">"Выберыце праграму для адпраўкі файла"</string>
+ <string name="debug_tool_email_subject" msgid="2403590332256471194">"Каляндар Db далучаны"</string>
+ <string name="debug_tool_email_body" msgid="740309162644398319">"Укладзена база дадзеных майго календара з усімі сустрэчамі і асабістай інфармацыяй. Працуйце з ёй уважліва."</string>
+</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
new file mode 100644
index 0000000..55e28a5
--- /dev/null
+++ b/res/values-et/strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="calendar_storage" msgid="5387668002987562770">"Kalendri mälumaht"</string>
+ <string name="upgrade_msg" msgid="2792831029435070926">"Kalendri andmebaasi uuendamine."</string>
+ <string name="calendar_default_name" msgid="6924293766625167275">"Vaikimisi"</string>
+ <string name="calendar_info" msgid="6687678621418059281">"Kalendri teave"</string>
+ <string name="calendar_info_error" msgid="5575162446528419982">"Viga"</string>
+ <string name="calendar_info_no_calendars" msgid="4287534468186704433">"Kalendrid puuduvad"</string>
+ <string name="calendar_info_events" msgid="1805502308105103803">"Sündmused: <xliff:g id="EVENTS">%1$d</xliff:g>"</string>
+ <string name="calendar_info_events_dirty" msgid="8879392112564499515">"Sündmused: <xliff:g id="EVENTS_0">%1$d</xliff:g>, salvestamata: <xliff:g id="DIRTY_EVENTS">%2$d</xliff:g>"</string>
+ <string name="provider_label" msgid="2306513350843464739">"Kalender"</string>
+ <string name="debug_tool_delete_button" msgid="5052706251268452090">"Kustuta kohe"</string>
+ <string name="debug_tool_start_button" msgid="5384780896342913563">"Alusta"</string>
+ <string name="debug_tool_message" msgid="4414152820946316089">"Olete 1) tegemas koopiat oma kalendri andmebaasist SD-kaardile/USB-mäluruumi, mida saavad lugeda kõik rakendused, ja 2) seda meilimas. Kustutage see kindlasti kohe pärast seadmest kustutamist või meili kättesaamist."</string>
+ <string name="debug_tool_email_sender_picker" msgid="4418621476577347562">"Vali programm faili saatmiseks"</string>
+ <string name="debug_tool_email_subject" msgid="2403590332256471194">"Kalendri Db on lisatud"</string>
+ <string name="debug_tool_email_body" msgid="740309162644398319">"Lisatud on minu kalendri andmebaas koos kõigi kohtumiste ja isiklike andmetega. Käsitlege neid ettevaatlikult."</string>
+</resources>
diff --git a/src/com/android/providers/calendar/CalendarDatabaseHelper.java b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
index 586515c..d609bdf 100644
--- a/src/com/android/providers/calendar/CalendarDatabaseHelper.java
+++ b/src/com/android/providers/calendar/CalendarDatabaseHelper.java
@@ -28,6 +28,8 @@
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Attendees;
+import android.provider.CalendarContract.Calendars;
+import android.provider.CalendarContract.Colors;
import android.provider.CalendarContract.Events;
import android.provider.CalendarContract.Reminders;
import android.provider.SyncStateContract;
@@ -60,7 +62,7 @@
// Versions under 100 cover through Froyo, 1xx version are for Gingerbread,
// 2xx for Honeycomb, and 3xx for ICS. For future versions bump this to the
// next hundred at each major release.
- static final int DATABASE_VERSION = 307;
+ static final int DATABASE_VERSION = 308;
private static final int PRE_FROYO_SYNC_STATE_VERSION = 3;
@@ -72,6 +74,7 @@
Events.EVENT_LOCATION + "," +
Events.DESCRIPTION + "," +
Events.EVENT_COLOR + "," +
+ Events.EVENT_COLOR_KEY + "," +
Events.STATUS + "," +
Events.SELF_ATTENDEE_STATUS + "," +
Events.DTSTART + "," +
@@ -130,6 +133,7 @@
public static final String CALENDAR_CACHE = "CalendarCache";
public static final String SYNC_STATE = "_sync_state";
public static final String SYNC_STATE_META = "_sync_state_metadata";
+ public static final String COLORS = "Colors";
}
public interface Views {
@@ -179,6 +183,33 @@
" WHERE " + CalendarContract.Events.CALENDAR_ID + "=" +
"old." + CalendarContract.Events._ID + ";";
+ private static final String CALENDAR_UPDATE_COLOR_TRIGGER_SQL = "UPDATE " + Tables.CALENDARS
+ + " SET calendar_color=(SELECT " + Colors.COLOR + " FROM " + Tables.COLORS + " WHERE "
+ + Colors.ACCOUNT_NAME + "=" + "new." + Calendars.ACCOUNT_NAME + " AND "
+ + Colors.ACCOUNT_TYPE + "=" + "new." + Calendars.ACCOUNT_TYPE + " AND "
+ + Colors.COLOR_KEY + "=" + "new." + Calendars.CALENDAR_COLOR_KEY + ") "
+ + " WHERE " + Calendars._ID + "=" + "old." + Calendars._ID
+ + ";";
+ private static final String CALENDAR_COLOR_UPDATE_TRIGGER_NAME = "calendar_color_update";
+ private static final String CREATE_CALENDAR_COLOR_UPDATE_TRIGGER = "CREATE TRIGGER "
+ + CALENDAR_COLOR_UPDATE_TRIGGER_NAME + " UPDATE OF " + Calendars.CALENDAR_COLOR_KEY
+ + " ON " + Tables.CALENDARS + " WHEN new." + Calendars.CALENDAR_COLOR_KEY
+ + " NOT NULL BEGIN " + CALENDAR_UPDATE_COLOR_TRIGGER_SQL + " END";
+
+ private static final String EVENT_UPDATE_COLOR_TRIGGER_SQL = "UPDATE " + Tables.EVENTS
+ + " SET eventColor=(SELECT " + Colors.COLOR + " FROM " + Tables.COLORS + " WHERE "
+ + Colors.ACCOUNT_NAME + "=" + "(SELECT " + Calendars.ACCOUNT_NAME + " FROM "
+ + Tables.CALENDARS + " WHERE " + Calendars._ID + "=new." + Events.CALENDAR_ID
+ + ") AND " + Colors.ACCOUNT_TYPE + "=" + "(SELECT " + Calendars.ACCOUNT_TYPE + " FROM "
+ + Tables.CALENDARS + " WHERE " + Calendars._ID + "=new." + Events.CALENDAR_ID
+ + ") AND " + Colors.COLOR_KEY + "=" + "new." + Events.EVENT_COLOR_KEY + ") "
+ + " WHERE " + Events._ID + "=" + "old." + Events._ID + ";";
+ private static final String EVENT_COLOR_UPDATE_TRIGGER_NAME = "event_color_update";
+ private static final String CREATE_EVENT_COLOR_UPDATE_TRIGGER = "CREATE TRIGGER "
+ + EVENT_COLOR_UPDATE_TRIGGER_NAME + " UPDATE OF " + Events.EVENT_COLOR_KEY + " ON "
+ + Tables.EVENTS + " WHEN new." + Events.EVENT_COLOR_KEY + " NOT NULL BEGIN "
+ + EVENT_UPDATE_COLOR_TRIGGER_SQL + " END";
+
/** Selects rows from Attendees for which the event_id refers to a nonexistent Event */
private static final String WHERE_ATTENDEES_ORPHANS =
Attendees.EVENT_ID + " IN (SELECT " + Attendees.EVENT_ID + " FROM " +
@@ -200,6 +231,7 @@
private static CalendarDatabaseHelper sSingleton = null;
private DatabaseUtils.InsertHelper mCalendarsInserter;
+ private DatabaseUtils.InsertHelper mColorsInserter;
private DatabaseUtils.InsertHelper mEventsInserter;
private DatabaseUtils.InsertHelper mEventsRawTimesInserter;
private DatabaseUtils.InsertHelper mInstancesInserter;
@@ -212,6 +244,10 @@
return mCalendarsInserter.insert(values);
}
+ public long colorsInsert(ContentValues values) {
+ return mColorsInserter.insert(values);
+ }
+
public long eventsInsert(ContentValues values) {
return mEventsInserter.insert(values);
}
@@ -271,6 +307,7 @@
mSyncState.onDatabaseOpened(db);
mCalendarsInserter = new DatabaseUtils.InsertHelper(db, Tables.CALENDARS);
+ mColorsInserter = new DatabaseUtils.InsertHelper(db, Tables.COLORS);
mEventsInserter = new DatabaseUtils.InsertHelper(db, Tables.EVENTS);
mEventsRawTimesInserter = new DatabaseUtils.InsertHelper(db, Tables.EVENTS_RAW_TIMES);
mInstancesInserter = new DatabaseUtils.InsertHelper(db, Tables.INSTANCES);
@@ -333,6 +370,8 @@
mSyncState.createDatabase(db);
+ createColorsTable(db);
+
createCalendarsTable(db);
createEventsTable(db);
@@ -441,6 +480,10 @@
EVENTS_CLEANUP_TRIGGER_SQL +
"END");
+ // Triggers to update the color stored in an event or a calendar when
+ // the color_index is changed.
+ createColorsTriggers(db);
+
// Trigger to update exceptions when an original event updates its
// _sync_id
db.execSQL(CREATE_SYNC_ID_UPDATE_TRIGGER);
@@ -464,6 +507,7 @@
CalendarContract.Events.EVENT_LOCATION + " TEXT," +
CalendarContract.Events.DESCRIPTION + " TEXT," +
CalendarContract.Events.EVENT_COLOR + " INTEGER," +
+ CalendarContract.Events.EVENT_COLOR_KEY + " TEXT," +
CalendarContract.Events.STATUS + " INTEGER," +
CalendarContract.Events.SELF_ATTENDEE_STATUS + " INTEGER NOT NULL DEFAULT 0," +
// dtstart in millis since epoch
@@ -516,6 +560,68 @@
+ CalendarContract.Events.CALENDAR_ID + ");");
}
+ private void createEventsTable307(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE Events ("
+ + "_id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ + "_sync_id TEXT,"
+ + "dirty INTEGER,"
+ + "lastSynced INTEGER DEFAULT 0,"
+ + "calendar_id INTEGER NOT NULL,"
+ + "title TEXT,"
+ + "eventLocation TEXT,"
+ + "description TEXT,"
+ + "eventColor INTEGER,"
+ + "eventStatus INTEGER,"
+ + "selfAttendeeStatus INTEGER NOT NULL DEFAULT 0,"
+ // dtstart in millis since epoch
+ + "dtstart INTEGER,"
+ // dtend in millis since epoch
+ + "dtend INTEGER,"
+ // timezone for event
+ + "eventTimezone TEXT,"
+ + "duration TEXT,"
+ + "allDay INTEGER NOT NULL DEFAULT 0,"
+ + "accessLevel INTEGER NOT NULL DEFAULT 0,"
+ + "availability INTEGER NOT NULL DEFAULT 0,"
+ + "hasAlarm INTEGER NOT NULL DEFAULT 0,"
+ + "hasExtendedProperties INTEGER NOT NULL DEFAULT 0,"
+ + "rrule TEXT,"
+ + "rdate TEXT,"
+ + "exrule TEXT,"
+ + "exdate TEXT,"
+ + "original_id INTEGER,"
+ // ORIGINAL_SYNC_ID is the _sync_id of recurring event
+ + "original_sync_id TEXT,"
+ // originalInstanceTime is in millis since epoch
+ + "originalInstanceTime INTEGER,"
+ + "originalAllDay INTEGER,"
+ // lastDate is in millis since epoch
+ + "lastDate INTEGER,"
+ + "hasAttendeeData INTEGER NOT NULL DEFAULT 0,"
+ + "guestsCanModify INTEGER NOT NULL DEFAULT 0,"
+ + "guestsCanInviteOthers INTEGER NOT NULL DEFAULT 1,"
+ + "guestsCanSeeGuests INTEGER NOT NULL DEFAULT 1,"
+ + "organizer STRING,"
+ + "deleted INTEGER NOT NULL DEFAULT 0,"
+ // timezone for event with allDay events are in local timezone
+ + "eventEndTimezone TEXT,"
+ // SYNC_DATAX columns are available for use by sync adapters
+ + "sync_data1 TEXT,"
+ + "sync_data2 TEXT,"
+ + "sync_data3 TEXT,"
+ + "sync_data4 TEXT,"
+ + "sync_data5 TEXT,"
+ + "sync_data6 TEXT,"
+ + "sync_data7 TEXT,"
+ + "sync_data8 TEXT,"
+ + "sync_data9 TEXT,"
+ + "sync_data10 TEXT);");
+
+ // **When updating this be sure to also update LAST_SYNCED_EVENT_COLUMNS
+
+ db.execSQL("CREATE INDEX eventsCalendarIdIndex ON Events (calendar_id);");
+ }
+
// TODO Remove this method after merging all ICS upgrades
private void createEventsTable300(SQLiteDatabase db) {
db.execSQL("CREATE TABLE Events (" +
@@ -611,6 +717,24 @@
"END");
}
+ private void createColorsTable(SQLiteDatabase db) {
+
+ db.execSQL("CREATE TABLE " + Tables.COLORS + " (" +
+ CalendarContract.Colors._ID + " INTEGER PRIMARY KEY," +
+ CalendarContract.Colors.ACCOUNT_NAME + " TEXT NOT NULL," +
+ CalendarContract.Colors.ACCOUNT_TYPE + " TEXT NOT NULL," +
+ CalendarContract.Colors.DATA + " TEXT," +
+ CalendarContract.Colors.COLOR_TYPE + " INTEGER NOT NULL," +
+ CalendarContract.Colors.COLOR_KEY + " TEXT NOT NULL," +
+ CalendarContract.Colors.COLOR + " INTEGER NOT NULL" +
+ ");");
+ }
+
+ public void createColorsTriggers(SQLiteDatabase db) {
+ db.execSQL(CREATE_EVENT_COLOR_UPDATE_TRIGGER);
+ db.execSQL(CREATE_CALENDAR_COLOR_UPDATE_TRIGGER);
+ }
+
private void createCalendarsTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.CALENDARS + " (" +
CalendarContract.Calendars._ID + " INTEGER PRIMARY KEY," +
@@ -621,6 +745,7 @@
CalendarContract.Calendars.NAME + " TEXT," +
CalendarContract.Calendars.CALENDAR_DISPLAY_NAME + " TEXT," +
CalendarContract.Calendars.CALENDAR_COLOR + " INTEGER," +
+ CalendarContract.Calendars.CALENDAR_COLOR_KEY + " TEXT," +
CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + " INTEGER," +
CalendarContract.Calendars.VISIBLE + " INTEGER NOT NULL DEFAULT 1," +
CalendarContract.Calendars.SYNC_EVENTS + " INTEGER NOT NULL DEFAULT 0," +
@@ -632,6 +757,8 @@
CalendarContract.Calendars.CAN_PARTIALLY_UPDATE + " INTEGER DEFAULT 0," +
CalendarContract.Calendars.MAX_REMINDERS + " INTEGER DEFAULT 5," +
CalendarContract.Calendars.ALLOWED_REMINDERS + " TEXT DEFAULT '0,1'," +
+ CalendarContract.Calendars.ALLOWED_AVAILABILITY + " TEXT DEFAULT '0,1'," +
+ CalendarContract.Calendars.ALLOWED_ATTENDEE_TYPES + " TEXT DEFAULT '0,1,2'," +
CalendarContract.Calendars.DELETED + " INTEGER NOT NULL DEFAULT 0," +
CalendarContract.Calendars.CAL_SYNC1 + " TEXT," +
CalendarContract.Calendars.CAL_SYNC2 + " TEXT," +
@@ -652,6 +779,47 @@
"END");
}
+ private void createCalendarsTable305(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE Calendars (" +
+ "_id INTEGER PRIMARY KEY," +
+ "account_name TEXT," +
+ "account_type TEXT," +
+ "_sync_id TEXT," +
+ "dirty INTEGER," +
+ "name TEXT," +
+ "calendar_displayName TEXT," +
+ "calendar_color INTEGER," +
+ "calendar_access_level INTEGER," +
+ "visible INTEGER NOT NULL DEFAULT 1," +
+ "sync_events INTEGER NOT NULL DEFAULT 0," +
+ "calendar_location TEXT," +
+ "calendar_timezone TEXT," +
+ "ownerAccount TEXT, " +
+ "canOrganizerRespond INTEGER NOT NULL DEFAULT 1," +
+ "canModifyTimeZone INTEGER DEFAULT 1," +
+ "canPartiallyUpdate INTEGER DEFAULT 0," +
+ "maxReminders INTEGER DEFAULT 5," +
+ "allowedReminders TEXT DEFAULT '0,1'," +
+ "deleted INTEGER NOT NULL DEFAULT 0," +
+ "cal_sync1 TEXT," +
+ "cal_sync2 TEXT," +
+ "cal_sync3 TEXT," +
+ "cal_sync4 TEXT," +
+ "cal_sync5 TEXT," +
+ "cal_sync6 TEXT," +
+ "cal_sync7 TEXT," +
+ "cal_sync8 TEXT," +
+ "cal_sync9 TEXT," +
+ "cal_sync10 TEXT" +
+ ");");
+
+ // Trigger to remove a calendar's events when we delete the calendar
+ db.execSQL("CREATE TRIGGER calendar_cleanup DELETE ON Calendars " +
+ "BEGIN " +
+ "DELETE FROM Events WHERE calendar_id=old._id;" +
+ "END");
+ }
+
private void createCalendarsTable300(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + Tables.CALENDARS + " (" +
"_id INTEGER PRIMARY KEY," +
@@ -1174,6 +1342,11 @@
upgradeToVersion307(db);
oldVersion++;
}
+ if (oldVersion == 307) {
+ upgradeToVersion308(db);
+ oldVersion++;
+ createEventsView = true;
+ }
if (createEventsView) {
createEventsView(db);
}
@@ -1230,6 +1403,33 @@
}
@VisibleForTesting
+ void upgradeToVersion308(SQLiteDatabase db) {
+ /*
+ * Changes from version 307 to 308:
+ * - add Colors table to db
+ * - add eventColor_index to Events table
+ * - add calendar_color_index to Calendars table
+ * - add allowedAttendeeTypes to Calendars table
+ * - add allowedAvailability to Calendars table
+ */
+ createColorsTable(db);
+
+ db.execSQL("ALTER TABLE Calendars ADD COLUMN allowedAvailability TEXT DEFAULT '0,1';");
+ db.execSQL("ALTER TABLE Calendars ADD COLUMN allowedAttendeeTypes TEXT DEFAULT '0,1,2';");
+ db.execSQL("ALTER TABLE Calendars ADD COLUMN calendar_color_index TEXT;");
+ db.execSQL("ALTER TABLE Events ADD COLUMN eventColor_index TEXT;");
+
+ // Default Exchange calendars to be supporting the 'tentative'
+ // availability as well
+ db.execSQL("UPDATE Calendars SET allowedAvailability='0,1,2' WHERE _id IN "
+ + "(SELECT _id FROM Calendars WHERE account_type='com.android.exchange');");
+
+ // Triggers to update the color stored in an event or a calendar when
+ // the color_index is changed.
+ createColorsTriggers(db);
+ }
+
+ @VisibleForTesting
void upgradeToVersion307(SQLiteDatabase db) {
/*
* Changes from version 306 to 307:
@@ -1239,7 +1439,7 @@
db.execSQL("DROP TRIGGER IF EXISTS events_cleanup_delete");
db.execSQL("DROP TRIGGER IF EXISTS original_sync_update");
db.execSQL("DROP INDEX IF EXISTS eventsCalendarIdIndex");
- createEventsTable(db);
+ createEventsTable307(db);
String FIELD_LIST =
"_id, " +
@@ -1350,7 +1550,7 @@
// rename old table, create new table with updated layout
db.execSQL("ALTER TABLE Calendars RENAME TO Calendars_Backup;");
db.execSQL("DROP TRIGGER IF EXISTS calendar_cleanup");
- createCalendarsTable(db);
+ createCalendarsTable305(db);
// copy fields from old to new
db.execSQL("INSERT INTO Calendars (" +
@@ -1423,7 +1623,7 @@
// addition of "autoincrement" to _ID doesn't affect the upgrade path. (Note that
// much older databases may also already have autoincrement set because the change
// was back-ported.)
- createEventsTable(db);
+ createEventsTable307(db);
// copy fields from old to new
db.execSQL("INSERT INTO Events (" +
@@ -2718,6 +2918,7 @@
}
private void dropTables(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.COLORS + ";");
db.execSQL("DROP TABLE IF EXISTS " + Tables.CALENDARS + ";");
db.execSQL("DROP TABLE IF EXISTS " + Tables.EVENTS + ";");
db.execSQL("DROP TABLE IF EXISTS " + Tables.EVENTS_RAW_TIMES + ";");
@@ -2771,6 +2972,7 @@
+ CalendarContract.Events.DESCRIPTION + ","
+ CalendarContract.Events.EVENT_LOCATION + ","
+ CalendarContract.Events.EVENT_COLOR + ","
+ + CalendarContract.Events.EVENT_COLOR_KEY + ","
+ CalendarContract.Events.STATUS + ","
+ CalendarContract.Events.SELF_ATTENDEE_STATUS + ","
+ CalendarContract.Events.DTSTART + ","
@@ -2824,9 +3026,12 @@
+ CalendarContract.Calendars.CALENDAR_LOCATION + ","
+ CalendarContract.Calendars.VISIBLE + ","
+ CalendarContract.Calendars.CALENDAR_COLOR + ","
+ + CalendarContract.Calendars.CALENDAR_COLOR_KEY + ","
+ CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + ","
+ CalendarContract.Calendars.MAX_REMINDERS + ","
+ CalendarContract.Calendars.ALLOWED_REMINDERS + ","
+ + CalendarContract.Calendars.ALLOWED_ATTENDEE_TYPES + ","
+ + CalendarContract.Calendars.ALLOWED_AVAILABILITY + ","
+ CalendarContract.Calendars.CAN_ORGANIZER_RESPOND + ","
+ CalendarContract.Calendars.CAN_MODIFY_TIME_ZONE + ","
+ CalendarContract.Calendars.CAN_PARTIALLY_UPDATE + ","
diff --git a/src/com/android/providers/calendar/CalendarProvider2.java b/src/com/android/providers/calendar/CalendarProvider2.java
index 755f330..47d59f9 100644
--- a/src/com/android/providers/calendar/CalendarProvider2.java
+++ b/src/com/android/providers/calendar/CalendarProvider2.java
@@ -50,6 +50,7 @@
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.CalendarAlerts;
import android.provider.CalendarContract.Calendars;
+import android.provider.CalendarContract.Colors;
import android.provider.CalendarContract.Events;
import android.provider.CalendarContract.Instances;
import android.provider.CalendarContract.Reminders;
@@ -109,6 +110,28 @@
private static final int EVENTS_ORIGINAL_ID_INDEX = 3;
private static final int EVENTS_ORIGINAL_SYNC_ID_INDEX = 4;
+ private static final String[] COLORS_PROJECTION = new String[] {
+ Colors.ACCOUNT_NAME,
+ Colors.ACCOUNT_TYPE,
+ Colors.COLOR_TYPE,
+ Colors.COLOR_KEY,
+ Colors.COLOR,
+ };
+ private static final int COLORS_ACCOUNT_NAME_INDEX = 0;
+ private static final int COLORS_ACCOUNT_TYPE_INDEX = 1;
+ private static final int COLORS_COLOR_TYPE_INDEX = 2;
+ private static final int COLORS_COLOR_INDEX_INDEX = 3;
+ private static final int COLORS_COLOR_INDEX = 4;
+
+ private static final String GENERIC_ACCOUNT_NAME = Calendars.ACCOUNT_NAME;
+ private static final String GENERIC_ACCOUNT_TYPE = Calendars.ACCOUNT_TYPE;
+ private static final String[] ACCOUNT_PROJECTION = new String[] {
+ GENERIC_ACCOUNT_NAME,
+ GENERIC_ACCOUNT_TYPE,
+ };
+ private static final int ACCOUNT_NAME_INDEX = 0;
+ private static final int ACCOUNT_TYPE_INDEX = 1;
+
// many tables have _id and event_id; pick a representative version to use as our generic
private static final String GENERIC_ID = Attendees._ID;
private static final String GENERIC_EVENT_ID = Attendees.EVENT_ID;
@@ -171,6 +194,12 @@
" SET " + Events.DIRTY + "=1" +
" WHERE " + Events._ID + "=?";
+ private static final String SQL_WHERE_CALENDAR_COLOR = Calendars.ACCOUNT_NAME + "=? AND "
+ + Calendars.ACCOUNT_TYPE + "=? AND " + Calendars.CALENDAR_COLOR_KEY + "=?";
+
+ private static final String SQL_WHERE_EVENT_COLOR = Events.ACCOUNT_NAME + "=? AND "
+ + Events.ACCOUNT_TYPE + "=? AND " + Events.EVENT_COLOR_KEY + "=?";
+
protected static final String SQL_WHERE_ID = GENERIC_ID + "=?";
private static final String SQL_WHERE_EVENT_ID = GENERIC_EVENT_ID + "=?";
private static final String SQL_WHERE_ORIGINAL_ID = Events.ORIGINAL_ID + "=?";
@@ -208,6 +237,9 @@
" WHERE " + Calendars.ACCOUNT_NAME + "=? AND " +
Calendars.ACCOUNT_TYPE + "=?";
+ private static final String SQL_DELETE_FROM_COLORS = "DELETE FROM " + Tables.COLORS + " WHERE "
+ + Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?";
+
private static final String SQL_SELECT_COUNT_FOR_SYNC_ID =
"SELECT COUNT(*) FROM " + Tables.EVENTS + " WHERE " + Events._SYNC_ID + "=?";
@@ -340,6 +372,8 @@
ALLOWED_IN_EXCEPTION.add(Events.TITLE);
ALLOWED_IN_EXCEPTION.add(Events.EVENT_LOCATION);
ALLOWED_IN_EXCEPTION.add(Events.DESCRIPTION);
+ ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR);
+ ALLOWED_IN_EXCEPTION.add(Events.EVENT_COLOR_KEY);
ALLOWED_IN_EXCEPTION.add(Events.STATUS);
ALLOWED_IN_EXCEPTION.add(Events.SELF_ATTENDEE_STATUS);
ALLOWED_IN_EXCEPTION.add(Events.SYNC_DATA6);
@@ -809,6 +843,12 @@
qb.appendWhere(SQL_WHERE_ID);
break;
+ case COLORS:
+ qb.setTables(Tables.COLORS);
+ qb.setProjectionMap(sColorsProjectionMap);
+ selection = appendAccountFromParameterToSelection(selection, uri);
+ break;
+
case CALENDARS:
case CALENDAR_ENTITIES:
qb.setTables(Tables.CALENDARS);
@@ -1655,6 +1695,23 @@
}
//DatabaseUtils.dumpCursor(cursor);
+ // If there's a color index check that it's valid
+ String color_index = modValues.getAsString(Events.EVENT_COLOR_KEY);
+ if (!TextUtils.isEmpty(color_index)) {
+ int calIdCol = cursor.getColumnIndex(Events.CALENDAR_ID);
+ Long calId = cursor.getLong(calIdCol);
+ String accountName = null;
+ String accountType = null;
+ if (calId != null) {
+ Account account = getAccount(calId);
+ if (account != null) {
+ accountName = account.name;
+ accountType = account.type;
+ }
+ }
+ verifyColorExists(accountName, accountType, color_index, Colors.TYPE_EVENT);
+ }
+
/*
* Verify that the original event is in fact a recurring event by checking for the
* presence of an RRULE. If it's there, we assume that the event is otherwise
@@ -1687,6 +1744,8 @@
// and drop in the new caller-supplied values. This will set originalInstanceTime.
ContentValues values = new ContentValues();
DatabaseUtils.cursorRowToContentValues(cursor, values);
+ cursor.close();
+ cursor = null;
// TODO: if we're changing this to an all-day event, we should ensure that
// hours/mins/secs on DTSTART are zeroed out (before computing DTEND).
@@ -1884,13 +1943,9 @@
* the Calendar. We're expecting to find one matching entry in Attendees.
*/
long calendarId = values.getAsLong(Events.CALENDAR_ID);
- cursor = mDb.query(Tables.CALENDARS, new String[] { Calendars.OWNER_ACCOUNT },
- SQL_WHERE_ID, new String[] { String.valueOf(calendarId) },
- null /* groupBy */, null /* having */, null /* sortOrder */);
- if (!cursor.moveToFirst()) {
- Log.w(TAG, "Can't get calendar account_name for calendar " + calendarId);
- } else {
- String accountName = cursor.getString(0);
+ String accountName = getOwner(calendarId);
+
+ if (accountName != null) {
ContentValues attValues = new ContentValues();
attValues.put(Attendees.ATTENDEE_STATUS,
modValues.getAsString(Events.SELF_ATTENDEE_STATUS));
@@ -1906,8 +1961,8 @@
if (count != 1 && count != 2) {
// We're only expecting one matching entry. We might briefly see
// two during a server sync.
- Log.e(TAG, "Attendee status update on event=" + newEventId +
- " name=" + accountName + " touched " + count + " rows");
+ Log.e(TAG, "Attendee status update on event=" + newEventId
+ + " touched " + count + " rows. Expected one or two rows.");
if (false) {
// This dumps PII in the log, don't ship with it enabled.
Cursor debugCursor = mDb.query(Tables.ATTENDEES, null,
@@ -1920,7 +1975,6 @@
throw new RuntimeException("Status update WTF");
}
}
- cursor.close();
}
} else {
/*
@@ -2010,10 +2064,29 @@
throw new RuntimeException("Could not insert event.");
// return null;
}
+ Long calendar_id = updatedValues.getAsLong(Events.CALENDAR_ID);
+ if (calendar_id == null) {
+ // validateEventData checks this for non-sync adapter
+ // inserts
+ throw new IllegalArgumentException("New events must specify a calendar id");
+ }
+ // Verify the color is valid if it is being set
+ String color_id = updatedValues.getAsString(Events.EVENT_COLOR_KEY);
+ if (!TextUtils.isEmpty(color_id)) {
+ Account account = getAccount(calendar_id);
+ String accountName = null;
+ String accountType = null;
+ if (account != null) {
+ accountName = account.name;
+ accountType = account.type;
+ }
+ int color = verifyColorExists(accountName, accountType, color_id,
+ Colors.TYPE_EVENT);
+ updatedValues.put(Events.EVENT_COLOR, color);
+ }
String owner = null;
- if (updatedValues.containsKey(Events.CALENDAR_ID) &&
- !updatedValues.containsKey(Events.ORGANIZER)) {
- owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID));
+ if (!updatedValues.containsKey(Events.ORGANIZER)) {
+ owner = getOwner(calendar_id);
// TODO: This isn't entirely correct. If a guest is adding a recurrence
// exception to an event, the organizer should stay the original organizer.
// This value doesn't go to the server and it will get fixed on sync,
@@ -2056,43 +2129,10 @@
if (values.containsKey(Events.SELF_ATTENDEE_STATUS)) {
int status = values.getAsInteger(Events.SELF_ATTENDEE_STATUS);
if (owner == null) {
- owner = getOwner(updatedValues.getAsLong(Events.CALENDAR_ID));
+ owner = getOwner(calendar_id);
}
createAttendeeEntry(id, status, owner);
}
- // if the Event Timezone is defined, store it as the original one in the
- // ExtendedProperties table
- if (values.containsKey(Events.EVENT_TIMEZONE) && !callerIsSyncAdapter) {
- String originalTimezone = values.getAsString(Events.EVENT_TIMEZONE);
-
- ContentValues expropsValues = new ContentValues();
- expropsValues.put(CalendarContract.ExtendedProperties.EVENT_ID, id);
- expropsValues.put(CalendarContract.ExtendedProperties.NAME,
- EXT_PROP_ORIGINAL_TIMEZONE);
- expropsValues.put(CalendarContract.ExtendedProperties.VALUE,
- originalTimezone);
-
- // Insert the extended property
- long exPropId = mDbHelper.extendedPropertiesInsert(expropsValues);
- if (exPropId == -1) {
- if (Log.isLoggable(TAG, Log.ERROR)) {
- Log.e(TAG, "Cannot add the original Timezone in the "
- + "ExtendedProperties table for Event: " + id);
- }
- } else {
- // Update the Event for saying it has some extended properties
- ContentValues eventValues = new ContentValues();
- eventValues.put(Events.HAS_EXTENDED_PROPERTIES, "1");
- int result = mDb.update("Events", eventValues, SQL_WHERE_ID,
- new String[] {String.valueOf(id)});
- if (result <= 0) {
- if (Log.isLoggable(TAG, Log.ERROR)) {
- Log.e(TAG, "Cannot update hasExtendedProperties column"
- + " for Event: " + id);
- }
- }
- }
- }
backfillExceptionOriginalIds(id, values);
@@ -2114,9 +2154,56 @@
String eventsUrl = values.getAsString(Calendars.CAL_SYNC1);
mDbHelper.scheduleSync(account, false /* two-way sync */, eventsUrl);
}
+ String cal_color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY);
+ if (!TextUtils.isEmpty(cal_color_id)) {
+ String accountName = values.getAsString(Calendars.ACCOUNT_NAME);
+ String accountType = values.getAsString(Calendars.ACCOUNT_TYPE);
+ int color = verifyColorExists(accountName, accountType, cal_color_id,
+ Colors.TYPE_CALENDAR);
+ values.put(Calendars.CALENDAR_COLOR, color);
+ }
id = mDbHelper.calendarsInsert(values);
sendUpdateNotification(id, callerIsSyncAdapter);
break;
+ case COLORS:
+ // verifyTransactionAllowed requires this be from a sync
+ // adapter, all of the required fields are marked NOT NULL in
+ // the db. TODO Do we need explicit checks here or should we
+ // just let sqlite throw if something isn't specified?
+ String accountName = uri.getQueryParameter(Colors.ACCOUNT_NAME);
+ String accountType = uri.getQueryParameter(Colors.ACCOUNT_TYPE);
+ String colorIndex = values.getAsString(Colors.COLOR_KEY);
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ throw new IllegalArgumentException("Account name and type must be non"
+ + " empty parameters for " + uri);
+ }
+ if (TextUtils.isEmpty(colorIndex)) {
+ throw new IllegalArgumentException("COLOR_INDEX must be non empty for " + uri);
+ }
+ if (!values.containsKey(Colors.COLOR_TYPE) || !values.containsKey(Colors.COLOR)) {
+ throw new IllegalArgumentException(
+ "New colors must contain COLOR_TYPE and COLOR");
+ }
+ // Make sure the account we're inserting for is the same one the
+ // adapter is claiming to be. TODO should we throw if they
+ // aren't the same?
+ values.put(Colors.ACCOUNT_NAME, accountName);
+ values.put(Colors.ACCOUNT_TYPE, accountType);
+
+ // Verify the color doesn't already exist
+ Cursor c = null;
+ try {
+ c = getColorByIndex(accountName, accountType, colorIndex);
+ if (c.getCount() != 0) {
+ throw new IllegalArgumentException(colorIndex
+ + " already exists for account and type provided");
+ }
+ } finally {
+ if (c != null)
+ c.close();
+ }
+ id = mDbHelper.colorsInsert(values);
+ break;
case ATTENDEES:
if (!values.containsKey(Attendees.EVENT_ID)) {
throw new IllegalArgumentException("Attendees values must "
@@ -2435,9 +2522,16 @@
return originalSyncId;
}
+ private Cursor getColorByIndex(String accountName, String accountType, String index) {
+ return mDb.query(Tables.COLORS, COLORS_PROJECTION, Colors.ACCOUNT_NAME + "=? AND "
+ + Colors.ACCOUNT_TYPE + "=? AND " + Colors.COLOR_KEY + "=?",
+ new String[] { accountName, accountType, index }, null, null, null);
+ }
+
/**
- * Gets the calendar's owner for an event.
- * @param calId
+ * Gets a calendar's "owner account", i.e. the e-mail address of the owner of the calendar.
+ *
+ * @param calId The calendar ID.
* @return email of owner or null
*/
private String getOwner(long calId) {
@@ -2471,6 +2565,29 @@
return emailAddress;
}
+ private Account getAccount(long calId) {
+ Account account = null;
+ Cursor cursor = null;
+ try {
+ cursor = query(ContentUris.withAppendedId(Calendars.CONTENT_URI, calId),
+ ACCOUNT_PROJECTION, null /* selection */, null /* selectionArgs */,
+ null /* sort */);
+ if (cursor == null || !cursor.moveToFirst()) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Couldn't find " + calId + " in Calendars table");
+ }
+ return null;
+ }
+ account = new Account(cursor.getString(ACCOUNT_NAME_INDEX),
+ cursor.getString(ACCOUNT_TYPE_INDEX));
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ return account;
+ }
+
/**
* Creates an entry in the Attendees table that refers to the given event
* and that has the given response status.
@@ -2812,6 +2929,10 @@
return mDbHelper.getSyncState().delete(mDb, selectionWithId,
selectionArgs);
+ case COLORS:
+ return deleteMatchingColors(appendAccountToSelection(uri, selection),
+ selectionArgs);
+
case EVENTS:
{
int result = 0;
@@ -3294,6 +3415,56 @@
return count;
}
+ private int deleteMatchingColors(String selection, String[] selectionArgs) {
+ // query to find all the colors that match, for each
+ // - verify no one references it
+ // - delete color
+ Cursor c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs, null,
+ null, null);
+ if (c == null) {
+ return 0;
+ }
+ try {
+ Cursor c2 = null;
+ while (c.moveToNext()) {
+ String index = c.getString(COLORS_COLOR_INDEX_INDEX);
+ String accountName = c.getString(COLORS_ACCOUNT_NAME_INDEX);
+ String accountType = c.getString(COLORS_ACCOUNT_TYPE_INDEX);
+ boolean isCalendarColor = c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR;
+ try {
+ if (isCalendarColor) {
+ c2 = mDb.query(Tables.CALENDARS, ID_ONLY_PROJECTION,
+ SQL_WHERE_CALENDAR_COLOR, new String[] {
+ accountName, accountType, index
+ }, null, null, null);
+ if (c2.getCount() != 0) {
+ throw new UnsupportedOperationException("Cannot delete color " + index
+ + ". Referenced by " + c2.getCount() + " calendars.");
+
+ }
+ } else {
+ c2 = query(Events.CONTENT_URI, ID_ONLY_PROJECTION, SQL_WHERE_EVENT_COLOR,
+ new String[] {accountName, accountType, index}, null);
+ if (c2.getCount() != 0) {
+ throw new UnsupportedOperationException("Cannot delete color " + index
+ + ". Referenced by " + c2.getCount() + " events.");
+
+ }
+ }
+ } finally {
+ if (c2 != null) {
+ c2.close();
+ }
+ }
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return mDb.delete(Tables.COLORS, selection, selectionArgs);
+ }
+
private int deleteMatchingCalendars(String selection, String[] selectionArgs) {
// query to find all the calendars that match, for each
// - delete calendar subscription
@@ -3355,6 +3526,40 @@
return true;
}
+ private int handleUpdateColors(ContentValues values, String selection, String[] selectionArgs) {
+ Cursor c = null;
+ int result = mDb.update(Tables.COLORS, values, selection, selectionArgs);
+ if (values.containsKey(Colors.COLOR)) {
+ try {
+ c = mDb.query(Tables.COLORS, COLORS_PROJECTION, selection, selectionArgs,
+ null /* groupBy */, null /* having */, null /* orderBy */);
+ while (c.moveToNext()) {
+ boolean calendarColor =
+ c.getInt(COLORS_COLOR_TYPE_INDEX) == Colors.TYPE_CALENDAR;
+ int color = c.getInt(COLORS_COLOR_INDEX);
+ String[] args = {
+ c.getString(COLORS_ACCOUNT_NAME_INDEX),
+ c.getString(COLORS_ACCOUNT_TYPE_INDEX),
+ c.getString(COLORS_COLOR_INDEX_INDEX)
+ };
+ ContentValues colorValue = new ContentValues();
+ if (calendarColor) {
+ colorValue.put(Calendars.CALENDAR_COLOR, color);
+ mDb.update(Tables.CALENDARS, values, SQL_WHERE_CALENDAR_COLOR, args);
+ } else {
+ colorValue.put(Events.EVENT_COLOR, color);
+ mDb.update(Tables.EVENTS, values, SQL_WHERE_EVENT_COLOR, args);
+ }
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+ return result;
+ }
+
/**
* Handles a request to update one or more events.
@@ -3419,6 +3624,26 @@
// Merge the modifications in.
values.putAll(modValues);
+ // If a color_index is being set make sure it's valid
+ String color_id = modValues.getAsString(Events.EVENT_COLOR_KEY);
+ if (!TextUtils.isEmpty(color_id)) {
+ String accountName = null;
+ String accountType = null;
+ Cursor c = mDb.query(Tables.CALENDARS, ACCOUNT_PROJECTION, SQL_WHERE_ID,
+ new String[] { values.getAsString(Events.CALENDAR_ID) }, null, null, null);
+ try {
+ if (c.moveToFirst()) {
+ accountName = c.getString(ACCOUNT_NAME_INDEX);
+ accountType = c.getString(ACCOUNT_TYPE_INDEX);
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ verifyColorExists(accountName, accountType, color_id, Colors.TYPE_EVENT);
+ }
+
// Scrub and/or validate the combined event.
if (callerIsSyncAdapter) {
scrubEventData(values, modValues);
@@ -3559,6 +3784,15 @@
return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs);
}
+ case COLORS:
+ Integer color = values.getAsInteger(Colors.COLOR);
+ if (values.size() > 1 || (values.size() == 1 && color == null)) {
+ throw new UnsupportedOperationException("You may only change the COLOR "
+ + "for an existing Colors entry.");
+ }
+ return handleUpdateColors(values, appendAccountToSelection(uri, selection),
+ selectionArgs);
+
case CALENDARS:
case CALENDARS_ID:
{
@@ -3588,6 +3822,19 @@
if (syncEvents != null) {
modifyCalendarSubscription(id, syncEvents == 1);
}
+ String color_id = values.getAsString(Calendars.CALENDAR_COLOR_KEY);
+ if (!TextUtils.isEmpty(color_id)) {
+ String accountName = values.getAsString(Calendars.ACCOUNT_NAME);
+ String accountType = values.getAsString(Calendars.ACCOUNT_TYPE);
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ Account account = getAccount(id);
+ if (account != null) {
+ accountName = account.name;
+ accountType = account.type;
+ }
+ }
+ verifyColorExists(accountName, accountType, color_id, Colors.TYPE_CALENDAR);
+ }
int result = mDb.update(Tables.CALENDARS, values, SQL_WHERE_ID,
new String[] {String.valueOf(id)});
@@ -3777,6 +4024,39 @@
}
}
+ /**
+ * Verifies that a color with the given index exists for the given Calendar
+ * entry.
+ *
+ * @param accountName The email of the account the color is for
+ * @param accountType The type of account the color is for
+ * @param color_index The color_index being set for the calendar
+ * @param color_type The type of color expected (Calendar/Event)
+ * @return The color specified by the index
+ */
+ private int verifyColorExists(String accountName, String accountType, String color_index,
+ int color_type) {
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) {
+ throw new IllegalArgumentException("Cannot set color. A valid account does"
+ + " not exist for this calendar.");
+ }
+ int color;
+ Cursor c = null;
+ try {
+ c = getColorByIndex(accountName, accountType, color_index);
+ if (!c.moveToFirst() || c.getInt(COLORS_COLOR_TYPE_INDEX) != color_type) {
+ throw new IllegalArgumentException(color_index
+ + " color does not exist for account or is the wrong type.");
+ }
+ color = c.getInt(COLORS_COLOR_INDEX);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return color;
+ }
+
private String appendAccountFromParameterToSelection(String selection, Uri uri) {
final String accountName = QueryParameterUtils.getQueryParameter(uri,
CalendarContract.EventsEntity.ACCOUNT_NAME);
@@ -3869,8 +4149,10 @@
}
if (type == TRANSACTION_UPDATE || type == TRANSACTION_DELETE) {
+ // TODO review this list, document in contract.
if (!TextUtils.isEmpty(selection)) {
// Only allow selections for the URIs that can reasonably use them.
+ // Whitelist of URIs allowed selections
switch (uriMatch) {
case SYNCSTATE:
case CALENDARS:
@@ -3880,12 +4162,14 @@
case REMINDERS:
case EXTENDED_PROPERTIES:
case PROVIDER_PROPERTIES:
+ case COLORS:
break;
default:
throw new IllegalArgumentException("Selection not permitted for " + uri);
}
} else {
// Disallow empty selections for some URIs.
+ // Blacklist of URIs _not_ allowed empty selections
switch (uriMatch) {
case EVENTS:
case ATTENDEES:
@@ -3900,9 +4184,16 @@
}
// Only the sync adapter can use these to make changes.
- if (uriMatch == SYNCSTATE || uriMatch == EXTENDED_PROPERTIES) {
- if (!isSyncAdapter) {
- throw new IllegalArgumentException("Only sync adapters may use " + uri);
+ if (!isSyncAdapter) {
+ switch (uriMatch) {
+ case SYNCSTATE:
+ case SYNCSTATE_ID:
+ case EXTENDED_PROPERTIES:
+ case EXTENDED_PROPERTIES_ID:
+ case COLORS:
+ throw new IllegalArgumentException("Only sync adapters may write using " + uri);
+ default:
+ break;
}
}
@@ -4042,7 +4333,8 @@
oldSyncEvents = (cursor.getInt(3) != 0);
}
} finally {
- cursor.close();
+ if (cursor != null)
+ cursor.close();
}
}
@@ -4184,9 +4476,11 @@
private static final int EXCEPTION_ID = 29;
private static final int EXCEPTION_ID2 = 30;
private static final int EMMA = 31;
+ private static final int COLORS = 32;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final HashMap<String, String> sInstancesProjectionMap;
+ private static final HashMap<String, String> sColorsProjectionMap;
protected static final HashMap<String, String> sEventsProjectionMap;
private static final HashMap<String, String> sEventEntitiesProjectionMap;
private static final HashMap<String, String> sAttendeesProjectionMap;
@@ -4233,11 +4527,21 @@
sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#", EXCEPTION_ID);
sUriMatcher.addURI(CalendarContract.AUTHORITY, "exception/#/#", EXCEPTION_ID2);
sUriMatcher.addURI(CalendarContract.AUTHORITY, "emma", EMMA);
+ sUriMatcher.addURI(CalendarContract.AUTHORITY, "colors", COLORS);
/** Contains just BaseColumns._COUNT */
sCountProjectionMap = new HashMap<String, String>();
sCountProjectionMap.put(BaseColumns._COUNT, "COUNT(*)");
+ sColorsProjectionMap = new HashMap<String, String>();
+ sColorsProjectionMap.put(Colors._ID, Colors._ID);
+ sColorsProjectionMap.put(Colors.DATA, Colors.DATA);
+ sColorsProjectionMap.put(Colors.ACCOUNT_NAME, Colors.ACCOUNT_NAME);
+ sColorsProjectionMap.put(Colors.ACCOUNT_TYPE, Colors.ACCOUNT_TYPE);
+ sColorsProjectionMap.put(Colors.COLOR_KEY, Colors.COLOR_KEY);
+ sColorsProjectionMap.put(Colors.COLOR_TYPE, Colors.COLOR_TYPE);
+ sColorsProjectionMap.put(Colors.COLOR, Colors.COLOR);
+
sEventsProjectionMap = new HashMap<String, String>();
// Events columns
sEventsProjectionMap.put(Events.ACCOUNT_NAME, Events.ACCOUNT_NAME);
@@ -4247,6 +4551,7 @@
sEventsProjectionMap.put(Events.DESCRIPTION, Events.DESCRIPTION);
sEventsProjectionMap.put(Events.STATUS, Events.STATUS);
sEventsProjectionMap.put(Events.EVENT_COLOR, Events.EVENT_COLOR);
+ sEventsProjectionMap.put(Events.EVENT_COLOR_KEY, Events.EVENT_COLOR_KEY);
sEventsProjectionMap.put(Events.SELF_ATTENDEE_STATUS, Events.SELF_ATTENDEE_STATUS);
sEventsProjectionMap.put(Events.DTSTART, Events.DTSTART);
sEventsProjectionMap.put(Events.DTEND, Events.DTEND);
@@ -4282,12 +4587,16 @@
// Calendar columns
sEventsProjectionMap.put(Calendars.CALENDAR_COLOR, Calendars.CALENDAR_COLOR);
+ sEventsProjectionMap.put(Calendars.CALENDAR_COLOR_KEY, Calendars.CALENDAR_COLOR_KEY);
sEventsProjectionMap.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CALENDAR_ACCESS_LEVEL);
sEventsProjectionMap.put(Calendars.VISIBLE, Calendars.VISIBLE);
sEventsProjectionMap.put(Calendars.CALENDAR_TIME_ZONE, Calendars.CALENDAR_TIME_ZONE);
sEventsProjectionMap.put(Calendars.OWNER_ACCOUNT, Calendars.OWNER_ACCOUNT);
sEventsProjectionMap.put(Calendars.CALENDAR_DISPLAY_NAME, Calendars.CALENDAR_DISPLAY_NAME);
sEventsProjectionMap.put(Calendars.ALLOWED_REMINDERS, Calendars.ALLOWED_REMINDERS);
+ sEventsProjectionMap
+ .put(Calendars.ALLOWED_ATTENDEE_TYPES, Calendars.ALLOWED_ATTENDEE_TYPES);
+ sEventsProjectionMap.put(Calendars.ALLOWED_AVAILABILITY, Calendars.ALLOWED_AVAILABILITY);
sEventsProjectionMap.put(Calendars.MAX_REMINDERS, Calendars.MAX_REMINDERS);
sEventsProjectionMap.put(Calendars.CAN_ORGANIZER_RESPOND, Calendars.CAN_ORGANIZER_RESPOND);
sEventsProjectionMap.put(Calendars.CAN_MODIFY_TIME_ZONE, Calendars.CAN_MODIFY_TIME_ZONE);
@@ -4475,21 +4784,20 @@
return;
}
- HashMap<Account, Boolean> accountHasCalendar = new HashMap<Account, Boolean>();
HashSet<Account> validAccounts = new HashSet<Account>();
for (Account account : accounts) {
validAccounts.add(new Account(account.name, account.type));
- accountHasCalendar.put(account, false);
}
ArrayList<Account> accountsToDelete = new ArrayList<Account>();
mDb.beginTransaction();
+ Cursor c = null;
try {
- for (String table : new String[]{Tables.CALENDARS}) {
+ for (String table : new String[]{Tables.CALENDARS, Tables.COLORS}) {
// Find all the accounts the calendar DB knows about, mark the ones that aren't
// in the valid set for deletion.
- Cursor c = mDb.rawQuery("SELECT DISTINCT " +
+ c = mDb.rawQuery("SELECT DISTINCT " +
Calendars.ACCOUNT_NAME +
"," +
Calendars.ACCOUNT_TYPE +
@@ -4510,6 +4818,7 @@
}
}
c.close();
+ c = null;
}
for (Account account : accountsToDelete) {
@@ -4518,10 +4827,15 @@
}
String[] params = new String[]{account.name, account.type};
mDb.execSQL(SQL_DELETE_FROM_CALENDARS, params);
+ // This will be a no-op for accounts without a color palette.
+ mDb.execSQL(SQL_DELETE_FROM_COLORS, params);
}
mDbHelper.getSyncState().onAccountsChanged(mDb, accounts);
mDb.setTransactionSuccessful();
} finally {
+ if (c != null) {
+ c.close();
+ }
mDb.endTransaction();
}
diff --git a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
index 608984b..bbd98ff 100644
--- a/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
+++ b/tests/src/com/android/providers/calendar/CalendarProvider2Test.java
@@ -16,8 +16,6 @@
package com.android.providers.calendar;
-import com.android.common.ArrayListCursor;
-
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -29,6 +27,7 @@
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
@@ -2424,7 +2423,7 @@
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
- return new ArrayListCursor(new String[]{}, new ArrayList<ArrayList>());
+ return new MatrixCursor(new String[]{ "_id" }, 0);
}
@Override