From e94d230d1bda237fb3b11873908011432ef12fcc Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Wed, 17 Jun 2020 14:05:09 -0500 Subject: [PATCH] Convert Astrid2TaskProvider to Kotlin --- .../astrid/provider/Astrid2TaskProvider.java | 246 ------------------ .../astrid/provider/Astrid2TaskProvider.kt | 208 +++++++++++++++ 2 files changed, 208 insertions(+), 246 deletions(-) delete mode 100644 app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java create mode 100644 app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.kt diff --git a/app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java b/app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java deleted file mode 100644 index 14deba101..000000000 --- a/app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright (c) 2012 Todoroo Inc - * - * See the file "LICENSE" for the full license governing this code. - */ - -package com.todoroo.astrid.provider; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.Context; -import android.content.UriMatcher; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; -import androidx.annotation.ColorRes; -import androidx.annotation.NonNull; -import com.google.common.base.Joiner; -import com.todoroo.astrid.dao.TaskDao; -import com.todoroo.astrid.data.Task; -import dagger.hilt.EntryPoint; -import dagger.hilt.InstallIn; -import dagger.hilt.android.EntryPointAccessors; -import dagger.hilt.android.components.ApplicationComponent; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.List; -import org.tasks.BuildConfig; -import org.tasks.R; -import org.tasks.analytics.Firebase; -import org.tasks.data.TagDao; -import org.tasks.data.TagData; -import org.tasks.data.TagDataDao; -import timber.log.Timber; - -/** - * This is the legacy Astrid task provider. While it will continue to be supported, note that it - * does not expose all of the information in Astrid, nor does it support many editing operations. - * - *

See the individual methods for a description of what is returned. - * - * @author Tim Su - */ -public class Astrid2TaskProvider extends ContentProvider { - - @EntryPoint - @InstallIn(ApplicationComponent.class) - public interface Astrid2TaskProviderEntryPoint { - TagDataDao getTagDataDao(); - TaskDao getTaskDao(); - TagDao getTagDao(); - Firebase getFirebase(); - } - - private static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".tasksprovider"; - - private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); - - private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); - - private static final String NAME = "name"; - private static final String IMPORTANCE_COLOR = "importance_color"; - private static final String IDENTIFIER = "identifier"; - private static final String PREFERRED_DUE_DATE = "preferredDueDate"; - private static final String DEFINITE_DUE_DATE = "definiteDueDate"; - private static final String IMPORTANCE = "importance"; - private static final String ID = "id"; - - private static final String TAGS_ID = "tags_id"; - - private static final String[] TASK_FIELD_LIST = - new String[] { - NAME, - IMPORTANCE_COLOR, - PREFERRED_DUE_DATE, - DEFINITE_DUE_DATE, - IMPORTANCE, - IDENTIFIER, - TAGS_ID - }; - - private static final String[] TAGS_FIELD_LIST = new String[] {ID, NAME}; - - private static final int URI_TASKS = 0; - private static final int URI_TAGS = 1; - - private static final String TAG_SEPARATOR = "|"; - - static { - URI_MATCHER.addURI(AUTHORITY, "tasks", URI_TASKS); - URI_MATCHER.addURI(AUTHORITY, "tags", URI_TAGS); - } - - public static void notifyDatabaseModification(Context context) { - try { - context.getContentResolver().notifyChange(CONTENT_URI, null); - } catch (Exception e) { - Timber.e(e); - } - } - - @Override - public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public String getType(@NonNull Uri uri) { - return null; - } - - @Override - public Uri insert(@NonNull Uri uri, ContentValues values) { - return null; - } - - @Override - public boolean onCreate() { - return true; - } - - private Astrid2TaskProviderEntryPoint hilt() { - return EntryPointAccessors.fromApplication( - getContext().getApplicationContext(), Astrid2TaskProviderEntryPoint.class); - } - - /** - * Note: tag id is no longer a real column, so we pass in a UID generated from the tag string. - * - * @return two-column cursor: tag id (string) and tag name - */ - private Cursor getTags() { - List tags = hilt().getTagDataDao().tagDataOrderedByName(); - - MatrixCursor ret = new MatrixCursor(TAGS_FIELD_LIST); - - for (TagData tag : tags) { - Object[] values = new Object[2]; - values[0] = tagNameToLong(tag.getName()); - values[1] = tag.getName(); - - ret.addRow(values); - } - - return ret; - } - - private long tagNameToLong(String tag) { - MessageDigest m; - try { - m = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e) { - Timber.e(e); - return -1; - } - - m.update(tag.getBytes(), 0, tag.length()); - return new BigInteger(1, m.digest()).longValue(); - } - - /** - * Cursor with the following columns - * - *

    - *
  1. task title, string - *
  2. task importance color, int android RGB color - *
  3. task due date (was: preferred due date), long millis since epoch - *
  4. task due date (was: absolute due date), long millis since epoch - *
  5. task importance, integer from 0 to 3 (0 => most important) - *
  6. task id, long - *
  7. task tags, string tags separated by | - *
- * - * @return cursor as described above - */ - private Cursor getTasks() { - Astrid2TaskProviderEntryPoint hilt = hilt(); - hilt.getFirebase().logEvent(R.string.event_query_legacy_content_provider); - List tasks = hilt.getTaskDao().getAstrid2TaskProviderTasks(); - MatrixCursor ret = new MatrixCursor(TASK_FIELD_LIST); - for (Task task : tasks) { - String taskTags = getTagsAsString(task.getId(), TAG_SEPARATOR); - - Object[] values = new Object[7]; - values[0] = task.getTitle(); - values[1] = getPriorityColor(getContext(), task.getPriority()); - values[2] = task.getDueDate(); - values[3] = task.getDueDate(); - values[4] = task.getPriority(); - values[5] = task.getId(); - values[6] = taskTags; - - ret.addRow(values); - } - return ret; - } - - private static int getPriorityColor(Context context, int priority) { - return context.getColor(getPriorityResId(priority)); - } - - @ColorRes private static int getPriorityResId(int priority) { - if (priority <= 0) { - return R.color.red_500; - } else if (priority == 1) { - return R.color.amber_500; - } else if (priority == 2) { - return R.color.blue_500; - } else { - return R.color.grey_500; - } - } - - @Override - public Cursor query( - @NonNull Uri uri, - String[] projection, - String selection, - String[] selectionArgs, - String sortOrder) { - switch (URI_MATCHER.match(uri)) { - case URI_TASKS: - return getTasks(); - case URI_TAGS: - return getTags(); - default: - throw new IllegalStateException("Unrecognized URI:" + uri); - } - } - - @Override - public int update( - @NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("not supported"); - } - - /** - * Return tags as a list of strings separated by given separator - * - * @return empty string if no tags, otherwise string - */ - private String getTagsAsString(long taskId, String separator) { - return Joiner.on(separator).join(hilt().getTagDao().getTagNames(taskId)); - } -} diff --git a/app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.kt b/app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.kt new file mode 100644 index 000000000..239c35fda --- /dev/null +++ b/app/src/main/java/com/todoroo/astrid/provider/Astrid2TaskProvider.kt @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2012 Todoroo Inc + * + * See the file "LICENSE" for the full license governing this code. + */ +package com.todoroo.astrid.provider + +import android.content.ContentProvider +import android.content.ContentValues +import android.content.Context +import android.content.UriMatcher +import android.database.Cursor +import android.database.MatrixCursor +import android.net.Uri +import androidx.annotation.ColorRes +import com.todoroo.astrid.dao.TaskDao +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.android.EntryPointAccessors +import dagger.hilt.android.components.ApplicationComponent +import org.tasks.BuildConfig +import org.tasks.R +import org.tasks.analytics.Firebase +import org.tasks.data.TagDao +import org.tasks.data.TagDataDao +import timber.log.Timber +import java.math.BigInteger +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException + +/** + * This is the legacy Astrid task provider. While it will continue to be supported, note that it + * does not expose all of the information in Astrid, nor does it support many editing operations. + * + * + * See the individual methods for a description of what is returned. + * + * @author Tim Su @todoroo.com> + */ +class Astrid2TaskProvider : ContentProvider() { + @EntryPoint + @InstallIn(ApplicationComponent::class) + interface Astrid2TaskProviderEntryPoint { + val tagDataDao: TagDataDao + val taskDao: TaskDao + val tagDao: TagDao + val firebase: Firebase + } + + companion object { + private const val AUTHORITY = BuildConfig.APPLICATION_ID + ".tasksprovider" + private val CONTENT_URI = Uri.parse("content://$AUTHORITY") + private val URI_MATCHER = UriMatcher(UriMatcher.NO_MATCH) + private const val NAME = "name" + private const val IMPORTANCE_COLOR = "importance_color" + private const val IDENTIFIER = "identifier" + private const val PREFERRED_DUE_DATE = "preferredDueDate" + private const val DEFINITE_DUE_DATE = "definiteDueDate" + private const val IMPORTANCE = "importance" + private const val ID = "id" + private const val TAGS_ID = "tags_id" + private val TASK_FIELD_LIST = arrayOf( + NAME, + IMPORTANCE_COLOR, + PREFERRED_DUE_DATE, + DEFINITE_DUE_DATE, + IMPORTANCE, + IDENTIFIER, + TAGS_ID + ) + private val TAGS_FIELD_LIST = arrayOf(ID, NAME) + private const val URI_TASKS = 0 + private const val URI_TAGS = 1 + private const val TAG_SEPARATOR = "|" + @JvmStatic + fun notifyDatabaseModification(context: Context) { + try { + context.contentResolver.notifyChange(CONTENT_URI, null) + } catch (e: Exception) { + Timber.e(e) + } + } + + private fun getPriorityColor(context: Context?, priority: Int): Int { + return context!!.getColor(getPriorityResId(priority)) + } + + @ColorRes + private fun getPriorityResId(priority: Int): Int { + return when { + priority <= 0 -> R.color.red_500 + priority == 1 -> R.color.amber_500 + priority == 2 -> R.color.blue_500 + else -> R.color.grey_500 + } + } + + init { + URI_MATCHER.addURI(AUTHORITY, "tasks", URI_TASKS) + URI_MATCHER.addURI(AUTHORITY, "tags", URI_TAGS) + } + } + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?) = 0 + + override fun getType(uri: Uri): String? = null + + override fun insert(uri: Uri, values: ContentValues?): Uri? = null + + override fun onCreate() = true + + private fun hilt(): Astrid2TaskProviderEntryPoint { + return EntryPointAccessors.fromApplication( + context!!.applicationContext, Astrid2TaskProviderEntryPoint::class.java) + } + + /** + * Note: tag id is no longer a real column, so we pass in a UID generated from the tag string. + * + * @return two-column cursor: tag id (string) and tag name + */ + private val tags: Cursor + get() { + val tags = hilt().tagDataDao.tagDataOrderedByName() + val ret = MatrixCursor(TAGS_FIELD_LIST) + for (tag in tags) { + val values = arrayOfNulls(2) + values[0] = tagNameToLong(tag.name) + values[1] = tag.name + ret.addRow(values) + } + return ret + } + + private fun tagNameToLong(tag: String?): Long { + val m: MessageDigest + m = try { + MessageDigest.getInstance("MD5") + } catch (e: NoSuchAlgorithmException) { + Timber.e(e) + return -1 + } + m.update(tag!!.toByteArray(), 0, tag.length) + return BigInteger(1, m.digest()).toLong() + } + + /** + * Cursor with the following columns + * + * + * 1. task title, string + * 1. task importance color, int android RGB color + * 1. task due date (was: preferred due date), long millis since epoch + * 1. task due date (was: absolute due date), long millis since epoch + * 1. task importance, integer from 0 to 3 (0 => most important) + * 1. task id, long + * 1. task tags, string tags separated by | + * + * + * @return cursor as described above + */ + private val tasks: Cursor + get() { + val hilt = hilt() + hilt.firebase.logEvent(R.string.event_query_legacy_content_provider) + val tasks = hilt.taskDao.getAstrid2TaskProviderTasks() + val ret = MatrixCursor(TASK_FIELD_LIST) + for (task in tasks) { + val taskTags = getTagsAsString(task.id, TAG_SEPARATOR) + val values = arrayOfNulls(7) + values[0] = task.title + values[1] = getPriorityColor(context, task.priority) + values[2] = task.dueDate + values[3] = task.dueDate + values[4] = task.priority + values[5] = task.id + values[6] = taskTags + ret.addRow(values) + } + return ret + } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String?): Cursor? { + return when (URI_MATCHER.match(uri)) { + URI_TASKS -> tasks + URI_TAGS -> tags + else -> throw IllegalStateException("Unrecognized URI:$uri") + } + } + + override fun update( + uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int { + throw UnsupportedOperationException("not supported") + } + + /** + * Return tags as a list of strings separated by given separator + * + * @return empty string if no tags, otherwise string + */ + private fun getTagsAsString(taskId: Long, separator: String) = + hilt().tagDao.getTagNames(taskId).joinToString(separator) +} \ No newline at end of file