Merge remote-tracking branch 'origin/130208_sb_task_attachments' into 130122_sb_new_sync

pull/14/head
Sam Bosley 13 years ago
commit ee0b5ddae4

@ -10,6 +10,7 @@ import java.io.BufferedReader;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -153,6 +154,32 @@ public class AndroidUtilities {
return BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length); return BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length);
} }
private static final int BUFFER_SIZE = 4096;
public static String encodeBase64File(File file) {
try {
FileInputStream in = new FileInputStream(file);
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[BUFFER_SIZE];
int len = 0;
while ((len = in.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
byte[] bytes = baos.toByteArray();
return Base64.encodeToString(bytes, Base64.DEFAULT);
} finally {
in.close();
}
} catch (FileNotFoundException e) {
return null;
} catch (IOException e) {
return null;
}
}
/** /**
* Start the given intent, handling security exceptions if they arise * Start the given intent, handling security exceptions if they arise
* *

@ -0,0 +1,193 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.data;
import android.content.ContentValues;
import android.net.Uri;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.IntegerProperty;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.api.AstridApiConstants;
/**
* Data Model which represents a user.
*
* @author Tim Su <tim@todoroo.com>
*
*/
@SuppressWarnings("nls")
public final class TaskAttachment extends RemoteModel {
// --- table and uri
/** table for this model */
public static final Table TABLE = new Table("task_attachments", TaskAttachment.class);
/** model class for entries in the outstanding table */
public static final Class<? extends OutstandingEntry<TaskAttachment>> OUTSTANDING_MODEL = TaskAttachmentOutstanding.class;
/** content uri for this model */
public static final Uri CONTENT_URI = Uri.parse("content://" + AstridApiConstants.API_PACKAGE + "/" +
TABLE.name);
// --- properties
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
/** Remote id */
public static final StringProperty UUID = new StringProperty(
TABLE, UUID_PROPERTY_NAME);
/** Pushed at date */
public static final LongProperty PUSHED_AT = new LongProperty(
TABLE, PUSHED_AT_PROPERTY_NAME);
/** Creator user id */
public static final StringProperty USER_UUID = new StringProperty(
TABLE, "user_id");
/** Task uuid */
public static final StringProperty TASK_UUID = new StringProperty(
TABLE, "task_id");
/** File name */
public static final StringProperty NAME = new StringProperty(
TABLE, "name");
/** File url (for downloading) */
public static final StringProperty URL = new StringProperty(
TABLE, "url");
/** File path (on local storage) */
public static final StringProperty FILE_PATH = new StringProperty(
TABLE, "path");
/** File size (in bytes) */
public static final IntegerProperty SIZE = new IntegerProperty(
TABLE, "size");
/** File mimetype */
public static final StringProperty CONTENT_TYPE = new StringProperty(
TABLE, "content_type");
/** Attachment creation date */
public static final LongProperty CREATED_AT = new LongProperty(
TABLE, "created_at", Property.PROP_FLAG_DATE);
/** Attachment deletion date */
public static final LongProperty DELETED_AT = new LongProperty(
TABLE, "deleted_at", Property.PROP_FLAG_DATE);
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(TaskAttachment.class);
// --- defaults
/** Default values container */
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(UUID.name, NO_UUID);
defaultValues.put(PUSHED_AT.name, 0);
defaultValues.put(USER_UUID.name, NO_UUID);
defaultValues.put(TASK_UUID.name, NO_UUID);
defaultValues.put(NAME.name, "");
defaultValues.put(URL.name, "");
defaultValues.put(FILE_PATH.name, "");
defaultValues.put(SIZE.name, 0);
defaultValues.put(CONTENT_TYPE.name, "");
defaultValues.put(CREATED_AT.name, 0);
defaultValues.put(DELETED_AT.name, 0);
}
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
// -- Constants
/** default directory for files on external storage */
public static final String FILES_DIRECTORY_DEFAULT = "attachments"; //$NON-NLS-1$
/** preference key for some other download directory */
public static final String FILES_DIRECTORY_PREF = "custom_files_dir"; //$NON-NLS-1$
/** Constants for file types */
public static final String FILE_TYPE_AUDIO = "audio/"; //$NON-NLS-1$
public static final String FILE_TYPE_IMAGE = "image/"; //$NON-NLS-1$
public static final String FILE_TYPE_PDF = "application/pdf"; //$NON-NLS-1$
public static final String FILE_TYPE_DOC = "application/msword"; //$NON-NLS-1$
public static final String FILE_TYPE_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; //$NON-NLS-1$
public static final String FILE_TYPE_PPT = "application/vnd.ms-powerpoint"; //$NON-NLS-1$
public static final String FILE_TYPE_PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; //$NON-NLS-1$
public static final String FILE_TYPE_XLS = "application/vnd.ms-excel"; //$NON-NLS-1$
public static final String FILE_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; //$NON-NLS-1$
public static final String[] MS_FILETYPES = {
FILE_TYPE_DOC, FILE_TYPE_DOCX,
FILE_TYPE_XLS, FILE_TYPE_XLSX,
FILE_TYPE_PPT, FILE_TYPE_PPTX,
};
public static final String FILE_TYPE_OTHER = "application/octet-stream"; //$NON-NLS-1$
public static TaskAttachment createNewAttachment(String taskUuid, String filePath, String fileName, String fileType) {
TaskAttachment attachment = new TaskAttachment();
attachment.setValue(TaskAttachment.TASK_UUID, taskUuid);
attachment.setValue(NAME, fileName);
attachment.setValue(USER_UUID, Task.USER_ID_SELF);
attachment.setValue(FILE_PATH, filePath);
attachment.setValue(CONTENT_TYPE, fileType);
attachment.setValue(CREATED_AT, DateUtilities.now());
attachment.setValue(DELETED_AT, 0L);
return attachment;
}
// --- data access boilerplate
public TaskAttachment() {
super();
}
public TaskAttachment(TodorooCursor<TaskAttachment> cursor) {
this();
readPropertiesFromCursor(cursor);
}
public void readFromCursor(TodorooCursor<TaskAttachment> cursor) {
super.readPropertiesFromCursor(cursor);
}
@Override
public long getId() {
return getIdHelper(ID);
}
@Override
public String getUuid() {
return getUuidHelper(UUID);
}
// --- parcelable helpers
public static final Creator<TaskAttachment> CREATOR = new ModelCreator<TaskAttachment>(TaskAttachment.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}

@ -0,0 +1,63 @@
package com.todoroo.astrid.data;
import android.content.ContentValues;
import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.Table;
@SuppressWarnings("nls")
public class TaskAttachmentOutstanding extends OutstandingEntry<TaskAttachment> {
/** table for this model */
public static final Table TABLE = new Table("task_attachment_outstanding", TaskAttachmentOutstanding.class);
// --- properties
/** ID */
public static final LongProperty ID = new LongProperty(
TABLE, ID_PROPERTY_NAME);
public static final LongProperty TASK_ATTACHMENT_ID = new LongProperty(
TABLE, ENTITY_ID_PROPERTY_NAME);
public static final StringProperty COLUMN_STRING = new StringProperty(
TABLE, COLUMN_STRING_PROPERTY_NAME);
public static final StringProperty VALUE_STRING = new StringProperty(
TABLE, VALUE_STRING_PROPERTY_NAME);
public static final LongProperty CREATED_AT = new LongProperty(
TABLE, CREATED_AT_PROPERTY_NAME);
private static final ContentValues defaultValues = new ContentValues();
static {
defaultValues.put(TASK_ATTACHMENT_ID.name, 0);
defaultValues.put(COLUMN_STRING.name, "");
defaultValues.put(VALUE_STRING.name, "");
}
/** List of all properties for this model */
public static final Property<?>[] PROPERTIES = generateProperties(TaskAttachmentOutstanding.class);
@Override
public ContentValues getDefaultValues() {
return defaultValues;
}
@Override
public long getId() {
return getIdHelper(ID);
}
public static final Creator<TaskAttachmentOutstanding> CREATOR = new ModelCreator<TaskAttachmentOutstanding>(TaskAttachmentOutstanding.class);
@Override
protected Creator<? extends AbstractModel> getCreator() {
return CREATOR;
}
}

@ -5,18 +5,13 @@
*/ */
package com.todoroo.astrid.actfm.sync; package com.todoroo.astrid.actfm.sync;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
@ -26,11 +21,8 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.timsu.astrid.GCMIntentService; import com.timsu.astrid.GCMIntentService;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService; import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.Preferences; import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.billing.BillingConstants; import com.todoroo.astrid.billing.BillingConstants;
@ -38,13 +30,9 @@ import com.todoroo.astrid.dao.MetadataDao;
import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.UserDao; import com.todoroo.astrid.dao.UserDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.MetadataApiDao.MetadataCriteria;
import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
import com.todoroo.astrid.files.FileMetadata;
import com.todoroo.astrid.gtasks.GtasksPreferenceService; import com.todoroo.astrid.gtasks.GtasksPreferenceService;
import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.service.TagDataService;
@ -290,99 +278,99 @@ public final class ActFmSyncService {
handleException("fetch-tag-order-io", e); handleException("fetch-tag-order-io", e);
} }
} }
//
public void pushAttachmentInBackground(final Metadata fileMetadata) { // public void pushAttachmentInBackground(final Metadata fileMetadata) {
if (!ActFmPreferenceService.isPremiumUser()) // if (!ActFmPreferenceService.isPremiumUser())
return; // return;
new Thread(new Runnable() { // new Thread(new Runnable() {
@Override // @Override
public void run() { // public void run() {
waitUntilEmpty.close(); // waitUntilEmpty.close();
taskPushThreads.incrementAndGet(); // taskPushThreads.incrementAndGet();
try { // try {
Task t = taskDao.fetch(fileMetadata.getValue(Metadata.TASK), Task.UUID); // Task t = taskDao.fetch(fileMetadata.getValue(Metadata.TASK), Task.UUID);
if (t == null || !RemoteModel.isValidUuid(t.getUuid())) // if (t == null || !RemoteModel.isValidUuid(t.getUuid()))
return; // return;
if (fileMetadata.getValue(FileMetadata.DELETION_DATE) > 0) // if (fileMetadata.getValue(FileMetadata.DELETION_DATE) > 0)
deleteAttachment(fileMetadata); // deleteAttachment(fileMetadata);
else // else
pushAttachment(t.getValue(Task.UUID), fileMetadata); // pushAttachment(t.getValue(Task.UUID), fileMetadata);
} finally { // } finally {
if (taskPushThreads.decrementAndGet() == 0) { // if (taskPushThreads.decrementAndGet() == 0) {
waitUntilEmpty.open(); // waitUntilEmpty.open();
} // }
} // }
} // }
}).start(); // }).start();
} // }
//
/** // /**
* Push a file attachment to the server // * Push a file attachment to the server
* @param remoteTaskId // * @param remoteTaskId
* @param fileMetadata // * @param fileMetadata
*/ // */
public void pushAttachment(String remoteTaskId, Metadata fileMetadata) { // public void pushAttachment(String remoteTaskId, Metadata fileMetadata) {
if (!ActFmPreferenceService.isPremiumUser()) // if (!ActFmPreferenceService.isPremiumUser())
return; // return;
//
if (!fileMetadata.containsNonNullValue(FileMetadata.FILE_PATH) || !RemoteModel.isValidUuid(remoteTaskId)) // if (!fileMetadata.containsNonNullValue(FileMetadata.FILE_PATH) || !RemoteModel.isValidUuid(remoteTaskId))
return; // return;
//
File f = new File(fileMetadata.getValue(FileMetadata.FILE_PATH)); // File f = new File(fileMetadata.getValue(FileMetadata.FILE_PATH));
if (!f.exists()) // if (!f.exists())
return; // return;
//
if (!checkForToken()) // if (!checkForToken())
return; // return;
//
ArrayList<Object> params = new ArrayList<Object>(); // ArrayList<Object> params = new ArrayList<Object>();
params.add("task_id"); params.add(remoteTaskId); // params.add("task_id"); params.add(remoteTaskId);
params.add("token"); params.add(token); // params.add("token"); params.add(token);
//
try { // try {
MultipartEntity entity = new MultipartEntity(); // MultipartEntity entity = new MultipartEntity();
FileBody body = new FileBody(f, fileMetadata.getValue(FileMetadata.FILE_TYPE)); // FileBody body = new FileBody(f, fileMetadata.getValue(FileMetadata.FILE_TYPE));
entity.addPart("file", body); // entity.addPart("file", body);
//
JSONObject result = actFmInvoker.post("task_attachment_create", entity, // JSONObject result = actFmInvoker.post("task_attachment_create", entity,
params.toArray(new Object[params.size()])); // params.toArray(new Object[params.size()]));
//
fileMetadata.setValue(FileMetadata.REMOTE_ID, result.optLong("id")); // fileMetadata.setValue(FileMetadata.REMOTE_ID, result.optLong("id"));
fileMetadata.setValue(FileMetadata.URL, result.optString("url")); // fileMetadata.setValue(FileMetadata.URL, result.optString("url"));
metadataService.save(fileMetadata); // metadataService.save(fileMetadata);
} catch (ActFmServiceException e) { // } catch (ActFmServiceException e) {
handleException("push-attachment-error", e); // handleException("push-attachment-error", e);
} catch (IOException e) { // } catch (IOException e) {
handleException("push-attachment-error", e); // handleException("push-attachment-error", e);
} // }
} // }
//
public void deleteAttachment(Metadata fileMetadata) { // public void deleteAttachment(Metadata fileMetadata) {
long attachmentId = fileMetadata.getValue(FileMetadata.REMOTE_ID); // long attachmentId = fileMetadata.getValue(FileMetadata.REMOTE_ID);
if (attachmentId <= 0) // if (attachmentId <= 0)
return; // return;
//
if (!checkForToken()) // if (!checkForToken())
return; // return;
//
ArrayList<Object> params = new ArrayList<Object>(); // ArrayList<Object> params = new ArrayList<Object>();
params.add("id"); params.add(attachmentId); // params.add("id"); params.add(attachmentId);
params.add("token"); params.add(token); // params.add("token"); params.add(token);
//
try { // try {
JSONObject result = actFmInvoker.post("task_attachment_remove", null, params.toArray(new Object[params.size()])); // JSONObject result = actFmInvoker.post("task_attachment_remove", null, params.toArray(new Object[params.size()]));
if (result.optString("status").equals("success")) { // if (result.optString("status").equals("success")) {
metadataService.delete(fileMetadata); // metadataService.delete(fileMetadata);
} // }
} catch (ActFmServiceException e) { // } catch (ActFmServiceException e) {
if (e.result != null && e.result.optString("code").equals("not_found")) // if (e.result != null && e.result.optString("code").equals("not_found"))
metadataService.delete(fileMetadata); // metadataService.delete(fileMetadata);
else // else
handleException("push-attachment-error", e); // handleException("push-attachment-error", e);
} catch (IOException e) { // } catch (IOException e) {
handleException("push-attachment-error", e); // handleException("push-attachment-error", e);
} // }
} // }
// --- data fetch methods // --- data fetch methods
public int fetchFeaturedLists(int serverTime) throws JSONException, IOException { public int fetchFeaturedLists(int serverTime) throws JSONException, IOException {
@ -477,64 +465,64 @@ public final class ActFmSyncService {
parameters[i+2] = getParameters[i]; parameters[i+2] = getParameters[i];
return actFmInvoker.invoke(method, parameters); return actFmInvoker.invoke(method, parameters);
} }
//
// --- helpers // // --- helpers
private void synchronizeAttachments(JSONObject item, Task model) { // private void synchronizeAttachments(JSONObject item, Task model) {
TodorooCursor<Metadata> attachments = metadataService.query(Query.select(Metadata.PROPERTIES) // TodorooCursor<Metadata> attachments = metadataService.query(Query.select(Metadata.PROPERTIES)
.where(Criterion.and(MetadataCriteria.byTaskAndwithKey(model.getId(), // .where(Criterion.and(MetadataCriteria.byTaskAndwithKey(model.getId(),
FileMetadata.METADATA_KEY), FileMetadata.REMOTE_ID.gt(0)))); // FileMetadata.METADATA_KEY), FileMetadata.REMOTE_ID.gt(0))));
try { // try {
HashMap<Long, Metadata> currentFiles = new HashMap<Long, Metadata>(); // HashMap<Long, Metadata> currentFiles = new HashMap<Long, Metadata>();
for (attachments.moveToFirst(); !attachments.isAfterLast(); attachments.moveToNext()) { // for (attachments.moveToFirst(); !attachments.isAfterLast(); attachments.moveToNext()) {
Metadata m = new Metadata(attachments); // Metadata m = new Metadata(attachments);
currentFiles.put(m.getValue(FileMetadata.REMOTE_ID), m); // currentFiles.put(m.getValue(FileMetadata.REMOTE_ID), m);
} // }
//
JSONArray remoteFiles = item.getJSONArray("attachments"); // JSONArray remoteFiles = item.getJSONArray("attachments");
for (int i = 0; i < remoteFiles.length(); i++) { // for (int i = 0; i < remoteFiles.length(); i++) {
JSONObject file = remoteFiles.getJSONObject(i); // JSONObject file = remoteFiles.getJSONObject(i);
//
long id = file.optLong("id"); // long id = file.optLong("id");
if (currentFiles.containsKey(id)) { // if (currentFiles.containsKey(id)) {
// Match, make sure name and url are up to date, then remove from map // // Match, make sure name and url are up to date, then remove from map
Metadata fileMetadata = currentFiles.get(id); // Metadata fileMetadata = currentFiles.get(id);
fileMetadata.setValue(FileMetadata.URL, file.getString("url")); // fileMetadata.setValue(FileMetadata.URL, file.getString("url"));
fileMetadata.setValue(FileMetadata.NAME, file.getString("name")); // fileMetadata.setValue(FileMetadata.NAME, file.getString("name"));
metadataService.save(fileMetadata); // metadataService.save(fileMetadata);
currentFiles.remove(id); // currentFiles.remove(id);
} else { // } else {
// Create new file attachment // // Create new file attachment
Metadata newAttachment = FileMetadata.createNewFileMetadata(model.getId(), "", // Metadata newAttachment = FileMetadata.createNewFileMetadata(model.getId(), "",
file.getString("name"), file.getString("content_type")); // file.getString("name"), file.getString("content_type"));
String url = file.getString("url"); // String url = file.getString("url");
newAttachment.setValue(FileMetadata.URL, url); // newAttachment.setValue(FileMetadata.URL, url);
newAttachment.setValue(FileMetadata.REMOTE_ID, id); // newAttachment.setValue(FileMetadata.REMOTE_ID, id);
metadataService.save(newAttachment); // metadataService.save(newAttachment);
} // }
} // }
//
// Remove all the leftovers // // Remove all the leftovers
Set<Long> attachmentsToDelete = currentFiles.keySet(); // Set<Long> attachmentsToDelete = currentFiles.keySet();
for (Long remoteId : attachmentsToDelete) { // for (Long remoteId : attachmentsToDelete) {
Metadata toDelete = currentFiles.get(remoteId); // Metadata toDelete = currentFiles.get(remoteId);
String path = toDelete.getValue(FileMetadata.FILE_PATH); // String path = toDelete.getValue(FileMetadata.FILE_PATH);
if (TextUtils.isEmpty(path)) // if (TextUtils.isEmpty(path))
metadataService.delete(toDelete); // metadataService.delete(toDelete);
else { // else {
File f = new File(toDelete.getValue(FileMetadata.FILE_PATH)); // File f = new File(toDelete.getValue(FileMetadata.FILE_PATH));
if (!f.exists() || f.delete()) { // if (!f.exists() || f.delete()) {
metadataService.delete(toDelete); // metadataService.delete(toDelete);
} // }
//
} // }
} // }
//
} catch (JSONException e) { // } catch (JSONException e) {
e.printStackTrace(); // e.printStackTrace();
} finally { // } finally {
attachments.close(); // attachments.close();
} // }
} // }
protected void handleException(String message, Exception exception) { protected void handleException(String message, Exception exception) {

@ -32,6 +32,7 @@ import com.todoroo.astrid.dao.OutstandingEntryDao;
import com.todoroo.astrid.dao.RemoteModelDao; import com.todoroo.astrid.dao.RemoteModelDao;
import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TagOutstandingDao; import com.todoroo.astrid.dao.TagOutstandingDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskOutstandingDao; import com.todoroo.astrid.dao.TaskOutstandingDao;
import com.todoroo.astrid.dao.UserActivityDao; import com.todoroo.astrid.dao.UserActivityDao;
@ -41,6 +42,7 @@ import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.TagOutstanding; import com.todoroo.astrid.data.TagOutstanding;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.TaskOutstanding; import com.todoroo.astrid.data.TaskOutstanding;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
import com.todoroo.astrid.data.UserActivity; import com.todoroo.astrid.data.UserActivity;
@ -87,7 +89,8 @@ public class ActFmSyncThread {
public static enum ModelType { public static enum ModelType {
TYPE_TASK, TYPE_TASK,
TYPE_TAG, TYPE_TAG,
TYPE_ACTIVITY TYPE_ACTIVITY,
TYPE_ATTACHMENT
} }
private static volatile ActFmSyncThread instance; private static volatile ActFmSyncThread instance;
@ -96,14 +99,14 @@ public class ActFmSyncThread {
if (instance == null) { if (instance == null) {
synchronized(ActFmSyncThread.class) { synchronized(ActFmSyncThread.class) {
if (instance == null) { if (instance == null) {
initializeSyncComponents(PluginServices.getTaskDao(), PluginServices.getTagDataDao(), PluginServices.getUserActivityDao()); initializeSyncComponents(PluginServices.getTaskDao(), PluginServices.getTagDataDao(), PluginServices.getUserActivityDao(), PluginServices.getTaskAttachmentDao());
} }
} }
} }
return instance; return instance;
} }
public static ActFmSyncThread initializeSyncComponents(TaskDao taskDao, TagDataDao tagDataDao, UserActivityDao userActivityDao) { public static ActFmSyncThread initializeSyncComponents(TaskDao taskDao, TagDataDao tagDataDao, UserActivityDao userActivityDao, TaskAttachmentDao taskAttachmentDao) {
if (instance == null) { if (instance == null) {
synchronized(ActFmSyncThread.class) { synchronized(ActFmSyncThread.class) {
if (instance == null) { if (instance == null) {
@ -115,6 +118,7 @@ public class ActFmSyncThread {
taskDao.addListener(new SyncDatabaseListener<Task>(instance, ModelType.TYPE_TASK)); taskDao.addListener(new SyncDatabaseListener<Task>(instance, ModelType.TYPE_TASK));
tagDataDao.addListener(new SyncDatabaseListener<TagData>(instance, ModelType.TYPE_TAG)); tagDataDao.addListener(new SyncDatabaseListener<TagData>(instance, ModelType.TYPE_TAG));
userActivityDao.addListener(new SyncDatabaseListener<UserActivity>(instance, ModelType.TYPE_ACTIVITY)); userActivityDao.addListener(new SyncDatabaseListener<UserActivity>(instance, ModelType.TYPE_ACTIVITY));
taskAttachmentDao.addListener(new SyncDatabaseListener<TaskAttachment>(instance, ModelType.TYPE_ATTACHMENT));
instance.startSyncThread(); instance.startSyncThread();
} }

@ -17,6 +17,7 @@ import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.OutstandingEntryDao; import com.todoroo.astrid.dao.OutstandingEntryDao;
import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TagOutstandingDao; import com.todoroo.astrid.dao.TagOutstandingDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskOutstandingDao; import com.todoroo.astrid.dao.TaskOutstandingDao;
import com.todoroo.astrid.dao.UpdateDao; import com.todoroo.astrid.dao.UpdateDao;
@ -29,10 +30,12 @@ import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.TagOutstanding; import com.todoroo.astrid.data.TagOutstanding;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.TaskOutstanding; import com.todoroo.astrid.data.TaskOutstanding;
import com.todoroo.astrid.data.Update; import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
import com.todoroo.astrid.data.UserActivity; import com.todoroo.astrid.data.UserActivity;
import com.todoroo.astrid.files.FileMetadata;
import com.todoroo.astrid.helper.UUIDHelper; import com.todoroo.astrid.helper.UUIDHelper;
import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.MetadataService;
import com.todoroo.astrid.service.TagDataService; import com.todoroo.astrid.service.TagDataService;
@ -48,6 +51,7 @@ public class AstridNewSyncMigrator {
@Autowired private UpdateDao updateDao; @Autowired private UpdateDao updateDao;
@Autowired private UserActivityDao userActivityDao; @Autowired private UserActivityDao userActivityDao;
@Autowired private UserDao userDao; @Autowired private UserDao userDao;
@Autowired private TaskAttachmentDao taskAttachmentDao;
@Autowired private TaskOutstandingDao taskOutstandingDao; @Autowired private TaskOutstandingDao taskOutstandingDao;
@Autowired private TagOutstandingDao tagOutstandingDao; @Autowired private TagOutstandingDao tagOutstandingDao;
@ -131,9 +135,9 @@ public class AstridNewSyncMigrator {
Log.e(LOG_TAG, "Error asserting UUIDs", e); Log.e(LOG_TAG, "Error asserting UUIDs", e);
} }
// ---------- // --------------
// Migrate unsynced task comments to UserActivity table // Migrate unsynced task comments to UserActivity table
// ---------- // --------------
try { try {
TodorooCursor<Update> updates = updateDao.query(Query.select(Update.PROPERTIES).where( TodorooCursor<Update> updates = updateDao.query(Query.select(Update.PROPERTIES).where(
Criterion.and(Criterion.or(Update.UUID.eq(0), Update.UUID.isNull()), Criterion.or(Update.ACTION_CODE.eq(UserActivity.ACTION_TAG_COMMENT), Criterion.and(Criterion.or(Update.UUID.eq(0), Update.UUID.isNull()), Criterion.or(Update.ACTION_CODE.eq(UserActivity.ACTION_TAG_COMMENT),
@ -177,15 +181,58 @@ public class AstridNewSyncMigrator {
} }
// ---------- // --------------
// Drop any entries from the Users table that don't have a UUID // Drop any entries from the Users table that don't have a UUID
// ---------- // --------------
try { try {
userDao.deleteWhere(Criterion.or(User.UUID.isNull(), User.UUID.eq(""), User.UUID.eq("0"))); userDao.deleteWhere(Criterion.or(User.UUID.isNull(), User.UUID.eq(""), User.UUID.eq("0")));
} catch (Exception e) { } catch (Exception e) {
Log.e(LOG_TAG, "Error deleting incomplete user entries", e); Log.e(LOG_TAG, "Error deleting incomplete user entries", e);
} }
// --------------
// Migrate legacy FileMetadata models to new TaskAttachment models
// --------------
try {
TodorooCursor<Metadata> fmCursor = metadataService.query(Query.select(Metadata.PROPERTIES)
.where(MetadataCriteria.withKey(FileMetadata.METADATA_KEY)));
try {
Metadata m = new Metadata();
for (fmCursor.moveToFirst(); !fmCursor.isAfterLast(); fmCursor.moveToNext()) {
m.clear();
m.readFromCursor(fmCursor);
TaskAttachment attachment = new TaskAttachment();
Task task = taskDao.fetch(m.getValue(Metadata.TASK), Task.UUID);
if (task == null || !RemoteModel.isValidUuid(task.getUuid()))
continue;
Long oldRemoteId = m.getValue(FileMetadata.REMOTE_ID);
boolean synced = false;
if (oldRemoteId != null && oldRemoteId > 0) {
synced = true;
attachment.setValue(TaskAttachment.UUID, Long.toString(oldRemoteId));
}
attachment.setValue(TaskAttachment.TASK_UUID, task.getUuid());
attachment.setValue(TaskAttachment.NAME, m.getValue(FileMetadata.NAME));
attachment.setValue(TaskAttachment.URL, m.getValue(FileMetadata.URL));
attachment.setValue(TaskAttachment.FILE_PATH, m.getValue(FileMetadata.FILE_PATH));
attachment.setValue(TaskAttachment.CONTENT_TYPE, m.getValue(FileMetadata.FILE_TYPE));
attachment.setValue(TaskAttachment.DELETED_AT, m.getValue(FileMetadata.DELETION_DATE));
if (synced)
attachment.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
taskAttachmentDao.createNew(attachment);
}
} finally {
fmCursor.close();
}
} catch (Exception e) {
Log.e(LOG_TAG, "Error migrating task attachment metadata", e);
}
// -------------- // --------------
// Finally, ensure that all tag metadata entities have all important fields filled in // Finally, ensure that all tag metadata entities have all important fields filled in
// -------------- // --------------

@ -27,6 +27,8 @@ public class AcknowledgeChange extends ServerToClientMessage {
dao = PluginServices.getTagOutstandingDao(); dao = PluginServices.getTagOutstandingDao();
else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table))
dao = PluginServices.getUserActivityOutstandingDao(); dao = PluginServices.getUserActivityOutstandingDao();
else if (NameMaps.TABLE_ID_ATTACHMENTS.equals(table))
dao = PluginServices.getTaskAttachmentOutstandingDao();
else else
dao = null; dao = null;
} }

@ -1,5 +1,6 @@
package com.todoroo.astrid.actfm.sync.messages; package com.todoroo.astrid.actfm.sync.messages;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -7,6 +8,7 @@ import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
@ -14,6 +16,7 @@ import com.todoroo.andlib.data.Property.PropertyVisitor;
import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Order; import com.todoroo.andlib.sql.Order;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncThread.ModelType; import com.todoroo.astrid.actfm.sync.ActFmSyncThread.ModelType;
@ -26,6 +29,8 @@ import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.TagOutstanding; import com.todoroo.astrid.data.TagOutstanding;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.TaskAttachmentOutstanding;
import com.todoroo.astrid.data.TaskOutstanding; import com.todoroo.astrid.data.TaskOutstanding;
import com.todoroo.astrid.data.UserActivity; import com.todoroo.astrid.data.UserActivity;
import com.todoroo.astrid.data.UserActivityOutstanding; import com.todoroo.astrid.data.UserActivityOutstanding;
@ -52,6 +57,9 @@ public class ChangesHappened<TYPE extends RemoteModel, OE extends OutstandingEnt
case TYPE_ACTIVITY: case TYPE_ACTIVITY:
return new ChangesHappened<UserActivity, UserActivityOutstanding>(id, UserActivity.class, return new ChangesHappened<UserActivity, UserActivityOutstanding>(id, UserActivity.class,
PluginServices.getUserActivityDao(), PluginServices.getUserActivityOutstandingDao()); PluginServices.getUserActivityDao(), PluginServices.getUserActivityOutstandingDao());
case TYPE_ATTACHMENT:
return new ChangesHappened<TaskAttachment, TaskAttachmentOutstanding>(id, TaskAttachment.class,
PluginServices.getTaskAttachmentDao(), PluginServices.getTaskAttachmentOutstandingDao());
default: default:
return null; return null;
} }
@ -110,6 +118,15 @@ public class ChangesHappened<TYPE extends RemoteModel, OE extends OutstandingEnt
} else if (NameMaps.MEMBER_REMOVED_COLUMN.equals(localColumn)) { } else if (NameMaps.MEMBER_REMOVED_COLUMN.equals(localColumn)) {
serverColumn = NameMaps.MEMBER_REMOVED_COLUMN; serverColumn = NameMaps.MEMBER_REMOVED_COLUMN;
changeJson.put("value", change.getValue(OutstandingEntry.VALUE_STRING_PROPERTY)); changeJson.put("value", change.getValue(OutstandingEntry.VALUE_STRING_PROPERTY));
} else if (NameMaps.ATTACHMENT_ADDED_COLUMN.equals(localColumn)) {
serverColumn = NameMaps.ATTACHMENT_ADDED_COLUMN;
JSONObject fileJson = getFileJson(change.getValue(OutstandingEntry.VALUE_STRING_PROPERTY));
if (fileJson == null) {
PluginServices.getTaskAttachmentDao().delete(id);
continue;
} else {
changeJson.put("value", fileJson);
}
} else { } else {
Property<?> localProperty = NameMaps.localColumnNameToProperty(table, localColumn); Property<?> localProperty = NameMaps.localColumnNameToProperty(table, localColumn);
if (localProperty == null) if (localProperty == null)
@ -155,6 +172,28 @@ public class ChangesHappened<TYPE extends RemoteModel, OE extends OutstandingEnt
} }
} }
private JSONObject getFileJson(String value) {
try {
JSONObject obj = new JSONObject(value);
String path = obj.optString("path");
if (TextUtils.isEmpty(path))
return null;
File f = new File(path);
if (!f.exists())
return null;
String encodedFile = AndroidUtilities.encodeBase64File(f);
if (TextUtils.isEmpty(encodedFile))
return null;
obj.remove("path");
obj.put("data", encodedFile);
return obj;
} catch (JSONException e) {
return null;
}
}
private class PropertyToJSONVisitor implements PropertyVisitor<Object, OE> { private class PropertyToJSONVisitor implements PropertyVisitor<Object, OE> {
private String getAsString(OE data) { private String getAsString(OE data) {

@ -8,8 +8,10 @@ import java.util.Set;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.data.Table; import com.todoroo.andlib.data.Table;
import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.astrid.data.History;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
import com.todoroo.astrid.data.UserActivity; import com.todoroo.astrid.data.UserActivity;
@ -28,6 +30,7 @@ public class NameMaps {
public static final String TABLE_ID_USERS = "users"; public static final String TABLE_ID_USERS = "users";
public static final String TABLE_ID_USER_ACTIVITY = "user_activities"; public static final String TABLE_ID_USER_ACTIVITY = "user_activities";
public static final String TABLE_ID_HISTORY = "history"; public static final String TABLE_ID_HISTORY = "history";
public static final String TABLE_ID_ATTACHMENTS = "task_attachments";
private static final String PUSHED_AT_PREFIX = "pushed_at"; private static final String PUSHED_AT_PREFIX = "pushed_at";
public static final String PUSHED_AT_TASKS = PUSHED_AT_PREFIX + "_" + TABLE_ID_TASKS; public static final String PUSHED_AT_TASKS = PUSHED_AT_PREFIX + "_" + TABLE_ID_TASKS;
@ -41,7 +44,9 @@ public class NameMaps {
TABLE_LOCAL_TO_SERVER.put(Task.TABLE, TABLE_ID_TASKS); TABLE_LOCAL_TO_SERVER.put(Task.TABLE, TABLE_ID_TASKS);
TABLE_LOCAL_TO_SERVER.put(TagData.TABLE, TABLE_ID_TAGS); TABLE_LOCAL_TO_SERVER.put(TagData.TABLE, TABLE_ID_TAGS);
TABLE_LOCAL_TO_SERVER.put(User.TABLE, TABLE_ID_USERS); TABLE_LOCAL_TO_SERVER.put(User.TABLE, TABLE_ID_USERS);
TABLE_LOCAL_TO_SERVER.put(History.TABLE, TABLE_ID_HISTORY);
TABLE_LOCAL_TO_SERVER.put(UserActivity.TABLE, TABLE_ID_USER_ACTIVITY); TABLE_LOCAL_TO_SERVER.put(UserActivity.TABLE, TABLE_ID_USER_ACTIVITY);
TABLE_LOCAL_TO_SERVER.put(TaskAttachment.TABLE, TABLE_ID_ATTACHMENTS);
// Reverse the mapping to construct the server to local map // Reverse the mapping to construct the server to local map
TABLE_SERVER_TO_LOCAL = AndroidUtilities.reverseMap(TABLE_LOCAL_TO_SERVER); TABLE_SERVER_TO_LOCAL = AndroidUtilities.reverseMap(TABLE_LOCAL_TO_SERVER);
@ -76,6 +81,8 @@ public class NameMaps {
return computeSyncableProperties(TAG_DATA_PROPERTIES_LOCAL_TO_SERVER.keySet(), TAG_PROPERTIES_EXCLUDED); return computeSyncableProperties(TAG_DATA_PROPERTIES_LOCAL_TO_SERVER.keySet(), TAG_PROPERTIES_EXCLUDED);
else if (TABLE_ID_USER_ACTIVITY.equals(table)) else if (TABLE_ID_USER_ACTIVITY.equals(table))
return computeSyncableProperties(USER_ACTIVITY_PROPERTIES_LOCAL_TO_SERVER.keySet(), USER_ACTIVITY_PROPERTIES_EXCLUDED); return computeSyncableProperties(USER_ACTIVITY_PROPERTIES_LOCAL_TO_SERVER.keySet(), USER_ACTIVITY_PROPERTIES_EXCLUDED);
else if (TABLE_ID_ATTACHMENTS.equals(table))
return computeSyncableProperties(TASK_ATTACHMENT_PROPERTIES_LOCAL_TO_SERVER.keySet(), TASK_ATTACHMENT_PROPERTIES_EXCLUDED);
return null; return null;
} }
@ -240,42 +247,48 @@ public class NameMaps {
} }
// ---------- // ----------
// History // TaskAttachment
// ---------- // ----------
// private static final Map<Property<?>, String> HISTORY_PROPERTIES_LOCAL_TO_SERVER; private static final Map<Property<?>, String> TASK_ATTACHMENT_PROPERTIES_LOCAL_TO_SERVER;
// private static final Map<String, Property<?>> HISTORY_COLUMN_NAMES_TO_PROPERTIES; private static final Map<String, Property<?>> TASK_ATTACHMENT_COLUMN_NAMES_TO_PROPERTIES;
// private static final Map<String, String> HISTORY_COLUMNS_LOCAL_TO_SERVER; private static final Map<String, String> TASK_ATTACHMENT_COLUMNS_LOCAL_TO_SERVER;
// private static final Map<String, Property<?>> HISTORY_PROPERTIES_SERVER_TO_LOCAL; private static final Map<String, Property<?>> TASK_ATTACHMENT_PROPERTIES_SERVER_TO_LOCAL;
// private static final Set<String> HISTORY_PROPERTIES_EXCLUDED; private static final Set<String> TASK_ATTACHMENT_PROPERTIES_EXCLUDED;
//
// private static void putHistoryPropertyToServerName(Property<?> property, String serverName, boolean writeable) { public static final String ATTACHMENT_ADDED_COLUMN = "file";
// putPropertyToServerName(property, serverName, HISTORY_PROPERTIES_LOCAL_TO_SERVER, HISTORY_COLUMN_NAMES_TO_PROPERTIES,
// HISTORY_COLUMNS_LOCAL_TO_SERVER, HISTORY_PROPERTIES_EXCLUDED, writeable); private static void putTaskAttachmentPropertyToServerName(Property<?> property, String serverName, boolean writeable) {
// } putPropertyToServerName(property, serverName, TASK_ATTACHMENT_PROPERTIES_LOCAL_TO_SERVER, TASK_ATTACHMENT_COLUMN_NAMES_TO_PROPERTIES,
// TASK_ATTACHMENT_COLUMNS_LOCAL_TO_SERVER, TASK_ATTACHMENT_PROPERTIES_EXCLUDED, writeable);
// static { }
// HISTORY_PROPERTIES_LOCAL_TO_SERVER = new HashMap<Property<?>, String>();
// HISTORY_COLUMN_NAMES_TO_PROPERTIES = new HashMap<String, Property<?>>(); static {
// HISTORY_COLUMNS_LOCAL_TO_SERVER = new HashMap<String, String>(); TASK_ATTACHMENT_PROPERTIES_LOCAL_TO_SERVER = new HashMap<Property<?>, String>();
// HISTORY_PROPERTIES_EXCLUDED = new HashSet<String>(); TASK_ATTACHMENT_COLUMN_NAMES_TO_PROPERTIES = new HashMap<String, Property<?>>();
// TASK_ATTACHMENT_COLUMNS_LOCAL_TO_SERVER = new HashMap<String, String>();
// putHistoryPropertyToServerName(History.UUID, "id", false); TASK_ATTACHMENT_PROPERTIES_EXCLUDED = new HashSet<String>();
// putHistoryPropertyToServerName(History.CREATED_AT, "created_at", false);
// putHistoryPropertyToServerName(History.USER_UUID, "user_id", false); putTaskAttachmentPropertyToServerName(TaskAttachment.UUID, "uuid", false);
// putHistoryPropertyToServerName(History.COLUMN, "column", false); putTaskAttachmentPropertyToServerName(TaskAttachment.USER_UUID, "user_id", false);
// putHistoryPropertyToServerName(History.OLD_VALUE, "prev", false); putTaskAttachmentPropertyToServerName(TaskAttachment.TASK_UUID, "task_id", true);
// putHistoryPropertyToServerName(History.NEW_VALUE, "value", false); putTaskAttachmentPropertyToServerName(TaskAttachment.NAME, "name", false);
// putTaskAttachmentPropertyToServerName(TaskAttachment.URL, "url", false);
// // Reverse the mapping to construct the server to local map putTaskAttachmentPropertyToServerName(TaskAttachment.SIZE, "size", false);
// HISTORY_PROPERTIES_SERVER_TO_LOCAL = AndroidUtilities.reverseMap(USER_ACTIVITY_PROPERTIES_LOCAL_TO_SERVER); putTaskAttachmentPropertyToServerName(TaskAttachment.CONTENT_TYPE, "content_type", false);
// } putTaskAttachmentPropertyToServerName(TaskAttachment.CREATED_AT, "created_at", true);
putTaskAttachmentPropertyToServerName(TaskAttachment.DELETED_AT, "deleted_at", true);
// Reverse the mapping to construct the server to local map
TASK_ATTACHMENT_PROPERTIES_SERVER_TO_LOCAL = AndroidUtilities.reverseMap(TASK_ATTACHMENT_PROPERTIES_LOCAL_TO_SERVER);
}
// ---------- // ----------
// Mapping helpers // Mapping helpers
// ---------- // ----------
private static <A, B> B mapColumnName(String table, String col, Map<A, B> taskMap, Map<A, B> tagMap, Map<A, B> userMap, Map<A, B> userActivityMap) { private static <A, B> B mapColumnName(String table, String col, Map<A, B> taskMap, Map<A, B> tagMap, Map<A, B> userMap, Map<A, B> userActivityMap, Map<A, B> taskAttachmentMap) {
Map<A, B> map = null; Map<A, B> map = null;
if (TABLE_ID_TASKS.equals(table)) if (TABLE_ID_TASKS.equals(table))
map = taskMap; map = taskMap;
@ -285,6 +298,8 @@ public class NameMaps {
map = userMap; map = userMap;
else if (TABLE_ID_USER_ACTIVITY.equals(table)) else if (TABLE_ID_USER_ACTIVITY.equals(table))
map = userActivityMap; map = userActivityMap;
else if (TABLE_ID_ATTACHMENTS.equals(table))
map = taskAttachmentMap;
if (map == null) if (map == null)
return null; return null;
@ -302,20 +317,23 @@ public class NameMaps {
} else if (TABLE_ID_USER_ACTIVITY.equals(table)) { } else if (TABLE_ID_USER_ACTIVITY.equals(table)) {
if (USER_ACTIVITY_COLUMN_NAMES_TO_PROPERTIES.containsKey(column)) if (USER_ACTIVITY_COLUMN_NAMES_TO_PROPERTIES.containsKey(column))
return !USER_ACTIVITY_PROPERTIES_EXCLUDED.contains(column); return !USER_ACTIVITY_PROPERTIES_EXCLUDED.contains(column);
} else if (TABLE_ID_ATTACHMENTS.equals(table)) {
if (TASK_ATTACHMENT_COLUMN_NAMES_TO_PROPERTIES.containsKey(column))
return !TASK_ATTACHMENT_PROPERTIES_EXCLUDED.contains(column);
} }
return false; return false;
} }
public static String localColumnNameToServerColumnName(String table, String localColumn) { public static String localColumnNameToServerColumnName(String table, String localColumn) {
return mapColumnName(table, localColumn, TASK_COLUMNS_LOCAL_TO_SERVER, TAG_DATA_COLUMNS_LOCAL_TO_SERVER, USER_COLUMNS_LOCAL_TO_SERVER, USER_ACTIVITY_COLUMNS_LOCAL_TO_SERVER); return mapColumnName(table, localColumn, TASK_COLUMNS_LOCAL_TO_SERVER, TAG_DATA_COLUMNS_LOCAL_TO_SERVER, USER_COLUMNS_LOCAL_TO_SERVER, USER_ACTIVITY_COLUMNS_LOCAL_TO_SERVER, TASK_ATTACHMENT_COLUMNS_LOCAL_TO_SERVER);
} }
public static Property<?> localColumnNameToProperty(String table, String localColumn) { public static Property<?> localColumnNameToProperty(String table, String localColumn) {
return mapColumnName(table, localColumn, TASK_COLUMN_NAMES_TO_PROPERTIES, TAG_DATA_COLUMN_NAMES_TO_PROPERTIES, USER_COLUMN_NAMES_TO_PROPERTIES, USER_ACTIVITY_COLUMN_NAMES_TO_PROPERTIES); return mapColumnName(table, localColumn, TASK_COLUMN_NAMES_TO_PROPERTIES, TAG_DATA_COLUMN_NAMES_TO_PROPERTIES, USER_COLUMN_NAMES_TO_PROPERTIES, USER_ACTIVITY_COLUMN_NAMES_TO_PROPERTIES, TASK_ATTACHMENT_COLUMN_NAMES_TO_PROPERTIES);
} }
public static Property<?> serverColumnNameToLocalProperty(String table, String serverColumn) { public static Property<?> serverColumnNameToLocalProperty(String table, String serverColumn) {
return mapColumnName(table, serverColumn, TASK_PROPERTIES_SERVER_TO_LOCAL, TAG_DATA_PROPERTIES_SERVER_TO_LOCAL, USER_PROPERTIES_SERVER_TO_LOCAL, USER_ACTIVITY_PROPERTIES_SERVER_TO_LOCAL); return mapColumnName(table, serverColumn, TASK_PROPERTIES_SERVER_TO_LOCAL, TAG_DATA_PROPERTIES_SERVER_TO_LOCAL, USER_PROPERTIES_SERVER_TO_LOCAL, USER_ACTIVITY_PROPERTIES_SERVER_TO_LOCAL, TASK_ATTACHMENT_PROPERTIES_SERVER_TO_LOCAL);
} }
} }

@ -5,6 +5,7 @@ import org.json.JSONObject;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
import com.todoroo.astrid.data.UserActivity; import com.todoroo.astrid.data.UserActivity;
@ -54,6 +55,8 @@ public abstract class ServerToClientMessage {
return new MakeChanges<User>(json, PluginServices.getUserDao()); return new MakeChanges<User>(json, PluginServices.getUserDao());
else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table))
return new MakeChanges<UserActivity>(json, PluginServices.getUserActivityDao()); return new MakeChanges<UserActivity>(json, PluginServices.getUserActivityDao());
else if (NameMaps.TABLE_ID_ATTACHMENTS.equals(table))
return new MakeChanges<TaskAttachment>(json, PluginServices.getTaskAttachmentDao());
else else
return null; return null;
} }
@ -68,6 +71,8 @@ public abstract class ServerToClientMessage {
return new NowBriefed<UserActivity>(json, PluginServices.getUserActivityDao()); return new NowBriefed<UserActivity>(json, PluginServices.getUserActivityDao());
else if (NameMaps.TABLE_ID_USERS.equals(table)) else if (NameMaps.TABLE_ID_USERS.equals(table))
return new NowBriefed<User>(json, PluginServices.getUserDao()); return new NowBriefed<User>(json, PluginServices.getUserDao());
else if (NameMaps.TABLE_ID_ATTACHMENTS.equals(table))
return new NowBriefed<TaskAttachment>(json, PluginServices.getTaskAttachmentDao());
else else
return null; return null;
} }

@ -17,6 +17,8 @@ import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TagMetadataDao; import com.todoroo.astrid.dao.TagMetadataDao;
import com.todoroo.astrid.dao.TagOutstandingDao; import com.todoroo.astrid.dao.TagOutstandingDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskAttachmentOutstandingDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskOutstandingDao; import com.todoroo.astrid.dao.TaskOutstandingDao;
import com.todoroo.astrid.dao.UserActivityDao; import com.todoroo.astrid.dao.UserActivityDao;
@ -85,6 +87,12 @@ public final class PluginServices {
@Autowired @Autowired
HistoryDao historyDao; HistoryDao historyDao;
@Autowired
TaskAttachmentDao taskAttachmentDao;
@Autowired
TaskAttachmentOutstandingDao taskAttachmentOutstandingDao;
private static volatile PluginServices instance; private static volatile PluginServices instance;
static { static {
@ -172,6 +180,14 @@ public final class PluginServices {
return getInstance().historyDao; return getInstance().historyDao;
} }
public static TaskAttachmentDao getTaskAttachmentDao() {
return getInstance().taskAttachmentDao;
}
public static TaskAttachmentOutstandingDao getTaskAttachmentOutstandingDao() {
return getInstance().taskAttachmentOutstandingDao;
}
// -- helpers // -- helpers
/** /**

@ -7,44 +7,19 @@ package com.todoroo.astrid.files;
import com.todoroo.andlib.data.Property.LongProperty; import com.todoroo.andlib.data.Property.LongProperty;
import com.todoroo.andlib.data.Property.StringProperty; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
/**
* This class was deprecated with SyncV2. Use TaskAttachment instead.
* @author Sam
*
*/
@Deprecated
public class FileMetadata { public class FileMetadata {
/** metadata key */ /** metadata key */
public static final String METADATA_KEY = "file"; //$NON-NLS-1$ public static final String METADATA_KEY = "file"; //$NON-NLS-1$
/** default directory for files on external storage */
public static final String FILES_DIRECTORY_DEFAULT = "attachments"; //$NON-NLS-1$
/** preference key for some other download directory */
public static final String FILES_DIRECTORY_PREF = "custom_files_dir"; //$NON-NLS-1$
/** Constants for file types */
public static final String FILE_TYPE_AUDIO = "audio/"; //$NON-NLS-1$
public static final String FILE_TYPE_IMAGE = "image/"; //$NON-NLS-1$
public static final String FILE_TYPE_PDF = "application/pdf"; //$NON-NLS-1$
public static final String FILE_TYPE_DOC = "application/msword"; //$NON-NLS-1$
public static final String FILE_TYPE_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; //$NON-NLS-1$
public static final String FILE_TYPE_PPT = "application/vnd.ms-powerpoint"; //$NON-NLS-1$
public static final String FILE_TYPE_PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; //$NON-NLS-1$
public static final String FILE_TYPE_XLS = "application/vnd.ms-excel"; //$NON-NLS-1$
public static final String FILE_TYPE_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; //$NON-NLS-1$
public static final String[] MS_FILETYPES = {
FILE_TYPE_DOC, FILE_TYPE_DOCX,
FILE_TYPE_XLS, FILE_TYPE_XLSX,
FILE_TYPE_PPT, FILE_TYPE_PPTX,
};
public static final String FILE_TYPE_OTHER = "application/octet-stream"; //$NON-NLS-1$
public static final StringProperty FILE_PATH = new StringProperty(Metadata.TABLE, public static final StringProperty FILE_PATH = new StringProperty(Metadata.TABLE,
Metadata.VALUE1.name); Metadata.VALUE1.name);
@ -63,29 +38,4 @@ public class FileMetadata {
public static final StringProperty NAME = new StringProperty(Metadata.TABLE, public static final StringProperty NAME = new StringProperty(Metadata.TABLE,
Metadata.VALUE6.name); Metadata.VALUE6.name);
public static Metadata createNewFileMetadata(long taskId, String filePath, String fileName, String fileType) {
Metadata metadata = new Metadata();
metadata.setValue(Metadata.KEY, METADATA_KEY);
metadata.setValue(Metadata.TASK, taskId);
metadata.setValue(NAME, fileName);
metadata.setValue(FILE_PATH, filePath);
metadata.setValue(FILE_TYPE, fileType);
metadata.setValue(REMOTE_ID, 0L);
metadata.setValue(DELETION_DATE, 0L);
return metadata;
}
public static boolean taskHasAttachments(long taskId) {
TodorooCursor<Metadata> files = PluginServices.getMetadataService()
.query(Query.select(Metadata.TASK).where(
Criterion.and(MetadataCriteria.byTaskAndwithKey(taskId, METADATA_KEY),
DELETION_DATE.eq(0))).limit(1));
try {
return files.getCount() > 0;
} finally {
files.close();
}
}
} }

@ -16,6 +16,7 @@ import android.text.TextUtils;
import com.timsu.astrid.R; import com.timsu.astrid.R;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences; import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.data.TaskAttachment;
public class FileUtilities { public class FileUtilities {
@ -68,12 +69,12 @@ public class FileUtilities {
public static File getAttachmentsDirectory(Context context) { public static File getAttachmentsDirectory(Context context) {
File directory = null; File directory = null;
String customDir = Preferences.getStringValue(FileMetadata.FILES_DIRECTORY_PREF); String customDir = Preferences.getStringValue(TaskAttachment.FILES_DIRECTORY_PREF);
if (!TextUtils.isEmpty(customDir)) if (!TextUtils.isEmpty(customDir))
directory = new File(customDir); directory = new File(customDir);
if (directory == null || !directory.exists()) if (directory == null || !directory.exists())
directory = context.getExternalFilesDir(FileMetadata.FILES_DIRECTORY_DEFAULT); directory = context.getExternalFilesDir(TaskAttachment.FILES_DIRECTORY_DEFAULT);
return directory; return directory;
} }

@ -44,23 +44,20 @@ import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncService; import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.ui.PopupControlSet; import com.todoroo.astrid.ui.PopupControlSet;
import com.todoroo.astrid.utility.Constants; import com.todoroo.astrid.utility.Constants;
public class FilesControlSet extends PopupControlSet { public class FilesControlSet extends PopupControlSet {
@Autowired @Autowired
private MetadataService metadataService; private TaskAttachmentDao taskAttachmentDao;
@Autowired private final ArrayList<TaskAttachment> files = new ArrayList<TaskAttachment>();
private ActFmSyncService actFmSyncService;
private final ArrayList<Metadata> files = new ArrayList<Metadata>();
private final LinearLayout fileDisplayList; private final LinearLayout fileDisplayList;
private LinearLayout fileList; private LinearLayout fileList;
private final LayoutInflater inflater; private final LayoutInflater inflater;
@ -77,7 +74,7 @@ public class FilesControlSet extends PopupControlSet {
@Override @Override
protected void refreshDisplayView() { protected void refreshDisplayView() {
fileDisplayList.removeAllViews(); fileDisplayList.removeAllViews();
for (final Metadata m : files) { for (final TaskAttachment m : files) {
View fileRow = inflater.inflate(R.layout.file_display_row, null); View fileRow = inflater.inflate(R.layout.file_display_row, null);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.gravity = Gravity.RIGHT; lp.gravity = Gravity.RIGHT;
@ -95,16 +92,16 @@ public class FilesControlSet extends PopupControlSet {
public void refreshMetadata() { public void refreshMetadata() {
if (model != null) { if (model != null) {
TodorooCursor<Metadata> cursor = metadataService.query( TodorooCursor<TaskAttachment> cursor = taskAttachmentDao.query(
Query.select(Metadata.PROPERTIES) Query.select(TaskAttachment.PROPERTIES)
.where(Criterion.and(MetadataCriteria.byTaskAndwithKey(model.getId(), FileMetadata.METADATA_KEY), .where(Criterion.and(TaskAttachment.TASK_UUID.eq(model.getUuid()),
FileMetadata.DELETION_DATE.eq(0)))); TaskAttachment.DELETED_AT.eq(0))));
try { try {
files.clear(); files.clear();
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
Metadata metadata = new Metadata(); TaskAttachment attachment = new TaskAttachment();
metadata.readFromCursor(cursor); attachment.readFromCursor(cursor);
files.add(metadata); files.add(attachment);
} }
} finally { } finally {
cursor.close(); cursor.close();
@ -117,15 +114,15 @@ public class FilesControlSet extends PopupControlSet {
private void validateFiles() { private void validateFiles() {
for (int i = 0; i < files.size(); i++) { for (int i = 0; i < files.size(); i++) {
Metadata m = files.get(i); TaskAttachment m = files.get(i);
if (m.containsNonNullValue(FileMetadata.FILE_PATH)) { if (m.containsNonNullValue(TaskAttachment.FILE_PATH)) {
File f = new File(m.getValue(FileMetadata.FILE_PATH)); File f = new File(m.getValue(TaskAttachment.FILE_PATH));
if (!f.exists()) { if (!f.exists()) {
m.setValue(FileMetadata.FILE_PATH, ""); //$NON-NLS-1$ m.setValue(TaskAttachment.FILE_PATH, ""); //$NON-NLS-1$
if (m.containsNonNullValue(FileMetadata.URL)) { // We're ok, just the local file was deleted if (m.containsNonNullValue(TaskAttachment.URL)) { // We're ok, just the local file was deleted
metadataService.save(m); taskAttachmentDao.saveExisting(m);
} else { // No local file and no url -- delete the metadata } else { // No local file and no url -- delete the metadata
metadataService.delete(m); taskAttachmentDao.delete(m.getId());
files.remove(i); files.remove(i);
i--; i--;
} }
@ -152,7 +149,7 @@ public class FilesControlSet extends PopupControlSet {
final LinearLayout finalList = fileList; final LinearLayout finalList = fileList;
fileList.removeAllViews(); fileList.removeAllViews();
LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
for (final Metadata m : files) { for (final TaskAttachment m : files) {
final View fileRow = inflater.inflate(R.layout.file_row, null); final View fileRow = inflater.inflate(R.layout.file_row, null);
setUpFileRow(m, fileRow, fileList, lp); setUpFileRow(m, fileRow, fileList, lp);
@ -170,16 +167,15 @@ public class FilesControlSet extends PopupControlSet {
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface d, int which) { public void onClick(DialogInterface d, int which) {
if (m.getValue(FileMetadata.REMOTE_ID) > 0) { if (RemoteModel.isValidUuid(m.getValue(TaskAttachment.UUID))) {
m.setValue(FileMetadata.DELETION_DATE, DateUtilities.now()); m.setValue(TaskAttachment.DELETED_AT, DateUtilities.now());
metadataService.save(m); taskAttachmentDao.saveExisting(m);
actFmSyncService.pushAttachmentInBackground(m);
} else { } else {
metadataService.delete(m); taskAttachmentDao.delete(m.getId());
} }
if (m.containsNonNullValue(FileMetadata.FILE_PATH)) { if (m.containsNonNullValue(TaskAttachment.FILE_PATH)) {
File f = new File(m.getValue(FileMetadata.FILE_PATH)); File f = new File(m.getValue(TaskAttachment.FILE_PATH));
f.delete(); f.delete();
} }
files.remove(m); files.remove(m);
@ -193,8 +189,8 @@ public class FilesControlSet extends PopupControlSet {
} }
} }
private void setupFileClickListener(View view, final Metadata m) { private void setupFileClickListener(View view, final TaskAttachment m) {
final String filePath = m.containsNonNullValue(FileMetadata.FILE_PATH) ? m.getValue(FileMetadata.FILE_PATH) : null; final String filePath = m.containsNonNullValue(TaskAttachment.FILE_PATH) ? m.getValue(TaskAttachment.FILE_PATH) : null;
if (TextUtils.isEmpty(filePath)) { if (TextUtils.isEmpty(filePath)) {
view.setOnClickListener(new OnClickListener() { view.setOnClickListener(new OnClickListener() {
@Override @Override
@ -218,18 +214,18 @@ public class FilesControlSet extends PopupControlSet {
} }
} }
private void showFile(final Metadata m) { private void showFile(final TaskAttachment m) {
final String fileType = m.containsNonNullValue(FileMetadata.FILE_TYPE) ? m.getValue(FileMetadata.FILE_TYPE) : FileMetadata.FILE_TYPE_OTHER; final String fileType = m.containsNonNullValue(TaskAttachment.CONTENT_TYPE) ? m.getValue(TaskAttachment.CONTENT_TYPE) : TaskAttachment.FILE_TYPE_OTHER;
final String filePath = m.getValue(FileMetadata.FILE_PATH); final String filePath = m.getValue(TaskAttachment.FILE_PATH);
if (fileType.startsWith(FileMetadata.FILE_TYPE_AUDIO)) { if (fileType.startsWith(TaskAttachment.FILE_TYPE_AUDIO)) {
RecognizerApi.play(activity, m.getValue(FileMetadata.FILE_PATH), new PlaybackExceptionHandler() { RecognizerApi.play(activity, m.getValue(TaskAttachment.FILE_PATH), new PlaybackExceptionHandler() {
@Override @Override
public void playbackFailed(String file) { public void playbackFailed(String file) {
showFromIntent(filePath, fileType); showFromIntent(filePath, fileType);
} }
}); });
} else if (fileType.startsWith(FileMetadata.FILE_TYPE_IMAGE)) { } else if (fileType.startsWith(TaskAttachment.FILE_TYPE_IMAGE)) {
AlertDialog image = new AlertDialog.Builder(activity).create(); AlertDialog image = new AlertDialog.Builder(activity).create();
ImageView imageView = new ImageView(activity); ImageView imageView = new ImageView(activity);
imageView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); imageView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
@ -252,7 +248,7 @@ public class FilesControlSet extends PopupControlSet {
image.show(); image.show();
} else { } else {
String useType = fileType; String useType = fileType;
if (fileType.equals(FileMetadata.FILE_TYPE_OTHER)) { if (fileType.equals(TaskAttachment.FILE_TYPE_OTHER)) {
String extension = AndroidUtilities.getFileExtension(filePath); String extension = AndroidUtilities.getFileExtension(filePath);
MimeTypeMap map = MimeTypeMap.getSingleton(); MimeTypeMap map = MimeTypeMap.getSingleton();
@ -260,8 +256,9 @@ public class FilesControlSet extends PopupControlSet {
if (!TextUtils.isEmpty(guessedType)) if (!TextUtils.isEmpty(guessedType))
useType = guessedType; useType = guessedType;
if (useType != guessedType) { if (useType != guessedType) {
m.setValue(FileMetadata.FILE_TYPE, useType); m.setValue(TaskAttachment.CONTENT_TYPE, useType);
metadataService.save(m); m.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
taskAttachmentDao.saveExisting(m);
} }
} }
showFromIntent(filePath, useType); showFromIntent(filePath, useType);
@ -279,11 +276,11 @@ public class FilesControlSet extends PopupControlSet {
} }
private void handleActivityNotFound(String fileType) { private void handleActivityNotFound(String fileType) {
if (fileType.startsWith(FileMetadata.FILE_TYPE_AUDIO)) { if (fileType.startsWith(TaskAttachment.FILE_TYPE_AUDIO)) {
searchMarket("com.clov4r.android.nil", R.string.search_market_audio_title, R.string.search_market_audio); //$NON-NLS-1$ searchMarket("com.clov4r.android.nil", R.string.search_market_audio_title, R.string.search_market_audio); //$NON-NLS-1$
} else if (fileType.equals(FileMetadata.FILE_TYPE_PDF)) { } else if (fileType.equals(TaskAttachment.FILE_TYPE_PDF)) {
searchMarket("com.adobe.reader", R.string.search_market_pdf_title, R.string.search_market_pdf); //$NON-NLS-1$ searchMarket("com.adobe.reader", R.string.search_market_pdf_title, R.string.search_market_pdf); //$NON-NLS-1$
} else if (AndroidUtilities.indexOf(FileMetadata.MS_FILETYPES, fileType) >= 0) { } else if (AndroidUtilities.indexOf(TaskAttachment.MS_FILETYPES, fileType) >= 0) {
searchMarket("com.dataviz.docstogo", R.string.search_market_ms_title, R.string.search_market_ms); //$NON-NLS-1$ searchMarket("com.dataviz.docstogo", R.string.search_market_ms_title, R.string.search_market_ms); //$NON-NLS-1$
} else { } else {
DialogUtilities.okDialog(activity, activity.getString(R.string.file_type_unhandled_title), DialogUtilities.okDialog(activity, activity.getString(R.string.file_type_unhandled_title),
@ -311,7 +308,7 @@ public class FilesControlSet extends PopupControlSet {
} }
@SuppressWarnings("nls") @SuppressWarnings("nls")
private void downloadFile(final Metadata m) { private void downloadFile(final TaskAttachment m) {
final ProgressDialog pd = new ProgressDialog(activity); final ProgressDialog pd = new ProgressDialog(activity);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setMessage(activity.getString(R.string.file_download_progress)); pd.setMessage(activity.getString(R.string.file_download_progress));
@ -320,9 +317,9 @@ public class FilesControlSet extends PopupControlSet {
new Thread() { new Thread() {
@Override @Override
public void run() { public void run() {
String urlString = m.getValue(FileMetadata.URL); String urlString = m.getValue(TaskAttachment.URL);
urlString = urlString.replace(" ", "%20"); urlString = urlString.replace(" ", "%20");
String name = m.getValue(FileMetadata.NAME); String name = m.getValue(TaskAttachment.NAME);
StringBuilder filePathBuilder = new StringBuilder(); StringBuilder filePathBuilder = new StringBuilder();
File directory = FileUtilities.getAttachmentsDirectory(activity); File directory = FileUtilities.getAttachmentsDirectory(activity);
@ -378,8 +375,8 @@ public class FilesControlSet extends PopupControlSet {
fileOutput.flush(); fileOutput.flush();
fileOutput.close(); fileOutput.close();
m.setValue(FileMetadata.FILE_PATH, file.getAbsolutePath()); m.setValue(TaskAttachment.FILE_PATH, file.getAbsolutePath());
metadataService.save(m); taskAttachmentDao.saveExisting(m);
activity.runOnUiThread(new Runnable() { activity.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -404,11 +401,11 @@ public class FilesControlSet extends PopupControlSet {
pd.show(); pd.show();
} }
private void setUpFileRow(Metadata m, View row, LinearLayout parent, LayoutParams lp) { private void setUpFileRow(TaskAttachment m, View row, LinearLayout parent, LayoutParams lp) {
TextView nameView = (TextView) row.findViewById(R.id.file_text); TextView nameView = (TextView) row.findViewById(R.id.file_text);
TextView typeView = (TextView) row.findViewById(R.id.file_type); TextView typeView = (TextView) row.findViewById(R.id.file_type);
String name = getNameString(m); String name = getNameString(m);
String type = getTypeString(m.getValue(FileMetadata.NAME)); String type = getTypeString(m.getValue(TaskAttachment.NAME));
nameView.setText(name); nameView.setText(name);
if (TextUtils.isEmpty(type)) if (TextUtils.isEmpty(type))
@ -419,8 +416,8 @@ public class FilesControlSet extends PopupControlSet {
parent.addView(row, lp); parent.addView(row, lp);
} }
private String getNameString(Metadata metadata) { private String getNameString(TaskAttachment metadata) {
String name = metadata.getValue(FileMetadata.NAME); String name = metadata.getValue(TaskAttachment.NAME);
int extension = name.lastIndexOf('.'); int extension = name.lastIndexOf('.');
if (extension < 0) if (extension < 0)
return name; return name;

@ -148,7 +148,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
public void loadViewForTaskID(long t){ public void loadViewForTaskID(long t){
try { try {
task = PluginServices.getTaskService().fetchById(t, Task.NOTES, Task.ID, Task.UUID, Task.TITLE, Task.HISTORY_FETCH_DATE); task = PluginServices.getTaskService().fetchById(t, Task.NOTES, Task.ID, Task.UUID, Task.TITLE, Task.HISTORY_FETCH_DATE, Task.PUSHED_AT);
} catch (SQLiteException e) { } catch (SQLiteException e) {
StartupService.handleSQLiteError(ContextManager.getContext(), e); StartupService.handleSQLiteError(ContextManager.getContext(), e);
} }

@ -50,8 +50,8 @@ import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.files.FileExplore; import com.todoroo.astrid.files.FileExplore;
import com.todoroo.astrid.files.FileMetadata;
import com.todoroo.astrid.gcal.CalendarStartupReceiver; import com.todoroo.astrid.gcal.CalendarStartupReceiver;
import com.todoroo.astrid.gtasks.GtasksPreferences; import com.todoroo.astrid.gtasks.GtasksPreferences;
import com.todoroo.astrid.helper.MetadataHelper; import com.todoroo.astrid.helper.MetadataHelper;
@ -575,7 +575,7 @@ public class EditPreferences extends TodorooPreferenceActivity {
// pp preferences // pp preferences
else if (r.getString(R.string.p_files_dir).equals(preference.getKey())) { else if (r.getString(R.string.p_files_dir).equals(preference.getKey())) {
String dir = Preferences.getStringValue(FileMetadata.FILES_DIRECTORY_PREF); String dir = Preferences.getStringValue(TaskAttachment.FILES_DIRECTORY_PREF);
if (TextUtils.isEmpty(dir)) { if (TextUtils.isEmpty(dir)) {
dir = r.getString(R.string.p_files_dir_desc_default); dir = r.getString(R.string.p_files_dir_desc_default);
@ -665,7 +665,7 @@ public class EditPreferences extends TodorooPreferenceActivity {
} else if (requestCode == REQUEST_CODE_FILES_DIR && resultCode == RESULT_OK) { } else if (requestCode == REQUEST_CODE_FILES_DIR && resultCode == RESULT_OK) {
if (data != null) { if (data != null) {
String dir = data.getStringExtra(FileExplore.RESULT_DIR_SELECTED); String dir = data.getStringExtra(FileExplore.RESULT_DIR_SELECTED);
Preferences.setString(FileMetadata.FILES_DIRECTORY_PREF, dir); Preferences.setString(TaskAttachment.FILES_DIRECTORY_PREF, dir);
} }
return; return;
} }

@ -70,12 +70,12 @@ import com.todoroo.astrid.actfm.TaskCommentsFragment;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService; import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncService; import com.todoroo.astrid.actfm.sync.ActFmSyncService;
import com.todoroo.astrid.api.AstridApiConstants; import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.files.AACRecordingActivity; import com.todoroo.astrid.files.AACRecordingActivity;
import com.todoroo.astrid.files.FileExplore; import com.todoroo.astrid.files.FileExplore;
import com.todoroo.astrid.files.FileMetadata;
import com.todoroo.astrid.files.FileUtilities; import com.todoroo.astrid.files.FileUtilities;
import com.todoroo.astrid.files.FilesControlSet; import com.todoroo.astrid.files.FilesControlSet;
import com.todoroo.astrid.gcal.GCalControlSet; import com.todoroo.astrid.gcal.GCalControlSet;
@ -212,6 +212,9 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
@Autowired @Autowired
private MetadataService metadataService; private MetadataService metadataService;
@Autowired
private TaskAttachmentDao taskAttachmentDao;
@Autowired @Autowired
private AddOnService addOnService; private AddOnService addOnService;
@ -870,7 +873,7 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
loadItem(intent); loadItem(intent);
synchronized (controls) { synchronized (controls) {
if (!FileMetadata.taskHasAttachments(model.getId())) { if (!taskAttachmentDao.taskHasAttachments(model.getUuid())) {
filesControlSet.getDisplayView().setVisibility(View.GONE); filesControlSet.getDisplayView().setVisibility(View.GONE);
} }
for (TaskEditControlSet controlSet : controls) for (TaskEditControlSet controlSet : controls)
@ -1140,7 +1143,7 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
String name = dst.getName(); String name = dst.getName();
String extension = AndroidUtilities.getFileExtension(name); String extension = AndroidUtilities.getFileExtension(name);
String type = FileMetadata.FILE_TYPE_OTHER; String type = TaskAttachment.FILE_TYPE_OTHER;
if (!TextUtils.isEmpty(extension)) { if (!TextUtils.isEmpty(extension)) {
MimeTypeMap map = MimeTypeMap.getSingleton(); MimeTypeMap map = MimeTypeMap.getSingleton();
String guessedType = map.getMimeTypeFromExtension(extension); String guessedType = map.getMimeTypeFromExtension(extension);
@ -1163,16 +1166,15 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
fos.flush(); fos.flush();
fos.close(); fos.close();
createNewFileAttachment(path, nameRef.get(), FileMetadata.FILE_TYPE_IMAGE + "png"); createNewFileAttachment(path, nameRef.get(), TaskAttachment.FILE_TYPE_IMAGE + "png");
} catch (Exception e) { } catch (Exception e) {
Toast.makeText(getActivity(), R.string.file_err_copy, Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), R.string.file_err_copy, Toast.LENGTH_LONG).show();
} }
} }
private void createNewFileAttachment(String path, String fileName, String fileType) { private void createNewFileAttachment(String path, String fileName, String fileType) {
Metadata fileMetadata = FileMetadata.createNewFileMetadata(model.getId(), path, fileName, fileType); TaskAttachment attachment = TaskAttachment.createNewAttachment(model.getUuid(), path, fileName, fileType);
metadataService.save(fileMetadata); taskAttachmentDao.createNew(attachment);
actFmSyncService.pushAttachmentInBackground(fileMetadata);
filesControlSet.refreshMetadata(); filesControlSet.refreshMetadata();
filesControlSet.getDisplayView().setVisibility(View.VISIBLE); filesControlSet.getDisplayView().setVisibility(View.VISIBLE);
} }
@ -1291,7 +1293,7 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
} else if (requestCode == REQUEST_CODE_RECORD && resultCode == Activity.RESULT_OK) { } else if (requestCode == REQUEST_CODE_RECORD && resultCode == Activity.RESULT_OK) {
String recordedAudioPath = data.getStringExtra(AACRecordingActivity.RESULT_OUTFILE); String recordedAudioPath = data.getStringExtra(AACRecordingActivity.RESULT_OUTFILE);
String recordedAudioName = data.getStringExtra(AACRecordingActivity.RESULT_FILENAME); String recordedAudioName = data.getStringExtra(AACRecordingActivity.RESULT_FILENAME);
createNewFileAttachment(recordedAudioPath, recordedAudioName, FileMetadata.FILE_TYPE_AUDIO + "m4a"); //$NON-NLS-1$ createNewFileAttachment(recordedAudioPath, recordedAudioName, TaskAttachment.FILE_TYPE_AUDIO + "m4a"); //$NON-NLS-1$
} else if (requestCode == REQUEST_CODE_ATTACH_FILE && resultCode == Activity.RESULT_OK) { } else if (requestCode == REQUEST_CODE_ATTACH_FILE && resultCode == Activity.RESULT_OK) {
attachFile(data.getStringExtra(FileExplore.RESULT_FILE_SELECTED)); attachFile(data.getStringExtra(FileExplore.RESULT_FILE_SELECTED));
} else if (requestCode == REQUEST_CODE_BEAST_MODE) { } else if (requestCode == REQUEST_CODE_BEAST_MODE) {

@ -87,8 +87,8 @@ import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
import com.todoroo.astrid.files.FileMetadata;
import com.todoroo.astrid.helper.SyncActionHelper; import com.todoroo.astrid.helper.SyncActionHelper;
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader; import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader;
import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader.ContextMenuItem; import com.todoroo.astrid.helper.TaskListContextMenuExtensionLoader.ContextMenuItem;
@ -981,9 +981,7 @@ public class TaskListFragment extends ListFragment implements OnScrollListener,
tagsJoinCriterion).toString() //$NON-NLS-1$ tagsJoinCriterion).toString() //$NON-NLS-1$
+ Join.left(User.TABLE.as(USER_IMAGE_JOIN), + Join.left(User.TABLE.as(USER_IMAGE_JOIN),
Task.USER_ID.eq(Field.field(USER_IMAGE_JOIN + "." + User.UUID.name))).toString() Task.USER_ID.eq(Field.field(USER_IMAGE_JOIN + "." + User.UUID.name))).toString()
+ Join.left(Metadata.TABLE.as(FILE_METADATA_JOIN), + Join.left(TaskAttachment.TABLE.as(FILE_METADATA_JOIN), Task.UUID.eq(Field.field(FILE_METADATA_JOIN) + "." + TaskAttachment.UUID.name))
Criterion.and(Field.field(FILE_METADATA_JOIN + "." + Metadata.KEY.name).eq(FileMetadata.METADATA_KEY),
Task.ID.eq(Field.field(FILE_METADATA_JOIN + "." + Metadata.TASK.name))))
+ filter.getSqlQuery(); + filter.getSqlQuery();
sqlQueryTemplate.set(SortHelper.adjustQueryForFlagsAndSort( sqlQueryTemplate.set(SortHelper.adjustQueryForFlagsAndSort(

@ -77,6 +77,7 @@ import com.todoroo.astrid.core.LinkActionExposer;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.RemoteModel; import com.todoroo.astrid.data.RemoteModel;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
import com.todoroo.astrid.files.FilesAction; import com.todoroo.astrid.files.FilesAction;
import com.todoroo.astrid.files.FilesControlSet; import com.todoroo.astrid.files.FilesControlSet;
@ -113,7 +114,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
@SuppressWarnings("nls") @SuppressWarnings("nls")
private static final StringProperty TAGS = new StringProperty(null, "group_concat(" + TaskListFragment.TAGS_METADATA_JOIN + "." + TaskToTagMetadata.TAG_NAME.name + ", ' | ')").as("tags"); private static final StringProperty TAGS = new StringProperty(null, "group_concat(" + TaskListFragment.TAGS_METADATA_JOIN + "." + TaskToTagMetadata.TAG_NAME.name + ", ' | ')").as("tags");
@SuppressWarnings("nls") @SuppressWarnings("nls")
private static final LongProperty FILE_ID_PROPERTY = Metadata.ID.cloneAs(TaskListFragment.FILE_METADATA_JOIN, "fileId"); private static final LongProperty FILE_ID_PROPERTY = TaskAttachment.ID.cloneAs(TaskListFragment.FILE_METADATA_JOIN, "fileId");
@SuppressWarnings("nls") @SuppressWarnings("nls")
private static final IntegerProperty HAS_NOTES_PROPERTY = new IntegerProperty(null, "length(" + Task.NOTES + ") > 0").as("hasNotes"); private static final IntegerProperty HAS_NOTES_PROPERTY = new IntegerProperty(null, "length(" + Task.NOTES + ") > 0").as("hasNotes");

@ -22,6 +22,8 @@ import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.TagMetadata; import com.todoroo.astrid.data.TagMetadata;
import com.todoroo.astrid.data.TagOutstanding; import com.todoroo.astrid.data.TagOutstanding;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.TaskAttachmentOutstanding;
import com.todoroo.astrid.data.TaskOutstanding; import com.todoroo.astrid.data.TaskOutstanding;
import com.todoroo.astrid.data.Update; import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.data.User; import com.todoroo.astrid.data.User;
@ -68,10 +70,12 @@ public class Database extends AbstractDatabase {
ABTestEvent.TABLE, ABTestEvent.TABLE,
TagMetadata.TABLE, TagMetadata.TABLE,
History.TABLE, History.TABLE,
TaskAttachment.TABLE,
TaskOutstanding.TABLE, TaskOutstanding.TABLE,
TagOutstanding.TABLE, TagOutstanding.TABLE,
UserActivityOutstanding.TABLE, UserActivityOutstanding.TABLE,
TaskAttachmentOutstanding.TABLE
}; };
// --- listeners // --- listeners
@ -338,10 +342,12 @@ public class Database extends AbstractDatabase {
case 28: try { case 28: try {
database.execSQL(createTableSql(visitor, TaskOutstanding.TABLE.name, TaskOutstanding.PROPERTIES)); database.execSQL(createTableSql(visitor, TaskOutstanding.TABLE.name, TaskOutstanding.PROPERTIES));
database.execSQL(createTableSql(visitor, TagOutstanding.TABLE.name, TagOutstanding.PROPERTIES)); database.execSQL(createTableSql(visitor, TagOutstanding.TABLE.name, TagOutstanding.PROPERTIES));
database.execSQL(createTableSql(visitor, TaskAttachmentOutstanding.TABLE.name, TagOutstanding.PROPERTIES));
database.execSQL(createTableSql(visitor, TagMetadata.TABLE.name, TagMetadata.PROPERTIES)); database.execSQL(createTableSql(visitor, TagMetadata.TABLE.name, TagMetadata.PROPERTIES));
database.execSQL(createTableSql(visitor, UserActivity.TABLE.name, UserActivity.PROPERTIES)); database.execSQL(createTableSql(visitor, UserActivity.TABLE.name, UserActivity.PROPERTIES));
database.execSQL(createTableSql(visitor, UserActivityOutstanding.TABLE.name, UserActivityOutstanding.PROPERTIES)); database.execSQL(createTableSql(visitor, UserActivityOutstanding.TABLE.name, UserActivityOutstanding.PROPERTIES));
database.execSQL(createTableSql(visitor, History.TABLE.name, History.PROPERTIES)); database.execSQL(createTableSql(visitor, History.TABLE.name, History.PROPERTIES));
database.execSQL(createTableSql(visitor, TaskAttachment.TABLE.name, TaskAttachment.PROPERTIES));
database.execSQL(addColumnSql(Task.TABLE, Task.PUSHED_AT, visitor, null)); database.execSQL(addColumnSql(Task.TABLE, Task.PUSHED_AT, visitor, null));
database.execSQL(addColumnSql(Task.TABLE, Task.IS_PUBLIC, visitor, "0")); database.execSQL(addColumnSql(Task.TABLE, Task.IS_PUBLIC, visitor, "0"));

@ -0,0 +1,107 @@
/**
* Copyright (c) 2012 Todoroo Inc
*
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.dao;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.ContentValues;
import android.text.TextUtils;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.actfm.sync.messages.NameMaps;
import com.todoroo.astrid.data.OutstandingEntry;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.data.TaskAttachmentOutstanding;
/**
* Data Access layer for {@link TagData}-related operations.
*
* @author Tim Su <tim@todoroo.com>
*
*/
public class TaskAttachmentDao extends RemoteModelDao<TaskAttachment> {
@Autowired Database database;
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UR_UNINIT_READ")
public TaskAttachmentDao() {
super(TaskAttachment.class);
DependencyInjectionService.getInstance().inject(this);
setDatabase(database);
}
@Override
protected boolean shouldRecordOutstandingEntry(String columnName) {
return NameMaps.shouldRecordOutstandingColumnForTable(NameMaps.TABLE_ID_ATTACHMENTS, columnName);
}
@Override
protected int createOutstandingEntries(long modelId, ContentValues modelSetValues) {
// new attachment case -- only set by us when creating new attachments; when setting during sync outstanding entries suppressed
if (modelSetValues.containsKey(TaskAttachment.CONTENT_TYPE.name)) {
TaskAttachmentOutstanding m = new TaskAttachmentOutstanding();
String path = modelSetValues.getAsString(TaskAttachment.FILE_PATH.name);
if (TextUtils.isEmpty(path))
return -1;
try {
JSONObject newFileHash = new JSONObject();
newFileHash.put("name", modelSetValues.getAsString(TaskAttachment.NAME.name)); //$NON-NLS-1$
newFileHash.put("type", modelSetValues.getAsString(TaskAttachment.CONTENT_TYPE.name)); //$NON-NLS-1$
newFileHash.put("path", path); //$NON-NLS-1$
m.setValue(OutstandingEntry.ENTITY_ID_PROPERTY, modelId);
m.setValue(OutstandingEntry.COLUMN_STRING_PROPERTY, NameMaps.ATTACHMENT_ADDED_COLUMN);
m.setValue(OutstandingEntry.VALUE_STRING_PROPERTY, newFileHash.toString());
m.setValue(OutstandingEntry.CREATED_AT_PROPERTY, DateUtilities.now());
database.insert(outstandingTable.name, null, m.getSetValues());
} catch (JSONException e) {
return -1;
}
}
int result = super.createOutstandingEntries(modelId, modelSetValues);
if (result < 0) // Error
return result;
return 1 + result;
}
public boolean taskHasAttachments(String taskUuid) {
TodorooCursor<TaskAttachment> files = query(Query.select(TaskAttachment.TASK_UUID).where(
Criterion.and(TaskAttachment.TASK_UUID.eq(taskUuid),
TaskAttachment.DELETED_AT.eq(0))).limit(1));
try {
return files.getCount() > 0;
} finally {
files.close();
}
}
// --- SQL clause generators
/**
* Generates SQL clauses
*/
public static class TagDataCriteria {
/** @returns tasks by id */
public static Criterion byId(long id) {
return TagData.ID.eq(id);
}
public static Criterion isTeam() {
return TagData.IS_TEAM.eq(1);
}
}
}

@ -0,0 +1,12 @@
package com.todoroo.astrid.dao;
import com.todoroo.astrid.data.TaskAttachmentOutstanding;
public class TaskAttachmentOutstandingDao extends OutstandingEntryDao<TaskAttachmentOutstanding> {
public TaskAttachmentOutstandingDao() {
super(TaskAttachmentOutstanding.class);
}
}

@ -21,6 +21,8 @@ import com.todoroo.astrid.dao.StoreObjectDao;
import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TagMetadataDao; import com.todoroo.astrid.dao.TagMetadataDao;
import com.todoroo.astrid.dao.TagOutstandingDao; import com.todoroo.astrid.dao.TagOutstandingDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskAttachmentOutstandingDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.TaskOutstandingDao; import com.todoroo.astrid.dao.TaskOutstandingDao;
import com.todoroo.astrid.dao.UpdateDao; import com.todoroo.astrid.dao.UpdateDao;
@ -83,6 +85,8 @@ public class AstridDependencyInjector extends AbstractDependencyInjector {
injectables.put("tagOutstandingDao", TagOutstandingDao.class); injectables.put("tagOutstandingDao", TagOutstandingDao.class);
injectables.put("userActivityOutstandingDao", UserActivityOutstandingDao.class); injectables.put("userActivityOutstandingDao", UserActivityOutstandingDao.class);
injectables.put("historyDao", HistoryDao.class); injectables.put("historyDao", HistoryDao.class);
injectables.put("taskAttachmentDao", TaskAttachmentDao.class);
injectables.put("taskAttachmentOutstandingDao", TaskAttachmentOutstandingDao.class);
// com.todoroo.astrid.service // com.todoroo.astrid.service
injectables.put("taskService", TaskService.class); injectables.put("taskService", TaskService.class);

@ -52,6 +52,7 @@ import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.Database; import com.todoroo.astrid.dao.Database;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
import com.todoroo.astrid.dao.TagDataDao; import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.UserActivityDao; import com.todoroo.astrid.dao.UserActivityDao;
import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Metadata;
@ -106,6 +107,8 @@ public class StartupService {
@Autowired UserActivityDao userActivityDao; @Autowired UserActivityDao userActivityDao;
@Autowired TaskAttachmentDao taskAttachmentDao;
@Autowired MetadataService metadataService; @Autowired MetadataService metadataService;
@Autowired Database database; @Autowired Database database;
@ -221,7 +224,7 @@ public class StartupService {
abTestInvoker.reportAcquisition(); abTestInvoker.reportAcquisition();
initializeDatabaseListeners(); initializeDatabaseListeners();
ActFmSyncThread.initializeSyncComponents(taskDao, tagDataDao, userActivityDao); ActFmSyncThread.initializeSyncComponents(taskDao, tagDataDao, userActivityDao, taskAttachmentDao);
// perform startup activities in a background thread // perform startup activities in a background thread
new Thread(new Runnable() { new Thread(new Runnable() {

@ -46,12 +46,12 @@ import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.activity.TaskListFragment; import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.activity.TaskListFragment.OnTaskListItemClickedListener; import com.todoroo.astrid.activity.TaskListFragment.OnTaskListItemClickedListener;
import com.todoroo.astrid.core.PluginServices; import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.TaskAttachmentDao;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.SyncFlags; import com.todoroo.astrid.data.SyncFlags;
import com.todoroo.astrid.data.TagData; import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.files.FileMetadata; import com.todoroo.astrid.data.TaskAttachment;
import com.todoroo.astrid.files.FileUtilities; import com.todoroo.astrid.files.FileUtilities;
import com.todoroo.astrid.gcal.GCalControlSet; import com.todoroo.astrid.gcal.GCalControlSet;
import com.todoroo.astrid.gcal.GCalHelper; import com.todoroo.astrid.gcal.GCalHelper;
@ -90,6 +90,8 @@ public class QuickAddBar extends LinearLayout {
@Autowired ExceptionService exceptionService; @Autowired ExceptionService exceptionService;
@Autowired MetadataService metadataService; @Autowired MetadataService metadataService;
@Autowired ActFmPreferenceService actFmPreferenceService; @Autowired ActFmPreferenceService actFmPreferenceService;
@Autowired
private TaskAttachmentDao taskAttachmentDao;
private VoiceRecognizer voiceRecognizer; private VoiceRecognizer voiceRecognizer;
@ -361,8 +363,8 @@ public class QuickAddBar extends LinearLayout {
voiceRecognizer.convert(path); voiceRecognizer.convert(path);
currentVoiceFile = null; currentVoiceFile = null;
Metadata fileMetadata = FileMetadata.createNewFileMetadata(task.getId(), path, nameRef.get(), FileMetadata.FILE_TYPE_AUDIO + "m4a"); TaskAttachment attachment = TaskAttachment.createNewAttachment(task.getUuid(), path, nameRef.get(), TaskAttachment.FILE_TYPE_AUDIO + "m4a");
metadataService.save(fileMetadata); taskAttachmentDao.createNew(attachment);
} }
fragment.onTaskCreated(task); fragment.onTaskCreated(task);

Loading…
Cancel
Save