From 1ed192f1501908d613f0413c8b353b7f16ebd117 Mon Sep 17 00:00:00 2001
From: Jean-Luc Makiola
true to add a space in front of each word, false otherwise.
- *
- * @return This instance.
- */
- public NGramGenerator setAddSpaceInFront(boolean addSpace)
- {
- mAddSpaceInFront = addSpace;
- return this;
- }
-
-
- /**
- * Sets the {@link Locale} to use when converting the input string to lower case. This has no effect when {@link #setAllLowercase(boolean)} is called with
- * false.
- *
- * @param locale
- * The {@link Locale} to user for the conversion to lower case.
- *
- * @return This instance.
- */
- public NGramGenerator setLocale(Locale locale)
- {
- mLocale = locale;
- return this;
- }
-
-
- /**
- * Get all N-grams contained in the given String.
- *
- * @param data
- * The String to analyze.
- *
- * @return The {@link Set} containing the N-grams.
- */
- public Setnull.
- */
- public void fire(Context context, ContentValues values)
- {
- context.getContentResolver().update(uri(AuthorityUtil.taskAuthority(context)), values == null ? new ContentValues() : values, null, null);
- }
-
-
- /**
- * Run the operation on the given handler.
- *
- * @param context
- * A {@link Context}.
- * @param handler
- * A {@link Handler} to run the operation on.
- * @param uri
- * The {@link Uri} that triggered this operation.
- * @param db
- * The database.
- * @param values
- * The {@link ContentValues} that were supplied.
- */
- void run(final Context context, Handler handler, final Uri uri, final SQLiteDatabase db, final ContentValues values)
- {
- handler.post(new Runnable()
- {
- @Override
- public void run()
- {
- synchronized (mLock)
- {
- mHandler.handleOperation(context, uri, db, values);
- }
- }
- });
- }
-
-
- /**
- * Returns the {@link Uri} that triggers this {@link ContentOperation}.
- *
- * @param authority
- * The authority of this provide.
- *
- * @return A {@link Uri}.
- */
- private Uri uri(String authority)
- {
- return new Uri.Builder().scheme("content").authority(authority).path(BASE_PATH).appendPath(this.toString()).build();
- }
-
-
- /**
- * Register the operations with the given {@link UriMatcher}.
- *
- * @param uriMatcher
- * The {@link UriMatcher}.
- * @param authority
- * The authority of this TaskProvider.
- * @param firstID
- * Teh first Id to use for our Uris.
- */
- public static void register(UriMatcher uriMatcher, String authority, int firstID)
- {
- for (ContentOperation op : values())
- {
- Uri uri = op.uri(authority);
- uriMatcher.addURI(authority, uri.getPath().substring(1) /* remove leading slash */, firstID + op.ordinal());
- }
- }
-
-
- /**
- * Return a {@link ContentOperation} that belongs to the given id.
- *
- * @param id
- * The id or the {@link ContentOperation}.
- * @param firstId
- * The first ID to use for Uris.
- *
- * @return The respective {@link ContentOperation} or null if none was found.
- */
- public static ContentOperation get(int id, int firstId)
- {
- if (id < firstId)
- {
- return null;
- }
-
- if (id - firstId >= values().length)
- {
- return null;
- }
-
- return values()[id - firstId];
- }
-
-
- public interface OperationHandler
- {
- void handleOperation(Context context, Uri uri, SQLiteDatabase db, ContentValues values);
- }
-
-}
diff --git a/provider/src/main/java/org/dmfs/provider/tasks/FTSDatabaseHelper.java b/provider/src/main/java/org/dmfs/provider/tasks/FTSDatabaseHelper.java
deleted file mode 100644
index 1093a4f..0000000
--- a/provider/src/main/java/org/dmfs/provider/tasks/FTSDatabaseHelper.java
+++ /dev/null
@@ -1,630 +0,0 @@
-/*
- * Copyright 2017 dmfs GmbH
- *
- * 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 org.dmfs.provider.tasks;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.text.TextUtils;
-
-import org.dmfs.jems.iterable.decorators.Chunked;
-import org.dmfs.ngrams.NGramGenerator;
-import org.dmfs.provider.tasks.TaskDatabaseHelper.Tables;
-import org.dmfs.provider.tasks.model.TaskAdapter;
-import org.dmfs.tasks.contract.TaskContract;
-import org.dmfs.tasks.contract.TaskContract.Properties;
-import org.dmfs.tasks.contract.TaskContract.TaskColumns;
-import org.dmfs.tasks.contract.TaskContract.Tasks;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-
-/**
- * Supports the {@link TaskDatabaseHelper} in the matter of full-text-search.
- *
- * @author Tobias Reinsch null if the entry is not related to a property.
- */
- String PROPERTY_ID = "fts_property_id";
-
- /**
- * The the type of the searchable entry
- */
- String TYPE = "fts_type";
-
- /**
- * An n-gram for a task.
- */
- String NGRAM_ID = "fts_ngram_id";
-
- }
-
-
- /**
- * The columns of the N-gram table for the FTS search
- *
- * @author Tobias Reinsch TYPE column.
- *
- * @author Tobias Reinsch null as searchable text will remove the entry.
- *
- * @param db
- * The writable {@link SQLiteDatabase}.
- * @param taskId
- * the row id of the task this property belongs to.
- * @param propertyId
- * the id of the property
- * @param searchableText
- * the searchable text value of the property
- */
- public static void updatePropertyFTSEntry(SQLiteDatabase db, long taskId, long propertyId, String searchableText)
- {
- updateEntry(db, taskId, propertyId, SearchableTypes.PROPERTY, searchableText);
- }
-
-
- /**
- * Returns the IDs of each of the provided ngrams, creating them in th database if necessary.
- *
- * @param db
- * A writable {@link SQLiteDatabase}.
- * @param ngrams
- * The NGrams.
- *
- * @return The ids of the ngrams in the given set.
- */
- private static SetcontentType is not {@link SearchableTypes#PROPERTY}.
- * @param contentType
- * The {@link SearchableTypes} type.
- * @param ngramsIds
- * The set of ngrams ids which should be linked to the task
- *
- * @return The number of deleted relations.
- */
- private static Set- * This is the instances view as seen by the content provider clients. - */ - private final static String SQL_CREATE_INSTANCE_CLIENT_VIEW = "CREATE VIEW " + Tables.INSTANCE_CLIENT_VIEW + " AS SELECT " - + Tables.INSTANCES + ".*, " - // override task due, start and original times with the instance values - + Tables.INSTANCES + "." + TaskContract.Instances.INSTANCE_START + " as " + Tasks.DTSTART + ", " - + Tables.INSTANCES + "." + TaskContract.Instances.INSTANCE_DUE + " as " + Tasks.DUE + ", " - + Tables.INSTANCES + "." + TaskContract.Instances.INSTANCE_ORIGINAL_TIME + " as " + Tasks.ORIGINAL_INSTANCE_TIME + ", " - // override task duration with null, we already have a due - + "null as " + Tasks.DURATION + ", " - // override recurrence values with null, instances themselves are not recurring - + "null as " + Tasks.RRULE + ", " - + "null as " + Tasks.RDATE + ", " - + "null as " + Tasks.EXDATE + ", " - // this instance is part of a recurring task if either it has recurrence values or overrides an instance - + "not (" + Tasks.RRULE + " is null and " + Tasks.RDATE + " is null and " + Tasks.ORIGINAL_INSTANCE_ID + " is null and " + Tasks.ORIGINAL_INSTANCE_SYNC_ID + " is null) as " + TaskContract.Instances.IS_RECURRING + ", " - + Tables.TASKS + ".*, " - + Tables.LISTS + "." + Tasks.ACCOUNT_NAME + ", " - + Tables.LISTS + "." + Tasks.ACCOUNT_TYPE + ", " - + Tables.LISTS + "." + Tasks.LIST_OWNER + ", " - + Tables.LISTS + "." + Tasks.LIST_NAME + ", " - + Tables.LISTS + "." + Tasks.LIST_ACCESS_LEVEL + ", " - + Tables.LISTS + "." + Tasks.LIST_COLOR + ", " - + Tables.LISTS + "." + Tasks.VISIBLE - + " FROM " + Tables.TASKS - + " JOIN " + Tables.LISTS + " ON (" + Tables.TASKS + "." + TaskContract.Tasks.LIST_ID + "=" + Tables.LISTS + "." + TaskContract.TaskLists._ID + ")" - + " JOIN " + Tables.INSTANCES + " ON (" + Tables.TASKS + "." + TaskContract.Tasks._ID + "=" + Tables.INSTANCES + "." + TaskContract.Instances.TASK_ID + ");"; - - /** - * SQL command to create a view that combines task instances view with the belonging properties. - */ - private final static String SQL_CREATE_INSTANCE_PROPERTY_VIEW = "CREATE VIEW " + Tables.INSTANCE_PROPERTY_VIEW + " AS SELECT " - + Tables.INSTANCES + ".*, " - + Tables.PROPERTIES + ".*, " - + Tables.TASKS + ".*, " - + Tables.LISTS + "." + Tasks.ACCOUNT_NAME + ", " - + Tables.LISTS + "." + Tasks.ACCOUNT_TYPE + ", " - + Tables.LISTS + "." + Tasks.LIST_OWNER + ", " - + Tables.LISTS + "." + Tasks.LIST_NAME + ", " - + Tables.LISTS + "." + Tasks.LIST_ACCESS_LEVEL + ", " - + Tables.LISTS + "." + Tasks.LIST_COLOR + ", " - + Tables.LISTS + "." + Tasks.VISIBLE - + " FROM " + Tables.TASKS - + " JOIN " + Tables.LISTS + " ON (" + Tables.TASKS + "." + TaskContract.Tasks.LIST_ID + "=" + Tables.LISTS + "." + TaskContract.Tasks._ID + ")" - + " JOIN " + Tables.INSTANCES + " ON (" + Tables.TASKS + "." + TaskContract.Tasks._ID + "=" + Tables.INSTANCES + "." + TaskContract.Instances.TASK_ID + ")" - + " LEFT JOIN " + Tables.PROPERTIES + " ON (" + Tables.TASKS + "." + Tasks._ID + "=" + Tables.PROPERTIES + "." + Properties.TASK_ID + ");"; - - /** - * SQL command to create a view that combines task instances with some data from the list they belong to. - */ - private final static String SQL_CREATE_INSTANCE_CATEGORY_VIEW = "CREATE VIEW " + Tables.INSTANCE_CATEGORY_VIEW + " AS SELECT " - + Tables.INSTANCES + ".*, " - + Tables.CATEGORIES_MAPPING + "." + CategoriesMapping.CATEGORY_ID + ", " - + Tables.TASKS + ".*, " - + Tables.LISTS + "." + Tasks.ACCOUNT_NAME + ", " - + Tables.LISTS + "." + Tasks.ACCOUNT_TYPE + ", " - + Tables.LISTS + "." + Tasks.LIST_OWNER + ", " - + Tables.LISTS + "." + Tasks.LIST_NAME + ", " - + Tables.LISTS + "." + Tasks.LIST_ACCESS_LEVEL + ", " - + Tables.LISTS + "." + Tasks.LIST_COLOR + ", " - + Tables.LISTS + "." + Tasks.VISIBLE - + " FROM " + Tables.TASKS - + " JOIN " + Tables.LISTS + " ON (" + Tables.TASKS + "." + TaskContract.Tasks.LIST_ID + "=" + Tables.LISTS + "." + TaskContract.Tasks._ID + ")" - + " JOIN " + Tables.INSTANCES + " ON (" + Tables.TASKS + "." + TaskContract.Tasks._ID + "=" + Tables.INSTANCES + "." + TaskContract.Instances.TASK_ID + ")" - + " LEFT JOIN " + Tables.CATEGORIES_MAPPING + " ON (" + Tables.CATEGORIES_MAPPING + "." + CategoriesMapping.TASK_ID + "=" + Tables.INSTANCES + "." + TaskContract.Instances.TASK_ID + ");"; - - /** - * SQL command to drop the instance view. - */ - private final static String SQL_DROP_INSTANCE_VIEW = "DROP VIEW " + Tables.INSTANCE_VIEW + ";"; - - /** - * SQL command to drop the instance property view. - */ - //private final static String SQL_DROP_INSTANCE_PROPERTY_VIEW = "DROP VIEW " + Tables.INSTANCE_PROPERTY_VIEW + ";"; - - /** - * SQL command to create the instances table. - */ - private final static String SQL_CREATE_SYNCSTATE_TABLE = - "CREATE TABLE " + Tables.SYNCSTATE + " ( " + - TaskContract.SyncState._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + TaskContract.SyncState.ACCOUNT_NAME + " TEXT, " - + TaskContract.SyncState.ACCOUNT_TYPE + " TEXT, " - + TaskContract.SyncState.DATA + " TEXT " - + ");"; - - /** - * SQL command to create the instances table. - */ - private final static String SQL_CREATE_INSTANCES_TABLE = - "CREATE TABLE " + Tables.INSTANCES + " ( " + - TaskContract.Instances._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + TaskContract.Instances.TASK_ID + " INTEGER NOT NULL, " // NOT NULL - + TaskContract.Instances.INSTANCE_START + " INTEGER, " - + TaskContract.Instances.INSTANCE_DUE + " INTEGER, " - + TaskContract.Instances.INSTANCE_START_SORTING + " INTEGER, " - + TaskContract.Instances.INSTANCE_DUE_SORTING + " INTEGER, " - + TaskContract.Instances.INSTANCE_DURATION + " INTEGER, " - + TaskContract.Instances.INSTANCE_ORIGINAL_TIME + " INTEGER DEFAULT 0, " - + TaskContract.Instances.DISTANCE_FROM_CURRENT + " INTEGER DEFAULT 0);"; - - /** - * SQL command to create a trigger to clean up data of removed tasks. - */ - private final static String SQL_CREATE_TASKS_CLEANUP_TRIGGER = - "CREATE TRIGGER task_cleanup_trigger AFTER DELETE ON " + Tables.TASKS - + " BEGIN " - + " DELETE FROM " + Tables.PROPERTIES + " WHERE " + TaskContract.Properties.TASK_ID + "= old." + TaskContract.Tasks._ID + ";" - + " DELETE FROM " + Tables.INSTANCES + " WHERE " + TaskContract.Instances.TASK_ID + "=old." + TaskContract.Tasks._ID + ";" - + " END;"; - - /** - * SQL command to create a trigger to clean up data of removed lists. - */ - private final static String SQL_CREATE_LISTS_CLEANUP_TRIGGER = - "CREATE TRIGGER list_cleanup_trigger AFTER DELETE ON " + Tables.LISTS - + " BEGIN " - + " DELETE FROM " + Tables.TASKS + " WHERE " + Tasks.LIST_ID + "= old." + TaskLists._ID + ";" - + " END;"; - - /** - * SQL command to drop the clean up trigger. - */ - private final static String SQL_DROP_TASKS_CLEANUP_TRIGGER = - "DROP TRIGGER task_cleanup_trigger;"; - - /** - * SQL command that counts and sets the alarm on deletion - */ - private final static String SQL_COUNT_ALARMS_ON_DELETE = - " BEGIN UPDATE " + Tables.TASKS + " SET " + Tasks.HAS_ALARMS - + " = (SELECT COUNT (*) FROM " + Tables.PROPERTIES - + " WHERE " + Properties.MIMETYPE + " = '" + Alarm.CONTENT_ITEM_TYPE + "' AND " + Alarm.ALARM_TYPE + " <> " + Alarm.ALARM_TYPE_NOTHING + " AND " + Properties.TASK_ID + " = OLD." + Properties.TASK_ID - + ") WHERE " + Tasks._ID + " = OLD." + Properties.TASK_ID - + "; END;"; - - /** - * SQL command that counts and sets the alarm on insert and update - */ - private final static String SQL_COUNT_ALARMS = - " BEGIN UPDATE " + Tables.TASKS + " SET " + Tasks.HAS_ALARMS - + " = (SELECT COUNT (*) FROM " + Tables.PROPERTIES - + " WHERE " + Properties.MIMETYPE + " = '" + Alarm.CONTENT_ITEM_TYPE + "' AND " + Alarm.ALARM_TYPE + " <> " + Alarm.ALARM_TYPE_NOTHING + " AND " + Properties.TASK_ID + " = NEW." + Properties.TASK_ID - + ") WHERE " + Tasks._ID + " = NEW." + Properties.TASK_ID - + "; END;"; - - /** - * SQL command to create a trigger that counts the alarms for a task on create - */ - private final static String SQL_CREATE_ALARM_COUNT_CREATE_TRIGGER = - "CREATE TRIGGER alarm_count_create_trigger AFTER INSERT ON " + Tables.PROPERTIES + " WHEN NEW." + Properties.MIMETYPE + " = '" + Alarm.CONTENT_ITEM_TYPE + "'" - + SQL_COUNT_ALARMS; - - /** - * SQL command to create a trigger that counts the alarms for a task on update - */ - private final static String SQL_CREATE_ALARM_COUNT_UPDATE_TRIGGER = - "CREATE TRIGGER alarm_count_update_trigger AFTER UPDATE ON " + Tables.PROPERTIES + " WHEN NEW." + Properties.MIMETYPE + " = '" + Alarm.CONTENT_ITEM_TYPE + "'" - + SQL_COUNT_ALARMS; - - /** - * SQL command to create a trigger that counts the alarms for a task on delete - */ - private final static String SQL_CREATE_ALARM_COUNT_DELETE_TRIGGER = - "CREATE TRIGGER alarm_count_delete_trigger AFTER DELETE ON " + Tables.PROPERTIES + " WHEN OLD." + Properties.MIMETYPE + " = '" + Alarm.CONTENT_ITEM_TYPE + "'" - + SQL_COUNT_ALARMS_ON_DELETE; - - /** - * SQL command to create a trigger to clean up data of removed property. - */ - private final static String SQL_CREATE_ALARM_PROPERTY_CLEANUP_TRIGGER = - "CREATE TRIGGER alarm_property_cleanup_trigger AFTER DELETE ON " + Tables.PROPERTIES + " WHEN OLD." + Properties.MIMETYPE + " = '" + Alarm.CONTENT_ITEM_TYPE + "'" - + " BEGIN " - + " DELETE FROM " + Tables.ALARMS + " WHERE " + TaskContract.Alarms.ALARM_ID + "= OLD." + TaskContract.Properties.PROPERTY_ID + ";" - + " END;"; - - /** - * SQL command to create a trigger to clean up data of removed property. - */ - private final static String SQL_CREATE_CATEGORY_PROPERTY_CLEANUP_TRIGGER = - "CREATE TRIGGER category_property_cleanup_trigger AFTER DELETE ON " + Tables.PROPERTIES + " WHEN OLD." + Properties.MIMETYPE + " = '" + Category.CONTENT_ITEM_TYPE + "'" - + " BEGIN " - + " DELETE FROM " + Tables.CATEGORIES_MAPPING + " WHERE " + CategoriesMapping.PROPERTY_ID + "= OLD." + TaskContract.Properties.PROPERTY_ID + ";" - + " END;"; - - /** - * SQL command to create a trigger to clean up property data of removed task. - */ - private final static String SQL_CREATE_TASK_PROPERTY_CLEANUP_TRIGGER = - "CREATE TRIGGER task_property_cleanup_trigger AFTER DELETE ON " + Tables.TASKS + " BEGIN " - + " DELETE FROM " + Tables.PROPERTIES + " WHERE " + Properties.TASK_ID + "= OLD." + Tasks._ID + ";" - + " END;"; - - /** - * SQL command to create a trigger to increment task version number on every update. - */ - private final static String SQL_CREATE_TASK_VERSION_TRIGGER = - "CREATE TRIGGER task_version_trigger BEFORE UPDATE ON " + Tables.TASKS + " BEGIN " - + " UPDATE " + Tables.TASKS + " SET " + Tasks.VERSION + " = OLD." + Tasks.VERSION + " + 1 where " + Tasks._ID + " = NEW." + Tasks._ID + ";" - + " END;"; - - /** - * SQL command to create the task list table. - */ - private final static String SQL_CREATE_LISTS_TABLE = - "CREATE TABLE " + Tables.LISTS + " ( " - + TaskContract.TaskLists._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + TaskContract.TaskLists.ACCOUNT_NAME + " TEXT," - + TaskContract.TaskLists.ACCOUNT_TYPE + " TEXT," - + TaskContract.TaskLists.LIST_NAME + " TEXT," - + TaskContract.TaskLists.LIST_COLOR + " INTEGER," - + TaskContract.TaskLists.ACCESS_LEVEL + " INTEGER," - + TaskContract.TaskLists.VISIBLE + " INTEGER," - + TaskContract.TaskLists.SYNC_ENABLED + " INTEGER," - + TaskContract.TaskLists.OWNER + " TEXT," - + TaskContract.TaskLists._DIRTY + " INTEGER DEFAULT 0," - + TaskContract.TaskLists._SYNC_ID + " TEXT," - + TaskContract.TaskLists.SYNC_VERSION + " TEXT," - + TaskContract.TaskLists.SYNC1 + " TEXT," - + TaskContract.TaskLists.SYNC2 + " TEXT," - + TaskContract.TaskLists.SYNC3 + " TEXT," - + TaskContract.TaskLists.SYNC4 + " TEXT," - + TaskContract.TaskLists.SYNC5 + " TEXT," - + TaskContract.TaskLists.SYNC6 + " TEXT," - + TaskContract.TaskLists.SYNC7 + " TEXT," - + TaskContract.TaskLists.SYNC8 + " TEXT);"; - - /** - * SQL command to create the task table. - */ - private final static String SQL_CREATE_TASKS_TABLE = - "CREATE TABLE " + Tables.TASKS + " ( " - + TaskContract.Tasks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + TaskContract.Tasks.VERSION + " INTEGER DEFAULT 0," - + TaskContract.Tasks.LIST_ID + " INTEGER NOT NULL, " - + TaskContract.Tasks.TITLE + " TEXT," - + TaskContract.Tasks.LOCATION + " TEXT," - + TaskContract.Tasks.GEO + " TEXT," - + TaskContract.Tasks.DESCRIPTION + " TEXT," - + TaskContract.Tasks.URL + " TEXT," - + TaskContract.Tasks.ORGANIZER + " TEXT," - + TaskContract.Tasks.PRIORITY + " INTEGER, " - + TaskContract.Tasks.TASK_COLOR + " INTEGER," - + TaskContract.Tasks.CLASSIFICATION + " INTEGER," - + TaskContract.Tasks.COMPLETED + " INTEGER," - + TaskContract.Tasks.COMPLETED_IS_ALLDAY + " INTEGER," - + TaskContract.Tasks.PERCENT_COMPLETE + " INTEGER," - + TaskContract.Tasks.STATUS + " INTEGER DEFAULT " + TaskContract.Tasks.STATUS_DEFAULT + "," - + TaskContract.Tasks.IS_NEW + " INTEGER," - + TaskContract.Tasks.IS_CLOSED + " INTEGER," - + TaskContract.Tasks.DTSTART + " INTEGER," - + TaskContract.Tasks.CREATED + " INTEGER," - + TaskContract.Tasks.LAST_MODIFIED + " INTEGER," - + TaskContract.Tasks.IS_ALLDAY + " INTEGER," - + TaskContract.Tasks.TZ + " TEXT," - + TaskContract.Tasks.DUE + " INTEGER," - + TaskContract.Tasks.DURATION + " TEXT," - + TaskContract.Tasks.RDATE + " TEXT," - + TaskContract.Tasks.EXDATE + " TEXT," - + TaskContract.Tasks.RRULE + " TEXT," - + TaskContract.Tasks.PARENT_ID + " INTEGER," - + TaskContract.Tasks.SORTING + " TEXT," - + TaskContract.Tasks.HAS_ALARMS + " INTEGER," - + TaskContract.Tasks.HAS_PROPERTIES + " INTEGER," - + TaskContract.Tasks.PINNED + " INTEGER," - + TaskContract.Tasks.ORIGINAL_INSTANCE_SYNC_ID + " TEXT," - + TaskContract.Tasks.ORIGINAL_INSTANCE_ID + " INTEGER," - + TaskContract.Tasks.ORIGINAL_INSTANCE_TIME + " INTEGER," - + TaskContract.Tasks.ORIGINAL_INSTANCE_ALLDAY + " INTEGER," - + TaskContract.Tasks._DIRTY + " INTEGER DEFAULT 1," // a new task is always dirty - + TaskContract.Tasks._DELETED + " INTEGER DEFAULT 0," // new tasks are not deleted by default - + TaskContract.Tasks._SYNC_ID + " TEXT," - + TaskContract.Tasks._UID + " TEXT," - + TaskContract.Tasks.SYNC_VERSION + " TEXT," - + TaskContract.Tasks.SYNC1 + " TEXT," - + TaskContract.Tasks.SYNC2 + " TEXT," - + TaskContract.Tasks.SYNC3 + " TEXT," - + TaskContract.Tasks.SYNC4 + " TEXT," - + TaskContract.Tasks.SYNC5 + " TEXT," - + TaskContract.Tasks.SYNC6 + " TEXT," - + TaskContract.Tasks.SYNC7 + " TEXT," - + TaskContract.Tasks.SYNC8 + " TEXT);"; - - /** - * SQL command to create the categories table. - */ - private final static String SQL_CREATE_CATEGORIES_TABLE = - "CREATE TABLE " + Tables.CATEGORIES - + " ( " + TaskContract.Categories._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + TaskContract.Categories.ACCOUNT_NAME + " TEXT," - + TaskContract.Categories.ACCOUNT_TYPE + " TEXT," - + TaskContract.Categories.NAME + " TEXT," - + TaskContract.Categories.COLOR + " INTEGER);"; - - /** - * SQL command to create the categories table. - */ - private final static String SQL_CREATE_CATEGORIES_MAPPING_TABLE = - "CREATE TABLE " + Tables.CATEGORIES_MAPPING - + " ( " + CategoriesMapping.TASK_ID + " INTEGER," - + CategoriesMapping.CATEGORY_ID + " INTEGER," - + CategoriesMapping.PROPERTY_ID + " INTEGER," - + "FOREIGN KEY (" + CategoriesMapping.TASK_ID + ") REFERENCES " + Tables.TASKS + "(" + TaskContract.Tasks._ID + ")," - + "FOREIGN KEY (" + CategoriesMapping.PROPERTY_ID + ") REFERENCES " + Tables.PROPERTIES + "(" + TaskContract.Properties.PROPERTY_ID + ")," - + "FOREIGN KEY (" + CategoriesMapping.CATEGORY_ID + ") REFERENCES " + Tables.CATEGORIES + "(" + TaskContract.Categories._ID + "));"; - - /** - * SQL command to create the alarms table the stores the already triggered alarms. - */ - private final static String SQL_CREATE_ALARMS_TABLE = - "CREATE TABLE " + Tables.ALARMS - + " ( " + TaskContract.Alarms.ALARM_ID + " INTEGER," - + TaskContract.Alarms.LAST_TRIGGER + " TEXT," - + TaskContract.Alarms.NEXT_TRIGGER + " TEXT);"; - - /** - * SQL command to create the table for extended properties. - */ - private final static String SQL_CREATE_PROPERTIES_TABLE = - "CREATE TABLE " + Tables.PROPERTIES + " ( " - + TaskContract.Properties.PROPERTY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," - + TaskContract.Properties.TASK_ID + " INTEGER," - + TaskContract.Properties.MIMETYPE + " INTEGER," - + TaskContract.Properties.VERSION + " INTEGER," - + TaskContract.Properties.DATA0 + " TEXT," - + TaskContract.Properties.DATA1 + " TEXT," - + TaskContract.Properties.DATA2 + " TEXT," - + TaskContract.Properties.DATA3 + " TEXT," - + TaskContract.Properties.DATA4 + " TEXT," - + TaskContract.Properties.DATA5 + " TEXT," - + TaskContract.Properties.DATA6 + " TEXT," - + TaskContract.Properties.DATA7 + " TEXT," - + TaskContract.Properties.DATA8 + " TEXT," - + TaskContract.Properties.DATA9 + " TEXT," - + TaskContract.Properties.DATA10 + " TEXT," - + TaskContract.Properties.DATA11 + " TEXT," - + TaskContract.Properties.DATA12 + " TEXT," - + TaskContract.Properties.DATA13 + " TEXT," - + TaskContract.Properties.DATA14 + " TEXT," - + TaskContract.Properties.DATA15 + " TEXT," - + TaskContract.Properties.SYNC1 + " TEXT," - + TaskContract.Properties.SYNC2 + " TEXT," - + TaskContract.Properties.SYNC3 + " TEXT," - + TaskContract.Properties.SYNC4 + " TEXT," - + TaskContract.Properties.SYNC5 + " TEXT," - + TaskContract.Properties.SYNC6 + " TEXT," - + TaskContract.Properties.SYNC7 + " TEXT," - + TaskContract.Properties.SYNC8 + " TEXT);"; - - /** - * SQL command to drop the task view. - */ - private final static String SQL_DROP_PROPERTIES_TABLE = "DROP TABLE " + Tables.PROPERTIES + ";"; - - - /** - * Builds a string that creates an index on the given table for the given columns. - * - * @param table - * The table to create the index on. - * @param fields - * The fields to index. - * - * @return An SQL command string. - */ - public static String createIndexString(String table, boolean unique, String... fields) - { - if (fields == null || fields.length < 1) - { - throw new IllegalArgumentException("need at least one field to build an index!"); - } - - StringBuffer buffer = new StringBuffer(); - - // Index name is constructed like this: tablename_fields[0]_idx - buffer.append("CREATE "); - if (unique) - { - buffer.append(" UNIQUE "); - } - buffer.append("INDEX IF NOT EXISTS "); - buffer.append(table).append("_").append(fields[0]).append("_idx ON "); - buffer.append(table).append(" ("); - buffer.append(fields[0]); - for (int i = 1; i < fields.length; i++) - { - buffer.append(", ").append(fields[i]); - } - buffer.append(");"); - - return buffer.toString(); - - } - - - private final OnDatabaseOperationListener mListener; - - - TaskDatabaseHelper(Context context, OnDatabaseOperationListener listener) - { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - mListener = listener; - } - - - /** - * Creates the tables, views, triggers and indices. - *
- * TODO: move all strings to separate final static variables.
- */
- @Override
- public void onCreate(SQLiteDatabase db)
- {
-
- // create task list table
- db.execSQL(SQL_CREATE_LISTS_TABLE);
-
- // trigger that removes tasks of a list that has been removed
- db.execSQL("CREATE TRIGGER task_list_cleanup_trigger AFTER DELETE ON " + Tables.LISTS + " BEGIN DELETE FROM " + Tables.TASKS + " WHERE "
- + TaskContract.Tasks.LIST_ID + "= old." + TaskContract.TaskLists._ID + "; END");
-
- // create task table
- db.execSQL(SQL_CREATE_TASKS_TABLE);
-
- // trigger that marks a list as dirty if a task in that list gets marked as dirty or deleted
- db.execSQL("CREATE TRIGGER task_list_make_dirty_on_update AFTER UPDATE ON " + Tables.TASKS + " BEGIN UPDATE " + Tables.LISTS + " SET "
- + TaskContract.TaskLists._DIRTY + "=" + TaskContract.TaskLists._DIRTY + " + " + "new." + TaskContract.Tasks._DIRTY + " + " + "new."
- + TaskContract.Tasks._DELETED + " WHERE " + TaskContract.TaskLists._ID + "= new." + TaskContract.Tasks.LIST_ID + "; END");
-
- // trigger that marks a list as dirty if a task in that list gets marked as dirty or deleted
- db.execSQL("CREATE TRIGGER task_list_make_dirty_on_insert AFTER INSERT ON " + Tables.TASKS + " BEGIN UPDATE " + Tables.LISTS + " SET "
- + TaskContract.TaskLists._DIRTY + "=" + TaskContract.TaskLists._DIRTY + " + " + "new." + TaskContract.Tasks._DIRTY + " + " + "new."
- + TaskContract.Tasks._DELETED + " WHERE " + TaskContract.TaskLists._ID + "= new." + TaskContract.Tasks.LIST_ID + "; END");
-
- // create task version update trigger
- db.execSQL(SQL_CREATE_TASK_VERSION_TRIGGER);
-
- // create instances table and view
- db.execSQL(SQL_CREATE_INSTANCES_TABLE);
-
- // create categories table
- db.execSQL(SQL_CREATE_CATEGORIES_TABLE);
-
- // create categories mapping table
- db.execSQL(SQL_CREATE_CATEGORIES_MAPPING_TABLE);
-
- // create alarms table
- db.execSQL(SQL_CREATE_ALARMS_TABLE);
-
- // create properties table
- db.execSQL(SQL_CREATE_PROPERTIES_TABLE);
-
- // create syncstate table
- db.execSQL(SQL_CREATE_SYNCSTATE_TABLE);
-
- // create views
- db.execSQL(SQL_CREATE_TASK_VIEW);
- db.execSQL(SQL_CREATE_TASK_PROPERTY_VIEW);
- db.execSQL(SQL_CREATE_INSTANCE_VIEW);
- db.execSQL(SQL_CREATE_INSTANCE_CLIENT_VIEW);
- db.execSQL(SQL_CREATE_INSTANCE_PROPERTY_VIEW);
- db.execSQL(SQL_CREATE_INSTANCE_CATEGORY_VIEW);
-
- // create indices
- db.execSQL(createIndexString(Tables.INSTANCES, false, TaskContract.Instances.TASK_ID, TaskContract.Instances.INSTANCE_START,
- TaskContract.Instances.INSTANCE_DUE));
- db.execSQL(createIndexString(Tables.INSTANCES, false, TaskContract.Instances.INSTANCE_START_SORTING));
- db.execSQL(createIndexString(Tables.INSTANCES, false, TaskContract.Instances.INSTANCE_DUE_SORTING));
- db.execSQL(createIndexString(Tables.INSTANCES, false, TaskContract.Instances.INSTANCE_ORIGINAL_TIME));
- db.execSQL(createIndexString(Tables.LISTS, false, TaskContract.TaskLists.ACCOUNT_NAME, // not sure if necessary
- TaskContract.TaskLists.ACCOUNT_TYPE));
- db.execSQL(createIndexString(Tables.TASKS, false, TaskContract.Tasks.STATUS, TaskContract.Tasks.LIST_ID, TaskContract.Tasks._SYNC_ID));
- db.execSQL(createIndexString(Tables.PROPERTIES, false, TaskContract.Properties.MIMETYPE, TaskContract.Properties.TASK_ID));
- db.execSQL(createIndexString(Tables.PROPERTIES, false, TaskContract.Properties.TASK_ID));
- db.execSQL(createIndexString(Tables.CATEGORIES, false, TaskContract.Categories.ACCOUNT_NAME, TaskContract.Categories.ACCOUNT_TYPE,
- TaskContract.Categories.NAME));
- db.execSQL(createIndexString(Tables.CATEGORIES, false, TaskContract.Categories.NAME));
- db.execSQL(createIndexString(Tables.SYNCSTATE, true, TaskContract.SyncState.ACCOUNT_NAME, TaskContract.SyncState.ACCOUNT_TYPE));
-
- // trigger that removes properties of a task that has been removed
- db.execSQL(SQL_CREATE_TASKS_CLEANUP_TRIGGER);
-
- // trigger that removes alarms when an alarm property was deleted
- db.execSQL(SQL_CREATE_ALARM_PROPERTY_CLEANUP_TRIGGER);
-
- // trigger that removes tasks when a list was removed
- db.execSQL(SQL_CREATE_LISTS_CLEANUP_TRIGGER);
-
- // trigger that counts the alarms for tasks
- db.execSQL(SQL_CREATE_ALARM_COUNT_CREATE_TRIGGER);
- db.execSQL(SQL_CREATE_ALARM_COUNT_UPDATE_TRIGGER);
- db.execSQL(SQL_CREATE_ALARM_COUNT_DELETE_TRIGGER);
-
- // add cleanup trigger for orphaned properties
- db.execSQL(SQL_CREATE_TASK_PROPERTY_CLEANUP_TRIGGER);
-
- // initialize FTS
- FTSDatabaseHelper.onCreate(db);
-
- if (mListener != null)
- {
- mListener.onDatabaseCreated(db);
- }
- }
-
-
- /**
- * Manages the database schema migration.
- */
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
- {
- Log.i(TAG, "updgrading db from " + oldVersion + " to " + newVersion);
- if (oldVersion < 2)
- {
- // add IS_NEW and IS_CLOSED columns and update their values
- db.execSQL("ALTER TABLE " + Tables.TASKS + " ADD COLUMN " + TaskContract.Tasks.IS_NEW + " INTEGER");
- db.execSQL("ALTER TABLE " + Tables.TASKS + " ADD COLUMN " + TaskContract.Tasks.IS_CLOSED + " INTEGER");
- db.execSQL("UPDATE " + Tables.TASKS + " SET " + TaskContract.Tasks.IS_NEW + " = 1 WHERE " + TaskContract.Tasks.STATUS + " = "
- + TaskContract.Tasks.STATUS_NEEDS_ACTION);
- db.execSQL("UPDATE " + Tables.TASKS + " SET " + TaskContract.Tasks.IS_NEW + " = 0 WHERE " + TaskContract.Tasks.STATUS + " != "
- + TaskContract.Tasks.STATUS_NEEDS_ACTION);
- db.execSQL("UPDATE " + Tables.TASKS + " SET " + TaskContract.Tasks.IS_CLOSED + " = 1 WHERE " + TaskContract.Tasks.STATUS + " > "
- + TaskContract.Tasks.STATUS_IN_PROCESS);
- db.execSQL("UPDATE " + Tables.TASKS + " SET " + TaskContract.Tasks.IS_CLOSED + " = 0 WHERE " + TaskContract.Tasks.STATUS + " <= "
- + TaskContract.Tasks.STATUS_IN_PROCESS);
- }
-
- if (oldVersion < 3)
- {
- // add instance sortings
- db.execSQL("ALTER TABLE " + Tables.INSTANCES + " ADD COLUMN " + TaskContract.Instances.INSTANCE_START_SORTING + " INTEGER");
- db.execSQL("ALTER TABLE " + Tables.INSTANCES + " ADD COLUMN " + TaskContract.Instances.INSTANCE_DUE_SORTING + " INTEGER");
- db.execSQL("UPDATE " + Tables.INSTANCES + " SET " + TaskContract.Instances.INSTANCE_START_SORTING + " = " + TaskContract.Instances.INSTANCE_START
- + ", " + TaskContract.Instances.INSTANCE_DUE_SORTING + " = " + TaskContract.Instances.INSTANCE_DUE);
- }
- if (oldVersion < 4)
- {
- // drop old view before altering the schema
- db.execSQL(SQL_DROP_TASK_VIEW);
- db.execSQL(SQL_DROP_INSTANCE_VIEW);
-
- // change property id column name to work with the left join in task view
- db.execSQL(SQL_DROP_TASKS_CLEANUP_TRIGGER);
- db.execSQL(SQL_DROP_PROPERTIES_TABLE);
- db.execSQL(SQL_CREATE_PROPERTIES_TABLE);
- db.execSQL(SQL_CREATE_TASKS_CLEANUP_TRIGGER);
-
- // create categories mapping table
- db.execSQL(SQL_CREATE_CATEGORIES_MAPPING_TABLE);
-
- // create alarms table
- db.execSQL(SQL_CREATE_ALARMS_TABLE);
-
- // update views
- db.execSQL(SQL_CREATE_TASK_VIEW);
- db.execSQL(SQL_CREATE_TASK_PROPERTY_VIEW);
- db.execSQL(SQL_CREATE_INSTANCE_VIEW);
- db.execSQL(SQL_CREATE_INSTANCE_PROPERTY_VIEW);
- db.execSQL(SQL_CREATE_INSTANCE_CATEGORY_VIEW);
-
- // create Indices
- db.execSQL(createIndexString(Tables.PROPERTIES, false, TaskContract.Properties.MIMETYPE, TaskContract.Properties.TASK_ID));
- db.execSQL(createIndexString(Tables.PROPERTIES, false, TaskContract.Properties.TASK_ID));
- db.execSQL(createIndexString(Tables.CATEGORIES, false, TaskContract.Categories.ACCOUNT_NAME, TaskContract.Categories.ACCOUNT_TYPE,
- TaskContract.Categories.NAME));
- db.execSQL(createIndexString(Tables.CATEGORIES, false, TaskContract.Categories.NAME));
-
- // add new triggers
- db.execSQL(SQL_CREATE_ALARM_PROPERTY_CLEANUP_TRIGGER);
- db.execSQL(SQL_CREATE_ALARM_COUNT_CREATE_TRIGGER);
- db.execSQL(SQL_CREATE_ALARM_COUNT_UPDATE_TRIGGER);
- db.execSQL(SQL_CREATE_ALARM_COUNT_DELETE_TRIGGER);
-
- }
- if (oldVersion < 6)
- {
- db.execSQL("alter table " + Tables.TASKS + " add column " + Tasks.PARENT_ID + " integer;");
- db.execSQL("alter table " + Tables.TASKS + " add column " + Tasks.HAS_ALARMS + " integer;");
- db.execSQL("alter table " + Tables.TASKS + " add column " + Tasks.SORTING + " text;");
- }
- if (oldVersion < 7)
- {
- db.execSQL(SQL_CREATE_LISTS_CLEANUP_TRIGGER);
- }
- if (oldVersion < 8)
- {
- // replace priority 0 by null. We need this to sort the widget properly. Since 0 is the default this is no problem when syncing.
- db.execSQL("update " + Tables.TASKS + " set " + Tasks.PRIORITY + "=null where " + Tasks.PRIORITY + "=0;");
- }
- if (oldVersion < 9)
- {
- // add missing column _UID
- db.execSQL("alter table " + Tables.TASKS + " add column " + Tasks._UID + " integer;");
- // add cleanup trigger for orphaned properties
- db.execSQL(SQL_CREATE_TASK_PROPERTY_CLEANUP_TRIGGER);
- }
- if (oldVersion < 10)
- {
- // add property column to categories_mapping table. Since adding a constraint is not supported by SQLite we have to remove and recreate the entire
- // table
- db.execSQL("drop table " + Tables.CATEGORIES_MAPPING);
- db.execSQL(SQL_CREATE_CATEGORIES_MAPPING_TABLE);
- db.execSQL(SQL_CREATE_CATEGORY_PROPERTY_CLEANUP_TRIGGER);
- }
- if (oldVersion < 11)
- {
- db.execSQL("alter table " + Tables.TASKS + " add column " + Tasks.PINNED + " integer;");
- db.execSQL("alter table " + Tables.TASKS + " add column " + Tasks.HAS_PROPERTIES + " integer;");
- }
-
- if (oldVersion < 12)
- {
- // rename the local account type
- ContentValues values = new ContentValues(1);
- values.put(TaskLists.ACCOUNT_TYPE, TaskContract.LOCAL_ACCOUNT_TYPE);
- db.update(Tables.LISTS, values, TaskLists.ACCOUNT_TYPE + "=?", new String[] { "LOCAL" });
- }
-
- if (oldVersion < 13)
- {
- db.execSQL(SQL_CREATE_SYNCSTATE_TABLE);
- }
-
- if (oldVersion < 14)
- {
- // create a unique index for account name and account type on the sync state table
- db.execSQL(createIndexString(Tables.SYNCSTATE, true, TaskContract.SyncState.ACCOUNT_NAME, TaskContract.SyncState.ACCOUNT_TYPE));
- }
-
- if (oldVersion < 16)
- {
- db.execSQL(createIndexString(Tables.INSTANCES, false, TaskContract.Instances.INSTANCE_START_SORTING));
- db.execSQL(createIndexString(Tables.INSTANCES, false, TaskContract.Instances.INSTANCE_DUE_SORTING));
- }
-
- if (oldVersion < 17)
- {
- db.execSQL("alter table " + Tables.INSTANCES + " add column " + TaskContract.Instances.INSTANCE_ORIGINAL_TIME + " integer default 0;");
- db.execSQL(createIndexString(Tables.INSTANCES, false, TaskContract.Instances.INSTANCE_ORIGINAL_TIME));
- }
-
- if (oldVersion < 18)
- {
- db.execSQL("alter table " + Tables.INSTANCES + " add column " + TaskContract.Instances.DISTANCE_FROM_CURRENT + " integer default 0;");
- }
-
- if (oldVersion < 19)
- {
- db.execSQL(SQL_CREATE_INSTANCE_CLIENT_VIEW);
- }
-
- if (oldVersion < 22)
- {
- // create version column, unless it already exists
- if (!new First<>(new TableColumns(Tables.TASKS).value(db), new Equals<>(Tasks.VERSION)).isPresent())
- {
- // create task version column and update trigger
- db.execSQL("alter table " + Tables.TASKS + " add column " + Tasks.VERSION + " Integer default 0;");
- db.execSQL(SQL_CREATE_TASK_VERSION_TRIGGER);
- }
- }
-
- if (oldVersion < 22)
- {
- db.beginTransaction();
- try
- {
- // make sure we upgrade the instances of every recurring task
- EntityProcessor
- * TODO: add support for recurring tasks
- *
- * TODO: add support for reminders
- *
- * TODO: add support for attendees
- *
- * TODO: refactor the selection stuff
- *
- * @author Marten Gajda
- * This can be shared by multiple threads, hence the {@link AtomicBoolean}.
- */
- private AtomicBoolean mChanged = new AtomicBoolean(false);
-
- /**
- * This is a per transaction/thread flag which indicates whether new lists with an unknown account have been added.
- * If this holds true at the end of a transaction a window should be shown to ask the user for access to that account.
- */
- private ThreadLocal
- * AGENDULA CHANGE. Upstream held {@code android.permission.GET_ACCOUNTS} and so could enumerate every account on the device; we dropped it, because
- * since API 26 an authenticator already makes its own accounts visible to its own package and nothing else concerns us.
- *
- * That deletion is only safe together with {@link #cleanUpLists}: an account we cannot see is indistinguishable from an account that has been removed,
- * and the upstream cleanup treats the latter as a licence to delete the lists hanging off it. Restricting the cleanup to types in this set makes that
- * failure impossible by construction rather than by care — the provider can only ever prune accounts it is authoritative about.
- *
- * While Agendula ships no sync adapter this set is empty and no list is ever pruned. Our own account type joins it automatically the moment the
- * authenticator is declared, with no further change here.
- *
- * @return the account types authenticated by this very package, possibly empty — never null.
- */
- static Set
- * TODO: store links into the calendar provider if we find an event that matches the UID.
- * true if the caller pretends to be a sync adapter, false otherwise.
- */
- @Override
- public boolean isCallerSyncAdapter(Uri uri)
- {
- String param = uri.getQueryParameter(TaskContract.CALLER_IS_SYNCADAPTER);
- return param != null && !"false".equals(param);
- }
-
-
- /**
- * Return true if the URI indicates to a load extended properties with {@link TaskContract#LOAD_PROPERTIES}.
- *
- * @param uri
- * The {@link Uri} to check.
- *
- * @return true if the URI requests to load extended properties, false otherwise.
- */
- public boolean shouldLoadProperties(Uri uri)
- {
- String param = uri.getQueryParameter(TaskContract.LOAD_PROPERTIES);
- return param != null && !"false".equals(param);
- }
-
-
- /**
- * Get the account name from the given {@link Uri}.
- *
- * @param uri
- * The Uri to check.
- *
- * @return The account name or null if no account name has been specified.
- */
- protected String getAccountName(Uri uri)
- {
- return uri.getQueryParameter(TaskContract.ACCOUNT_NAME);
- }
-
-
- /**
- * Get the account type from the given {@link Uri}.
- *
- * @param uri
- * The Uri to check.
- *
- * @return The account type or null if no account type has been specified.
- */
- protected String getAccountType(Uri uri)
- {
- return uri.getQueryParameter(TaskContract.ACCOUNT_TYPE);
- }
-
-
- /**
- * Get any id from the given {@link Uri}.
- *
- * @param uri
- * The Uri.
- *
- * @return The last path segment (which should contain the id).
- */
- private long getId(Uri uri)
- {
- return Long.parseLong(uri.getPathSegments().get(1));
- }
-
-
- /**
- * Build a selection string that selects the account specified in uri.
- *
- * @param uri
- * A {@link Uri} that specifies an account.
- *
- * @return A {@link StringBuilder} with a selection string for the account.
- */
- protected StringBuilder selectAccount(Uri uri)
- {
- StringBuilder sb = new StringBuilder(256);
- return selectAccount(sb, uri);
- }
-
-
- /**
- * Append the selection of the account specified in uri to the {@link StringBuilder} sb.
- *
- * @param sb
- * A {@link StringBuilder} that the selection is appended to.
- * @param uri
- * A {@link Uri} that specifies an account.
- *
- * @return sb.
- */
- protected StringBuilder selectAccount(StringBuilder sb, Uri uri)
- {
- String accountName = getAccountName(uri);
- String accountType = getAccountType(uri);
-
- if (accountName != null || accountType != null)
- {
-
- if (accountName != null)
- {
- if (sb.length() > 0)
- {
- sb.append(" AND ");
- }
-
- sb.append(TaskListSyncColumns.ACCOUNT_NAME);
- sb.append("=");
- DatabaseUtils.appendEscapedSQLString(sb, accountName);
- }
- if (accountType != null)
- {
-
- if (sb.length() > 0)
- {
- sb.append(" AND ");
- }
-
- sb.append(TaskListSyncColumns.ACCOUNT_TYPE);
- sb.append("=");
- DatabaseUtils.appendEscapedSQLString(sb, accountType);
- }
- }
- return sb;
- }
-
-
- /**
- * Append the selection of the account specified in uri to the an {@link SQLiteQueryBuilder}.
- *
- * @param sqlBuilder
- * A {@link SQLiteQueryBuilder} that the selection is appended to.
- * @param uri
- * A {@link Uri} that specifies an account.
- */
- protected void selectAccount(SQLiteQueryBuilder sqlBuilder, Uri uri)
- {
- String accountName = getAccountName(uri);
- String accountType = getAccountType(uri);
-
- if (accountName != null)
- {
- sqlBuilder.appendWhere(" AND ");
- sqlBuilder.appendWhere(TaskListSyncColumns.ACCOUNT_NAME);
- sqlBuilder.appendWhere("=");
- sqlBuilder.appendWhereEscapeString(accountName);
- }
- if (accountType != null)
- {
- sqlBuilder.appendWhere(" AND ");
- sqlBuilder.appendWhere(TaskListSyncColumns.ACCOUNT_TYPE);
- sqlBuilder.appendWhere("=");
- sqlBuilder.appendWhereEscapeString(accountType);
- }
- }
-
-
- private StringBuilder _selectId(StringBuilder sb, long id, String key)
- {
- if (sb.length() > 0)
- {
- sb.append(" AND ");
- }
- sb.append(key);
- sb.append("=");
- sb.append(id);
- return sb;
- }
-
-
- protected StringBuilder selectId(Uri uri)
- {
- StringBuilder sb = new StringBuilder(128);
- return selectId(sb, uri);
- }
-
-
- protected StringBuilder selectId(StringBuilder sb, Uri uri)
- {
- return _selectId(sb, getId(uri), TaskListColumns._ID);
- }
-
-
- protected StringBuilder selectTaskId(Uri uri)
- {
- StringBuilder sb = new StringBuilder(128);
- return selectTaskId(sb, uri);
- }
-
-
- protected StringBuilder selectTaskId(long id)
- {
- StringBuilder sb = new StringBuilder(128);
- return selectTaskId(sb, id);
- }
-
-
- protected StringBuilder selectTaskId(StringBuilder sb, Uri uri)
- {
- return selectTaskId(sb, getId(uri));
- }
-
-
- protected StringBuilder selectTaskId(StringBuilder sb, long id)
- {
- return _selectId(sb, id, Instances.TASK_ID);
-
- }
-
-
- protected StringBuilder selectPropertyId(Uri uri)
- {
- StringBuilder sb = new StringBuilder(128);
- return selectPropertyId(sb, uri);
- }
-
-
- protected StringBuilder selectPropertyId(StringBuilder sb, Uri uri)
- {
- return selectPropertyId(sb, getId(uri));
- }
-
-
- protected StringBuilder selectPropertyId(long id)
- {
- StringBuilder sb = new StringBuilder(128);
- return selectPropertyId(sb, id);
- }
-
-
- protected StringBuilder selectPropertyId(StringBuilder sb, long id)
- {
- return _selectId(sb, id, PropertyColumns.PROPERTY_ID);
- }
-
-
- /**
- * Add a selection by ID to the given {@link SQLiteQueryBuilder}. The id is taken from the given Uri.
- *
- * @param sqlBuilder
- * The {@link SQLiteQueryBuilder} to append the selection to.
- * @param idColumn
- * The column that must match the id.
- * @param uri
- * An {@link Uri} that contains the id.
- */
- protected void selectId(SQLiteQueryBuilder sqlBuilder, String idColumn, Uri uri)
- {
- sqlBuilder.appendWhere(" AND ");
- sqlBuilder.appendWhere(idColumn);
- sqlBuilder.appendWhere("=");
- sqlBuilder.appendWhere(String.valueOf(getId(uri)));
- }
-
-
- /**
- * Append any arbitrary selection string to the selection in sb
- *
- * @param sb
- * A {@link StringBuilder} that already contains a selection string.
- * @param selection
- * A valid SQL selection string.
- *
- * @return A string with the final selection.
- */
- protected String updateSelection(StringBuilder sb, String selection)
- {
- if (selection != null)
- {
- if (sb.length() > 0)
- {
- sb.append(" AND ( ").append(selection).append(" ) ");
- }
- else
- {
- sb.append(" ( ").append(selection).append(" ) ");
- }
- }
- return sb.toString();
- }
-
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
- {
- final SQLiteDatabase db = getDatabaseHelper().getWritableDatabase();
- SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
- // initialize appendWhere, this allows us to append all other selections with a preceding "AND"
- sqlBuilder.appendWhere(" 1=1 ");
- boolean isSyncAdapter = isCallerSyncAdapter(uri);
-
- switch (mUriMatcher.match(uri))
- {
- case SYNCSTATE_ID:
- // the id is ignored, we only match by account type and name given in the Uri
- case SYNCSTATE:
- {
- if (TextUtils.isEmpty(getAccountName(uri)) || TextUtils.isEmpty(getAccountType(uri)))
- {
- throw new IllegalArgumentException("uri must contain an account when accessing syncstate");
- }
- selectAccount(sqlBuilder, uri);
- sqlBuilder.setTables(Tables.SYNCSTATE);
- break;
- }
- case LISTS:
- // add account to selection if any
- selectAccount(sqlBuilder, uri);
- sqlBuilder.setTables(Tables.LISTS);
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.TaskLists.DEFAULT_SORT_ORDER;
- }
- break;
-
- case LIST_ID:
- // add account to selection if any
- selectAccount(sqlBuilder, uri);
- sqlBuilder.setTables(Tables.LISTS);
- selectId(sqlBuilder, TaskListColumns._ID, uri);
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.TaskLists.DEFAULT_SORT_ORDER;
- }
- break;
-
- case TASKS:
- if (shouldLoadProperties(uri))
- {
- // extended properties were requested, therefore change to task view that includes these properties
- sqlBuilder.setTables(Tables.TASKS_PROPERTY_VIEW);
- }
- else
- {
- sqlBuilder.setTables(Tables.TASKS_VIEW);
- }
- if (!isSyncAdapter)
- {
- // do not return deleted rows if caller is not a sync adapter
- sqlBuilder.appendWhere(" AND ");
- sqlBuilder.appendWhere(Tasks._DELETED);
- sqlBuilder.appendWhere("=0");
- }
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.Tasks.DEFAULT_SORT_ORDER;
- }
- break;
-
- case TASK_ID:
- if (shouldLoadProperties(uri))
- {
- // extended properties were requested, therefore change to task view that includes these properties
- sqlBuilder.setTables(Tables.TASKS_PROPERTY_VIEW);
- }
- else
- {
- sqlBuilder.setTables(Tables.TASKS_VIEW);
- }
- selectId(sqlBuilder, TaskColumns._ID, uri);
- if (!isSyncAdapter)
- {
- // do not return deleted rows if caller is not a sync adapter
- sqlBuilder.appendWhere(" AND ");
- sqlBuilder.appendWhere(Tasks._DELETED);
- sqlBuilder.appendWhere("=0");
- }
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.Tasks.DEFAULT_SORT_ORDER;
- }
- break;
-
- case INSTANCES:
- if (shouldLoadProperties(uri))
- {
- // extended properties were requested, therefore change to instance view that includes these properties
- sqlBuilder.setTables(Tables.INSTANCE_PROPERTY_VIEW);
- }
- else
- {
- sqlBuilder.setTables(Tables.INSTANCE_CLIENT_VIEW);
- }
- if (!isSyncAdapter)
- {
- // do not return deleted rows if caller is not a sync adapter
- sqlBuilder.appendWhere(" AND ");
- sqlBuilder.appendWhere(Tasks._DELETED);
- sqlBuilder.appendWhere("=0");
- }
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.Instances.DEFAULT_SORT_ORDER;
- }
- break;
-
- case INSTANCE_ID:
- if (shouldLoadProperties(uri))
- {
- // extended properties were requested, therefore change to instance view that includes these properties
- sqlBuilder.setTables(Tables.INSTANCE_PROPERTY_VIEW);
- }
- else
- {
- sqlBuilder.setTables(Tables.INSTANCE_CLIENT_VIEW);
- }
- selectId(sqlBuilder, Instances._ID, uri);
- if (!isSyncAdapter)
- {
- // do not return deleted rows if caller is not a sync adapter
- sqlBuilder.appendWhere(" AND ");
- sqlBuilder.appendWhere(Tasks._DELETED);
- sqlBuilder.appendWhere("=0");
- }
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.Instances.DEFAULT_SORT_ORDER;
- }
- break;
-
- case CATEGORIES:
- selectAccount(sqlBuilder, uri);
- sqlBuilder.setTables(Tables.CATEGORIES);
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.Categories.DEFAULT_SORT_ORDER;
- }
- break;
-
- case CATEGORY_ID:
- selectAccount(sqlBuilder, uri);
- sqlBuilder.setTables(Tables.CATEGORIES);
- selectId(sqlBuilder, CategoriesColumns._ID, uri);
- if (sortOrder == null || sortOrder.length() == 0)
- {
- sortOrder = TaskContract.Categories.DEFAULT_SORT_ORDER;
- }
- break;
-
- case PROPERTIES:
- sqlBuilder.setTables(Tables.PROPERTIES);
- break;
-
- case PROPERTY_ID:
- sqlBuilder.setTables(Tables.PROPERTIES);
- selectId(sqlBuilder, PropertyColumns.PROPERTY_ID, uri);
- break;
-
- case SEARCH:
- String searchString = uri.getQueryParameter(Tasks.SEARCH_QUERY_PARAMETER);
- searchString = Uri.decode(searchString);
- Cursor searchCursor = FTSDatabaseHelper.getTaskSearchCursor(db, searchString, projection, selection, selectionArgs, sortOrder);
- if (searchCursor != null)
- {
- // attach tasks uri for notifications, that way the search results are updated when a task changes
- searchCursor.setNotificationUri(getContext().getContentResolver(), Tasks.getContentUri(mAuthority));
- }
- return searchCursor;
-
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
-
- Cursor c = sqlBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
-
- if (c != null)
- {
- c.setNotificationUri(getContext().getContentResolver(), uri);
- }
- return c;
- }
-
-
- @Override
- public int deleteInTransaction(final SQLiteDatabase db, Uri uri, String selection, String[] selectionArgs, final boolean isSyncAdapter)
- {
- int count = 0;
- String accountName = getAccountName(uri);
- String accountType = getAccountType(uri);
-
- switch (mUriMatcher.match(uri))
- {
- case SYNCSTATE_ID:
- // the id is ignored, we only match by account type and name given in the Uri
- case SYNCSTATE:
- {
- if (!isSyncAdapter)
- {
- throw new IllegalAccessError("only sync adapters may access syncstate");
- }
- if (TextUtils.isEmpty(getAccountName(uri)) || TextUtils.isEmpty(getAccountType(uri)))
- {
- throw new IllegalArgumentException("uri must contain an account when accessing syncstate");
- }
- selection = updateSelection(selectAccount(uri), selection);
- count = db.delete(Tables.SYNCSTATE, selection, selectionArgs);
- break;
- }
- /*
- * Deleting task lists is only allowed to sync adapters. They must provide ACCOUNT_NAME and ACCOUNT_TYPE.
- */
- case LIST_ID:
- // add _id to selection and fall through
- selection = updateSelection(selectId(uri), selection);
- case LISTS:
- {
- if (isSyncAdapter)
- {
- if (TextUtils.isEmpty(accountType) || TextUtils.isEmpty(accountName))
- {
- throw new IllegalArgumentException("Sync adapters must specify an account and account type: " + uri);
- }
- }
-
- // iterate over all lists that match the selection
- final Cursor cursor = db.query(Tables.LISTS, null, selection, selectionArgs, null, null, null, null);
-
- try
- {
- while (cursor.moveToNext())
- {
- final ListAdapter list = new CursorContentValuesListAdapter(ListAdapter._ID.getFrom(cursor), cursor, new ContentValues());
-
- mListProcessorChain.delete(db, list, isSyncAdapter);
- mChanged.set(true);
- count++;
- }
- }
- finally
- {
- cursor.close();
- }
-
- break;
-
- }
- /*
- * Task won't be removed, just marked as deleted if the caller isn't a sync adapter. Sync adapters can remove tasks immediately.
- */
- case TASK_ID:
- // add id to selection and fall through
- selection = updateSelection(selectId(uri), selection);
-
- case TASKS:
- {
- // TODO: filter by account name and type if present in uri.
-
- if (isSyncAdapter)
- {
- if (TextUtils.isEmpty(accountType) || TextUtils.isEmpty(accountName))
- {
- throw new IllegalArgumentException("Sync adapters must specify an account and account type: " + uri);
- }
- }
-
- // iterate over all tasks that match the selection
- final Cursor cursor = db.query(Tables.TASKS_VIEW, null, selection, selectionArgs, null, null, null, null);
-
- try
- {
- while (cursor.moveToNext())
- {
- final TaskAdapter task = new CursorContentValuesTaskAdapter(cursor, new ContentValues());
-
- mTaskProcessorChain.delete(db, task, isSyncAdapter);
-
- mChanged.set(true);
- count++;
- }
- }
- finally
- {
- cursor.close();
- }
-
- break;
- }
-
- case INSTANCE_ID:
- // add id to selection and fall through
- selection = updateSelection(selectId(uri), selection);
-
- case INSTANCES:
- {
- // iterate over all instances that match the selection
- try (Cursor cursor = db.query(Tables.INSTANCE_VIEW, null, selection, selectionArgs, null, null, null, null))
- {
- while (cursor.moveToNext())
- {
- mInstanceProcessorChain.delete(db, new CursorContentValuesInstanceAdapter(cursor, new ContentValues()), isSyncAdapter);
- mChanged.set(true);
- count++;
- }
- }
-
- break;
- }
-
- case ALARM_ID:
- // add id to selection and fall through
- selection = updateSelection(selectId(uri), selection);
-
- case ALARMS:
-
- count = db.delete(Tables.ALARMS, selection, selectionArgs);
- break;
-
- case PROPERTY_ID:
- selection = updateSelection(selectPropertyId(uri), selection);
-
- case PROPERTIES:
- // fetch all properties that match the selection
- Cursor cursor = db.query(Tables.PROPERTIES, null, selection, selectionArgs, null, null, null);
-
- try
- {
- int propIdCol = cursor.getColumnIndex(Properties.PROPERTY_ID);
- int taskIdCol = cursor.getColumnIndex(Properties.TASK_ID);
- int mimeTypeCol = cursor.getColumnIndex(Properties.MIMETYPE);
- while (cursor.moveToNext())
- {
- long propertyId = cursor.getLong(propIdCol);
- long taskId = cursor.getLong(taskIdCol);
- String mimeType = cursor.getString(mimeTypeCol);
- if (mimeType != null)
- {
- PropertyHandler handler = PropertyHandlerFactory.get(mimeType);
- count += handler.delete(db, taskId, propertyId, cursor, isSyncAdapter);
- }
- }
- }
- finally
- {
- cursor.close();
- }
- postNotifyUri(Properties.getContentUri(mAuthority));
- break;
-
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
-
- if (count > 0)
- {
- postNotifyUri(uri);
- postNotifyUri(Instances.getContentUri(mAuthority));
- postNotifyUri(Tasks.getContentUri(mAuthority));
- }
- return count;
- }
-
-
- @Override
- public Uri insertInTransaction(final SQLiteDatabase db, Uri uri, final ContentValues values, final boolean isSyncAdapter)
- {
- long rowId;
- Uri result_uri;
-
- String accountName = getAccountName(uri);
- String accountType = getAccountType(uri);
-
- switch (mUriMatcher.match(uri))
- {
- case SYNCSTATE:
- {
- if (!isSyncAdapter)
- {
- throw new IllegalAccessError("only sync adapters may access syncstate");
- }
- if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType))
- {
- throw new IllegalArgumentException("uri must contain an account when accessing syncstate");
- }
- values.put(SyncState.ACCOUNT_NAME, accountName);
- values.put(SyncState.ACCOUNT_TYPE, accountType);
- rowId = db.replace(Tables.SYNCSTATE, null, values);
- result_uri = TaskContract.SyncState.getContentUri(mAuthority);
- break;
- }
- case LISTS:
- {
- final ListAdapter list = new ContentValuesListAdapter(values);
- list.set(ListAdapter.ACCOUNT_NAME, accountName);
- list.set(ListAdapter.ACCOUNT_TYPE, accountType);
-
- mListProcessorChain.insert(db, list, isSyncAdapter);
- mChanged.set(true);
-
- rowId = list.id();
- result_uri = TaskContract.TaskLists.getContentUri(mAuthority);
- // if the account is unknown we need to ask the user
- //
- // AGENDULA CHANGE: also require the type to be one we authenticate ourselves, for
- // the same reason Utils.cleanUpLists does. Without GET_ACCOUNTS the cache only ever
- // holds our own accounts, so upstream's test would call *every* externally-synced
- // list stale and fire a broadcast about it on each insert. Nothing listens today;
- // the point is that whatever listens tomorrow gets a signal that means something.
- if (Build.VERSION.SDK_INT >= 26 &&
- !TaskContract.LOCAL_ACCOUNT_TYPE.equals(accountType) &&
- Utils.isOwnAccountType(getContext(), accountType) &&
- !mAccountCache.get().contains(new Account(accountName, accountType)))
- {
- // store the fact that we have an unknown account in this transaction
- mStaleListCreated.set(true);
- Log.d(TAG, String.format("List with unknown account %s inserted.", new Account(accountName, accountType)));
- }
- break;
- }
- case TASKS:
- final TaskAdapter task = new ContentValuesTaskAdapter(values);
-
- mTaskProcessorChain.insert(db, task, isSyncAdapter);
-
- mChanged.set(true);
-
- rowId = task.id();
- result_uri = TaskContract.Tasks.getContentUri(mAuthority);
-
- postNotifyUri(Instances.getContentUri(mAuthority));
- postNotifyUri(Tasks.getContentUri(mAuthority));
-
- break;
-
- // inserting instances is currently disabled because we only expand one instance,
- // so even though a new task (exception) would be created, no instance might show up
- // we need to resolve this discrepancy. Until then this feature remains disabled.
-// case INSTANCES:
-// {
-// InstanceAdapter instance = mInstanceProcessorChain.insert(db, new ContentValuesInstanceAdapter(values), isSyncAdapter);
-// rowId = instance.id();
-// result_uri = TaskContract.Instances.getContentUri(mAuthority);
-//
-// postNotifyUri(Instances.getContentUri(mAuthority));
-// postNotifyUri(Tasks.getContentUri(mAuthority));
-//
-// break;
-// }
- case PROPERTIES:
- String mimetype = values.getAsString(Properties.MIMETYPE);
-
- if (mimetype == null)
- {
- throw new IllegalArgumentException("missing mimetype in property values");
- }
-
- Long taskId = values.getAsLong(Properties.TASK_ID);
- if (taskId == null)
- {
- throw new IllegalArgumentException("missing task id in property values");
- }
-
- if (values.containsKey(Properties.PROPERTY_ID))
- {
- throw new IllegalArgumentException("property id can not be written");
- }
-
- PropertyHandler handler = PropertyHandlerFactory.get(mimetype);
- rowId = handler.insert(db, taskId, values, isSyncAdapter);
- result_uri = TaskContract.Properties.getContentUri(mAuthority);
- if (rowId >= 0)
- {
- postNotifyUri(Tasks.getContentUri(mAuthority));
- postNotifyUri(Instances.getContentUri(mAuthority));
- }
- break;
-
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
-
- if (rowId > 0 && result_uri != null)
- {
- result_uri = ContentUris.withAppendedId(result_uri, rowId);
- postNotifyUri(result_uri);
- postNotifyUri(uri);
- return result_uri;
- }
- throw new SQLException("Failed to insert row into " + uri);
- }
-
-
- @Override
- public int updateInTransaction(final SQLiteDatabase db, Uri uri, final ContentValues values, String selection, String[] selectionArgs,
- final boolean isSyncAdapter)
- {
- int count = 0;
- boolean dataChanged = false;
- switch (mUriMatcher.match(uri))
- {
- case SYNCSTATE_ID:
- // the id is ignored, we only match by account type and name given in the Uri
- case SYNCSTATE:
- {
- if (!isSyncAdapter)
- {
- throw new IllegalAccessError("only sync adapters may access syncstate");
- }
-
- String accountName = getAccountName(uri);
- String accountType = getAccountType(uri);
- if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType))
- {
- throw new IllegalArgumentException("uri must contain an account when accessing syncstate");
- }
-
- if (values.size() == 0)
- {
- // we're done
- break;
- }
-
- values.put(SyncState.ACCOUNT_NAME, accountName);
- values.put(SyncState.ACCOUNT_TYPE, accountType);
-
- long id = db.replace(Tables.SYNCSTATE, null, values);
- if (id >= 0)
- {
- count = 1;
- }
- break;
- }
- case LIST_ID:
- // update selection and fall through
- selection = updateSelection(selectId(uri), selection);
-
- case LISTS:
- {
- // iterate over all task lists that match the selection
- final Cursor cursor = db.query(Tables.LISTS, null, selection, selectionArgs, null, null, null, null);
-
- int idCol = cursor.getColumnIndex(TaskContract.TaskLists._ID);
-
- try
- {
- while (cursor.moveToNext())
- {
- final long listId = cursor.getLong(idCol);
-
- // clone list values if we have more than one list to update
- // we need this, because the processors may change the values
- final ListAdapter list = new CursorContentValuesListAdapter(listId, cursor, cursor.getCount() > 1 ? new ContentValues(values) : values);
-
- if (list.hasUpdates())
- {
- mListProcessorChain.update(db, list, isSyncAdapter);
- dataChanged |= !TASK_LIST_SYNC_COLUMNS.containsAll(values.keySet());
- }
- // note we still count the row even if no update was necessary
- count++;
- }
- }
- finally
- {
- cursor.close();
- }
- break;
- }
- case TASK_ID:
- // update selection and fall through
- selection = updateSelection(selectId(uri), selection);
-
- case TASKS:
- {
- // iterate over all tasks that match the selection
- final Cursor cursor = db.query(Tables.TASKS_VIEW, null, selection, selectionArgs, null, null, null, null);
-
- try
- {
- while (cursor.moveToNext())
- {
- // clone task values if we have more than one task to update
- // we need this, because the processors may change the values
- final TaskAdapter task = new CursorContentValuesTaskAdapter(cursor, cursor.getCount() > 1 ? new ContentValues(values) : values);
-
- if (task.hasUpdates())
- {
- mTaskProcessorChain.update(db, task, isSyncAdapter);
- dataChanged |= !TASK_LIST_SYNC_COLUMNS.containsAll(values.keySet());
- }
- // note we still count the row even if no update was necessary
- count++;
- }
- }
- finally
- {
- cursor.close();
- }
-
- if (dataChanged)
- {
- postNotifyUri(Instances.getContentUri(mAuthority));
- postNotifyUri(Tasks.getContentUri(mAuthority));
- }
- break;
- }
-
- case INSTANCE_ID:
- // update selection and fall through
- selection = updateSelection(selectId(uri), selection);
-
- case INSTANCES:
- {
- // iterate over all instances that match the selection
-
- try (Cursor cursor = db.query(Tables.INSTANCE_VIEW, null, selection, selectionArgs, null, null, null, null))
- {
- while (cursor.moveToNext())
- {
- // clone task values if we have more than one task to update
- // we need this, because the processors may change the values
- final InstanceAdapter instance = new CursorContentValuesInstanceAdapter(cursor,
- cursor.getCount() > 1 ? new ContentValues(values) : values);
-
- if (instance.hasUpdates())
- {
- mInstanceProcessorChain.update(db, instance, isSyncAdapter);
- dataChanged = true;
- }
- // note we still count the row even if no update was necessary
- count++;
- }
- }
-
- if (dataChanged)
- {
- postNotifyUri(Instances.getContentUri(mAuthority));
- postNotifyUri(Tasks.getContentUri(mAuthority));
- }
- break;
- }
- case PROPERTY_ID:
- selection = updateSelection(selectPropertyId(uri), selection);
-
- case PROPERTIES:
- if (values.containsKey(Properties.MIMETYPE))
- {
- throw new IllegalArgumentException("property mimetypes can not be modified");
- }
-
- if (values.containsKey(Properties.TASK_ID))
- {
- throw new IllegalArgumentException("task id can not be changed");
- }
-
- if (values.containsKey(Properties.PROPERTY_ID))
- {
- throw new IllegalArgumentException("property id can not be changed");
- }
-
- // fetch all properties that match the selection
- Cursor cursor = db.query(Tables.PROPERTIES, null, selection, selectionArgs, null, null, null);
-
- try
- {
- int propIdCol = cursor.getColumnIndex(Properties.PROPERTY_ID);
- int taskIdCol = cursor.getColumnIndex(Properties.TASK_ID);
- int mimeTypeCol = cursor.getColumnIndex(Properties.MIMETYPE);
- while (cursor.moveToNext())
- {
- long propertyId = cursor.getLong(propIdCol);
- long taskId = cursor.getLong(taskIdCol);
- String mimeType = cursor.getString(mimeTypeCol);
- if (mimeType != null)
- {
- PropertyHandler handler = PropertyHandlerFactory.get(mimeType);
- count += handler.update(db, taskId, propertyId, values, cursor, isSyncAdapter);
- }
- }
- }
- finally
- {
- cursor.close();
- }
- postNotifyUri(Properties.getContentUri(mAuthority));
- break;
-
- case CATEGORY_ID:
- String newCategorySelection = updateSelection(selectId(uri), selection);
- validateCategoryValues(values, false, isSyncAdapter);
- count = db.update(Tables.CATEGORIES, values, newCategorySelection, selectionArgs);
- break;
- case ALARM_ID:
- String newAlarmSelection = updateSelection(selectId(uri), selection);
- validateAlarmValues(values, false, isSyncAdapter);
- count = db.update(Tables.ALARMS, values, newAlarmSelection, selectionArgs);
- break;
- default:
- ContentOperation operation = ContentOperation.get(mUriMatcher.match(uri), OPERATIONS);
-
- if (operation == null)
- {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
-
- operation.run(getContext(), mAsyncHandler, uri, db, values);
- }
-
- if (dataChanged)
- {
- // send notifications, because non-sync columns have been updated
- postNotifyUri(uri);
- mChanged.set(true);
- }
-
- return count;
- }
-
-
- /**
- * Update task due and task start notifications.
- */
- private void updateNotifications()
- {
- mAsyncHandler.post(new Runnable()
- {
-
- @Override
- public void run()
- {
- ContentOperation.UPDATE_NOTIFICATION_ALARM.fire(getContext(), null);
- }
- });
- }
-
-
- /**
- * Validate the given category values.
- *
- * @param values
- * The category properties to validate.
- *
- * @throws IllegalArgumentException
- * if any of the values is invalid.
- */
- private void validateCategoryValues(ContentValues values, boolean isNew, boolean isSyncAdapter)
- {
- // row id can not be changed or set manually
- if (values.containsKey(Categories._ID))
- {
- throw new IllegalArgumentException("_ID can not be set manually");
- }
-
- if (isNew != values.containsKey(Categories.ACCOUNT_NAME) && (!isNew || values.get(Categories.ACCOUNT_NAME) != null))
- {
- throw new IllegalArgumentException("ACCOUNT_NAME is write-once and required on INSERT");
- }
-
- if (isNew != values.containsKey(Categories.ACCOUNT_TYPE) && (!isNew || values.get(Categories.ACCOUNT_TYPE) != null))
- {
- throw new IllegalArgumentException("ACCOUNT_TYPE is write-once and required on INSERT");
- }
- }
-
-
- /**
- * Validate the given alarm values.
- *
- * @param values
- * The alarm values to validate
- *
- * @throws IllegalArgumentException
- * if any of the values is invalid.
- */
- private void validateAlarmValues(ContentValues values, boolean isNew, boolean isSyncAdapter)
- {
- if (values.containsKey(Alarms.ALARM_ID))
- {
- throw new IllegalArgumentException("ALARM_ID can not be set manually");
- }
- }
-
-
- @Override
- public String getType(Uri uri)
- {
- switch (mUriMatcher.match(uri))
- {
- case LISTS:
- return ContentResolver.CURSOR_DIR_BASE_TYPE + "/org.dmfs.tasks." + TaskLists.CONTENT_URI_PATH;
- case LIST_ID:
- return ContentResolver.CURSOR_ITEM_BASE_TYPE + "/org.dmfs.tasks." + TaskLists.CONTENT_URI_PATH;
- case TASKS:
- return ContentResolver.CURSOR_DIR_BASE_TYPE + "/org.dmfs.tasks." + Tasks.CONTENT_URI_PATH;
- case TASK_ID:
- return ContentResolver.CURSOR_ITEM_BASE_TYPE + "/org.dmfs.tasks." + Tasks.CONTENT_URI_PATH;
- case INSTANCES:
- return ContentResolver.CURSOR_DIR_BASE_TYPE + "/org.dmfs.tasks." + Instances.CONTENT_URI_PATH;
- case INSTANCE_ID:
- return ContentResolver.CURSOR_ITEM_BASE_TYPE + "/org.dmfs.tasks." + Instances.CONTENT_URI_PATH;
- default:
- throw new IllegalArgumentException("Unsupported URI: " + uri);
- }
- }
-
-
- @Override
- protected void onEndTransaction(boolean callerIsSyncAdapter)
- {
- super.onEndTransaction(callerIsSyncAdapter);
- if (mChanged.compareAndSet(true, false))
- {
- updateNotifications();
- Utils.sendActionProviderChangedBroadCast(getContext(), mAuthority);
- }
-
- if (Boolean.TRUE.equals(mStaleListCreated.get()))
- {
- // notify UI about the stale lists, it's up the UI to deal with this, either by showing a notification or an instant popup.
- Intent visbilityRequest = new Intent("org.dmfs.tasks.action.STALE_LIST_BROADCAST").setPackage(getContext().getPackageName());
- getContext().sendBroadcast(visbilityRequest);
- }
- }
-
-
- @Override
- public SQLiteOpenHelper getDatabaseHelper(Context context)
- {
- TaskDatabaseHelper helper = new TaskDatabaseHelper(context, this);
-
- return helper;
- }
-
-
- @Override
- public void onDatabaseCreated(SQLiteDatabase db)
- {
- // notify listeners that the database has been created
- Intent dbInitializedIntent = new Intent(TaskContract.ACTION_DATABASE_INITIALIZED);
- dbInitializedIntent.setDataAndType(TaskContract.getContentUri(mAuthority), TaskContract.MIMETYPE_AUTHORITY);
- // Android SDK 26 doesn't allow us to send implicit broadcasts, this particular brodcast is only for internal use, so just make it explicit by setting our package name
- dbInitializedIntent.setPackage(getContext().getPackageName());
- getContext().sendBroadcast(dbInitializedIntent);
- }
-
-
- @Override
- public void onDatabaseUpdate(SQLiteDatabase db, int oldVersion, int newVersion)
- {
- if (oldVersion < 15)
- {
- mAsyncHandler.post(() -> ContentOperation.UPDATE_TIMEZONE.fire(getContext(), null));
- }
- }
-
-
- @Override
- protected boolean syncToNetwork(Uri uri)
- {
- return true;
- }
-
-
- @Override
- public void onAccountsUpdated(Account[] accounts)
- {
- // cache the known accounts so we can check whether we know accounts for which new lists are added
- mAccountCache.set(new HashSet<>(Arrays.asList(accounts)));
- // TODO: we probably can move the cleanup code here and get rid of the Utils class
- Utils.cleanUpLists(getContext(), getDatabaseHelper().getWritableDatabase(), accounts, mAuthority);
- }
-}
diff --git a/provider/src/main/java/org/dmfs/provider/tasks/TaskProviderBroadcastReceiver.java b/provider/src/main/java/org/dmfs/provider/tasks/TaskProviderBroadcastReceiver.java
deleted file mode 100644
index e9b6a07..0000000
--- a/provider/src/main/java/org/dmfs/provider/tasks/TaskProviderBroadcastReceiver.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2017 dmfs GmbH
- *
- * 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 org.dmfs.provider.tasks;
-
-import android.annotation.SuppressLint;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-
-import org.dmfs.rfc5545.DateTime;
-
-import java.util.TimeZone;
-
-
-/**
- * A receiver for all task provider related broadcasts. This receiver merely forwards all incoming broadcasts to the provider, so they can be handled
- * asynchronously in the provider context.
- *
- * @author Marten Gajda isNew is false. If isNew is true this value is ignored.
- * @param isNew
- * Indicates that the content is new and not an update.
- * @param values
- * The {@link ContentValues} to validate.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The valid {@link ContentValues}.
- *
- * @throws IllegalArgumentException
- * if the {@link ContentValues} are invalid.
- */
- @Override
- public ContentValues validateValues(SQLiteDatabase db, long taskId, long propertyId, boolean isNew, ContentValues values, boolean isSyncAdapter)
- {
- // row id can not be changed or set manually
- if (values.containsKey(Property.Alarm.PROPERTY_ID))
- {
- throw new IllegalArgumentException("_ID can not be set manually");
- }
-
- if (!values.containsKey(Property.Alarm.MINUTES_BEFORE))
- {
- throw new IllegalArgumentException("alarm property requires a time offset");
- }
-
- if (!values.containsKey(Property.Alarm.REFERENCE) || values.getAsInteger(Property.Alarm.REFERENCE) < 0)
- {
- throw new IllegalArgumentException("alarm property requires a valid reference date ");
- }
-
- if (!values.containsKey(Property.Alarm.ALARM_TYPE))
- {
- throw new IllegalArgumentException("alarm property requires an alarm type");
- }
-
- return values;
- }
-
-
- /**
- * Inserts the alarm into the database.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * The id of the task the new property belongs to.
- * @param values
- * The {@link ContentValues} to insert.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The row id of the new alarm as long
- */
- @Override
- public long insert(SQLiteDatabase db, long taskId, ContentValues values, boolean isSyncAdapter)
- {
- values = validateValues(db, taskId, -1, true, values, isSyncAdapter);
- return super.insert(db, taskId, values, isSyncAdapter);
- }
-
-
- /**
- * Updates the alarm in the database.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * The id of the task this property belongs to.
- * @param propertyId
- * The id of the property.
- * @param values
- * The {@link ContentValues} to update.
- * @param oldValues
- * A {@link Cursor} pointing to the old values in the database.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The number of rows affected.
- */
- @Override
- public int update(SQLiteDatabase db, long taskId, long propertyId, ContentValues values, Cursor oldValues, boolean isSyncAdapter)
- {
- values = validateValues(db, taskId, propertyId, false, values, isSyncAdapter);
- return super.update(db, taskId, propertyId, values, oldValues, isSyncAdapter);
- }
-}
diff --git a/provider/src/main/java/org/dmfs/provider/tasks/handler/CategoryHandler.java b/provider/src/main/java/org/dmfs/provider/tasks/handler/CategoryHandler.java
deleted file mode 100644
index 8c129cb..0000000
--- a/provider/src/main/java/org/dmfs/provider/tasks/handler/CategoryHandler.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright 2017 dmfs GmbH
- *
- * 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 org.dmfs.provider.tasks.handler;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-
-import org.dmfs.provider.tasks.TaskDatabaseHelper.CategoriesMapping;
-import org.dmfs.provider.tasks.TaskDatabaseHelper.Tables;
-import org.dmfs.tasks.contract.TaskContract.Categories;
-import org.dmfs.tasks.contract.TaskContract.Properties;
-import org.dmfs.tasks.contract.TaskContract.Property.Category;
-import org.dmfs.tasks.contract.TaskContract.Tasks;
-
-
-/**
- * This class is used to handle category property values during database transactions.
- *
- * @author Tobias Reinsch isNew is false. If isNew is true this value is ignored.
- * @param isNew
- * Indicates that the content is new and not an update.
- * @param values
- * The {@link ContentValues} to validate.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The valid {@link ContentValues}.
- *
- * @throws IllegalArgumentException
- * if the {@link ContentValues} are invalid.
- */
- @Override
- public ContentValues validateValues(SQLiteDatabase db, long taskId, long propertyId, boolean isNew, ContentValues values, boolean isSyncAdapter)
- {
- // the category requires a name or an id
- if (!values.containsKey(Category.CATEGORY_ID) && !values.containsKey(Category.CATEGORY_NAME))
- {
- throw new IllegalArgumentException("Neiter an id nor a category name was supplied for the category property.");
- }
-
- // get the matching task & account for the property
- if (!values.containsKey(Properties.TASK_ID))
- {
- throw new IllegalArgumentException("No task id was supplied for the category property");
- }
- String[] queryArgs = { values.getAsString(Properties.TASK_ID) };
- String[] queryProjection = { Tasks.ACCOUNT_NAME, Tasks.ACCOUNT_TYPE };
- String querySelection = Tasks._ID + "=?";
- Cursor taskCursor = db.query(Tables.TASKS_VIEW, queryProjection, querySelection, queryArgs, null, null, null);
-
- String accountName = null;
- String accountType = null;
- try
- {
- if (taskCursor.moveToNext())
- {
- accountName = taskCursor.getString(0);
- accountType = taskCursor.getString(1);
-
- values.put(Categories.ACCOUNT_NAME, accountName);
- values.put(Categories.ACCOUNT_TYPE, accountType);
- }
- }
- finally
- {
- if (taskCursor != null)
- {
- taskCursor.close();
- }
- }
-
- if (accountName != null && accountType != null)
- {
- // search for matching categories
- String[] categoryArgs;
- Cursor cursor;
-
- if (values.containsKey(Categories._ID))
- {
- // serach by ID
- categoryArgs = new String[] { values.getAsString(Category.CATEGORY_ID), accountName, accountType };
- cursor = db.query(Tables.CATEGORIES, CATEGORY_ID_PROJECTION, CATEGORY_ID_SELECTION, categoryArgs, null, null, null);
- }
- else
- {
- // search by name
- categoryArgs = new String[] { values.getAsString(Category.CATEGORY_NAME), accountName, accountType };
- cursor = db.query(Tables.CATEGORIES, CATEGORY_ID_PROJECTION, CATEGORY_NAME_SELECTION, categoryArgs, null, null, null);
- }
- try
- {
- if (cursor != null && cursor.getCount() == 1)
- {
- cursor.moveToNext();
- Long categoryID = cursor.getLong(0);
- String categoryName = cursor.getString(1);
- int color = cursor.getInt(2);
-
- values.put(Category.CATEGORY_ID, categoryID);
- values.put(Category.CATEGORY_NAME, categoryName);
- values.put(Category.CATEGORY_COLOR, color);
- values.put(IS_NEW_CATEGORY, false);
- }
- else
- {
- values.put(IS_NEW_CATEGORY, true);
- }
- }
- finally
- {
- if (cursor != null)
- {
- cursor.close();
- }
- }
-
- }
-
- return values;
- }
-
-
- /**
- * Inserts the category into the database.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * The id of the task the new property belongs to.
- * @param values
- * The {@link ContentValues} to insert.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The row id of the new category as long
- */
- @Override
- public long insert(SQLiteDatabase db, long taskId, ContentValues values, boolean isSyncAdapter)
- {
- values = validateValues(db, taskId, -1, true, values, isSyncAdapter);
- values = getOrInsertCategory(db, values);
-
- // insert property row and create relation
- long id = super.insert(db, taskId, values, isSyncAdapter);
- insertRelation(db, taskId, values.getAsLong(Category.CATEGORY_ID), id);
-
- // update FTS entry with category name
- updateFTSEntry(db, taskId, id, values.getAsString(Category.CATEGORY_NAME));
- return id;
- }
-
-
- /**
- * Updates the category in the database.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * The id of the task this property belongs to.
- * @param propertyId
- * The id of the property.
- * @param values
- * The {@link ContentValues} to update.
- * @param oldValues
- * A {@link Cursor} pointing to the old values in the database.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The number of rows affected.
- */
- @Override
- public int update(SQLiteDatabase db, long taskId, long propertyId, ContentValues values, Cursor oldValues, boolean isSyncAdapter)
- {
- values = validateValues(db, taskId, propertyId, false, values, isSyncAdapter);
- values = getOrInsertCategory(db, values);
-
- if (values.containsKey(Category.CATEGORY_NAME))
- {
- // update FTS entry with new category name
- updateFTSEntry(db, taskId, propertyId, values.getAsString(Category.CATEGORY_NAME));
- }
-
- return super.update(db, taskId, propertyId, values, oldValues, isSyncAdapter);
- }
-
-
- /**
- * Check if a category with matching {@link ContentValues} exists and returns the existing category or creates a new category in the database.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param values
- * The {@link ContentValues} of the category.
- *
- * @return The {@link ContentValues} of the existing or new category.
- */
- private ContentValues getOrInsertCategory(SQLiteDatabase db, ContentValues values)
- {
- if (values.getAsBoolean(IS_NEW_CATEGORY))
- {
- // insert new category in category table
- ContentValues newCategoryValues = new ContentValues(4);
- newCategoryValues.put(Categories.ACCOUNT_NAME, values.getAsString(Categories.ACCOUNT_NAME));
- newCategoryValues.put(Categories.ACCOUNT_TYPE, values.getAsString(Categories.ACCOUNT_TYPE));
- newCategoryValues.put(Categories.NAME, values.getAsString(Category.CATEGORY_NAME));
- newCategoryValues.put(Categories.COLOR, values.getAsInteger(Category.CATEGORY_COLOR));
-
- long categoryID = db.insert(Tables.CATEGORIES, "", newCategoryValues);
- values.put(Category.CATEGORY_ID, categoryID);
- }
-
- // remove redundant values
- values.remove(IS_NEW_CATEGORY);
- values.remove(Categories.ACCOUNT_NAME);
- values.remove(Categories.ACCOUNT_TYPE);
-
- return values;
- }
-
-
- /**
- * Inserts a relation entry in the database to link task and category.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * The row id of the task.
- * @param categoryId
- * The row id of the category.
- *
- * @return The row id of the inserted relation.
- */
- private long insertRelation(SQLiteDatabase db, long taskId, long categoryId, long propertyId)
- {
- ContentValues relationValues = new ContentValues(3);
- relationValues.put(CategoriesMapping.TASK_ID, taskId);
- relationValues.put(CategoriesMapping.CATEGORY_ID, categoryId);
- relationValues.put(CategoriesMapping.PROPERTY_ID, propertyId);
- return db.insert(Tables.CATEGORIES_MAPPING, "", relationValues);
- }
-}
diff --git a/provider/src/main/java/org/dmfs/provider/tasks/handler/DefaultPropertyHandler.java b/provider/src/main/java/org/dmfs/provider/tasks/handler/DefaultPropertyHandler.java
deleted file mode 100644
index 52d32d3..0000000
--- a/provider/src/main/java/org/dmfs/provider/tasks/handler/DefaultPropertyHandler.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2017 dmfs GmbH
- *
- * 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 org.dmfs.provider.tasks.handler;
-
-import android.content.ContentValues;
-import android.database.sqlite.SQLiteDatabase;
-
-
-/**
- * This class is used to handle properties with unknown / unsupported mime-types.
- *
- * @author Tobias Reinsch isNew is false. If isNew is true this value is ignored.
- * @param isNew
- * Indicates that the content is new and not an update.
- * @param values
- * The {@link ContentValues} to validate.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The valid {@link ContentValues}.
- *
- * @throws IllegalArgumentException
- * if the {@link ContentValues} are invalid.
- */
- public abstract ContentValues validateValues(SQLiteDatabase db, long taskId, long propertyId, boolean isNew, ContentValues values, boolean isSyncAdapter);
-
-
- /**
- * Inserts the property {@link ContentValues} into the database.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * The id of the task the new property belongs to.
- * @param values
- * The {@link ContentValues} to insert.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The row id of the new property as long
- */
- public long insert(SQLiteDatabase db, long taskId, ContentValues values, boolean isSyncAdapter)
- {
- return db.insert(Tables.PROPERTIES, "", values);
- }
-
-
- /**
- * Updates the property {@link ContentValues} in the database.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * The id of the task this property belongs to.
- * @param propertyId
- * The id of the property.
- * @param values
- * The {@link ContentValues} to update.
- * @param oldValues
- * A {@link Cursor} pointing to the old values in the database.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return The number of rows affected.
- */
- public int update(SQLiteDatabase db, long taskId, long propertyId, ContentValues values, Cursor oldValues, boolean isSyncAdapter)
- {
- return db.update(Tables.PROPERTIES, values, Properties.PROPERTY_ID + "=" + propertyId, null);
- }
-
-
- /**
- * Deletes the property in the database.
- *
- * @param db
- * The belonging database.
- * @param taskId
- * The id of the task this property belongs to.
- * @param propertyId
- * The id of the property.
- * @param oldValues
- * A {@link Cursor} pointing to the old values in the database.
- * @param isSyncAdapter
- * Indicates that the transaction was triggered from a SyncAdapter.
- *
- * @return
- */
- public int delete(SQLiteDatabase db, long taskId, long propertyId, Cursor oldValues, boolean isSyncAdapter)
- {
- return db.delete(Tables.PROPERTIES, Properties.PROPERTY_ID + "=" + propertyId, null);
-
- }
-
-
- /**
- * Method hook to insert FTS entries on database migration.
- *
- * @param db
- * The {@link SQLiteDatabase}.
- * @param taskId
- * the row id of the task this property belongs to
- * @param propertyId
- * the id of the property
- * @param text
- * the searchable text of the property. If the property has multiple text snippets to search in, concat them separated by a space.
- */
- protected void updateFTSEntry(SQLiteDatabase db, long taskId, long propertyId, String text)
- {
- FTSDatabaseHelper.updatePropertyFTSEntry(db, taskId, propertyId, text);
- }
-
-
- public ContentValues cloneForNewTask(long newTaskId, ContentValues values)
- {
- ContentValues newValues = new ContentValues(values);
- newValues.remove(Properties.PROPERTY_ID);
- newValues.put(Properties.TASK_ID, newTaskId);
- return newValues;
- }
-
-
- ;
-}
diff --git a/provider/src/main/java/org/dmfs/provider/tasks/handler/PropertyHandlerFactory.java b/provider/src/main/java/org/dmfs/provider/tasks/handler/PropertyHandlerFactory.java
deleted file mode 100644
index 0e19463..0000000
--- a/provider/src/main/java/org/dmfs/provider/tasks/handler/PropertyHandlerFactory.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2017 dmfs GmbH
- *
- * 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 org.dmfs.provider.tasks.handler;
-
-import org.dmfs.tasks.contract.TaskContract.Property.Alarm;
-import org.dmfs.tasks.contract.TaskContract.Property.Category;
-import org.dmfs.tasks.contract.TaskContract.Property.Relation;
-
-
-/**
- * A factory that creates the matching {@link PropertyHandler} for the given mimetype.
- *
- * @author Tobias Reinsch null
- */
- public static PropertyHandler get(String mimeType)
- {
- if (Category.CONTENT_ITEM_TYPE.equals(mimeType))
- {
- return CATEGORY_HANDLER;
- }
- if (Alarm.CONTENT_ITEM_TYPE.equals(mimeType))
- {
- return ALARM_HANDLER;
- }
- if (Relation.CONTENT_ITEM_TYPE.equals(mimeType))
- {
- return RELATION_HANDLER;
- }
- return DEFAULT_PROPERTY_HANDLER;
- }
-}
diff --git a/provider/src/main/java/org/dmfs/provider/tasks/handler/RelationHandler.java b/provider/src/main/java/org/dmfs/provider/tasks/handler/RelationHandler.java
deleted file mode 100644
index 8f896c5..0000000
--- a/provider/src/main/java/org/dmfs/provider/tasks/handler/RelationHandler.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright 2017 dmfs GmbH
- *
- * 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 org.dmfs.provider.tasks.handler;
-
-import android.annotation.SuppressLint;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-
-import org.dmfs.provider.tasks.TaskDatabaseHelper;
-import org.dmfs.tasks.contract.TaskContract.Property.Relation;
-import org.dmfs.tasks.contract.TaskContract.Tasks;
-
-
-/**
- * Handles any inserts, updates and deletes on the relations table.
- *
- * @author Marten Gajda _id or _uid, depending of which value is given.
- * -1 if the entity has not been stored yet.
- *
- * @return The entity row id or -1.
- */
- long id();
-
- /**
- * Returns the {@link Uri} of the entity using the given authority.
- *
- * @param authority
- * The authority of this provider.
- *
- * @return A {@link Uri} or null if this entity has not been stored yet.
- */
- Uri uri(String authority);
-
- /**
- * Returns the value identified by the given {@link FieldAdapter}.
- *
- * @param fieldAdapter
- * The {@link FieldAdapter} of the value to return.
- *
- * @return The value, maybe be null.
- */
- null.
- */
- true if the field has been overridden, false otherwise.
- */
- boolean isUpdated(FieldAdapter, EntityType> fieldAdapter);
-
- /**
- * Returns whether this adapter supports modifying values.
- *
- * @return true if the task values can be changed by this adapter, false otherwise.
- */
- boolean isWriteable();
-
- /**
- * Returns whether any value has been modified.
- *
- * @return true if there are modified values, false otherwise.
- */
- boolean hasUpdates();
-
- /**
- * Sets a value of the adapted entity. The value is identified by a {@link FieldAdapter}.
- *
- * @param fieldAdapter
- * The {@link FieldAdapter} of the value to set.
- * @param value
- * The new value.
- */
- 0 if no fields have been changed.
- */
- int commit(SQLiteDatabase db);
-
- /**
- * Return the value of a temporary state field. The state of an entity is not committed to the database, it's only bound to the instances of this
- * {@link EntityAdapter} and will be lost once it gets garbage collected.
- *
- * @param stateFieldAdater
- * The {@link FieldAdapter} of a state field.
- *
- * @return The value of the state field.
- */
-