mirror of https://github.com/tasks/tasks
Remove ActFm synchronization code
* Remove client<->server messages * Remove SyncService, SyncThread, Invokerpull/46/head
parent
580ab99d91
commit
30c972d7dc
@ -1,53 +0,0 @@
|
||||
package com.todoroo.astrid.sync;
|
||||
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ConstructTaskOutstandingTableFromMasterTable;
|
||||
import com.todoroo.astrid.actfm.sync.messages.NameMaps;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.SyncFlags;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.TaskOutstanding;
|
||||
import com.todoroo.astrid.tags.TaskToTagMetadata;
|
||||
|
||||
public class ConstructOutstandingFromMasterTest extends NewSyncTestCase {
|
||||
|
||||
public void testConstructOutstandingConstructsOutstanding() {
|
||||
Task t = createTask(true);
|
||||
Metadata m = TaskToTagMetadata.newTagMetadata(t.getId(), t.getUuid(), "Tag", "2");
|
||||
m.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
metadataDao.createNew(m);
|
||||
TodorooCursor<TaskOutstanding> to = taskOutstandingDao.query(Query.select(TaskOutstanding.PROPERTIES));
|
||||
try {
|
||||
assertEquals(0, to.getCount());
|
||||
} finally {
|
||||
to.close();
|
||||
}
|
||||
|
||||
new ConstructTaskOutstandingTableFromMasterTable(NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, metadataDao, Task.CREATION_DATE).execute();
|
||||
|
||||
Property<?>[] syncable = NameMaps.syncableProperties(NameMaps.TABLE_ID_TASKS);
|
||||
for (Property<?> p : syncable) {
|
||||
to = taskOutstandingDao.query(Query.select(TaskOutstanding.PROPERTIES).where(TaskOutstanding.COLUMN_STRING.eq(p.name)));
|
||||
try {
|
||||
assertEquals(1, to.getCount());
|
||||
to.moveToFirst();
|
||||
String value = t.getValue(p).toString();
|
||||
assertEquals(value, to.get(TaskOutstanding.VALUE_STRING));
|
||||
} finally {
|
||||
to.close();
|
||||
}
|
||||
}
|
||||
|
||||
to = taskOutstandingDao.query(Query.select(TaskOutstanding.PROPERTIES).where(TaskOutstanding.COLUMN_STRING.eq(NameMaps.TAG_ADDED_COLUMN)));
|
||||
try {
|
||||
assertEquals(1, to.getCount());
|
||||
to.moveToFirst();
|
||||
assertEquals("2", to.get(TaskOutstanding.VALUE_STRING));
|
||||
} finally {
|
||||
to.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,172 +0,0 @@
|
||||
package com.todoroo.astrid.sync;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmSyncThread.ModelType;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ChangesHappened;
|
||||
import com.todoroo.astrid.actfm.sync.messages.NameMaps;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ReplayOutstandingEntries;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ServerToClientMessage;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.SyncFlags;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.TaskOutstanding;
|
||||
|
||||
|
||||
public class SyncMessageTest extends NewSyncTestCase {
|
||||
|
||||
public void testTaskChangesHappenedConstructor() {
|
||||
Task t = createTask();
|
||||
try {
|
||||
ChangesHappened<?, ?> changes = ChangesHappened.instantiateChangesHappened(t.getId(), ModelType.TYPE_TASK);
|
||||
assertNotNull(changes.serializeToJSON(null));
|
||||
assertTrue(changes.getChanges().size() > 0);
|
||||
assertFalse(RemoteModel.NO_UUID.equals(changes.getUUID()));
|
||||
assertEquals(t.getValue(Task.UUID), changes.getUUID());
|
||||
} catch (Exception e) {
|
||||
fail("ChangesHappened constructor threw exception " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String MAKE_CHANGES_TITLE = "Made changes to title";
|
||||
private JSONObject getMakeChanges(String... extraChanges) throws JSONException {
|
||||
JSONObject makeChanges = new JSONObject();
|
||||
makeChanges.put("type", ServerToClientMessage.TYPE_MAKE_CHANGES);
|
||||
makeChanges.put("table", NameMaps.TABLE_ID_TASKS);
|
||||
|
||||
JSONObject changes = new JSONObject();
|
||||
changes.put("title", MAKE_CHANGES_TITLE);
|
||||
changes.put("importance", Task.IMPORTANCE_DO_OR_DIE);
|
||||
if (extraChanges != null) {
|
||||
for (int i = 0; i < extraChanges.length - 1; i+=2) {
|
||||
String key = extraChanges[i];
|
||||
String value = extraChanges[i + 1];
|
||||
changes.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
makeChanges.put("changes", changes);
|
||||
return makeChanges;
|
||||
}
|
||||
|
||||
public void testMakeChangesMakesChanges() {
|
||||
Task t = createTask();
|
||||
try {
|
||||
JSONObject makeChanges = getMakeChanges();
|
||||
makeChanges.put("uuid", t.getValue(Task.UUID));
|
||||
|
||||
ServerToClientMessage message = ServerToClientMessage.instantiateMessage(makeChanges);
|
||||
message.processMessage(null);
|
||||
|
||||
t = taskDao.fetch(t.getId(), Task.TITLE, Task.IMPORTANCE);
|
||||
assertEquals(MAKE_CHANGES_TITLE, t.getValue(Task.TITLE));
|
||||
assertEquals(Task.IMPORTANCE_DO_OR_DIE, t.getValue(Task.IMPORTANCE).intValue());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
fail("JSONException");
|
||||
}
|
||||
}
|
||||
|
||||
public void testMakeChangesMakesNewTasks() {
|
||||
try {
|
||||
JSONObject makeChanges = getMakeChanges();
|
||||
makeChanges.put("uuid", "1");
|
||||
ServerToClientMessage message = ServerToClientMessage.instantiateMessage(makeChanges);
|
||||
message.processMessage(null);
|
||||
|
||||
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.ID, Task.UUID, Task.TITLE, Task.IMPORTANCE).where(Task.UUID.eq("1")));
|
||||
try {
|
||||
assertEquals(1, cursor.getCount());
|
||||
cursor.moveToFirst();
|
||||
Task t = new Task(cursor);
|
||||
|
||||
assertEquals(MAKE_CHANGES_TITLE, t.getValue(Task.TITLE));
|
||||
assertEquals(Task.IMPORTANCE_DO_OR_DIE, t.getValue(Task.IMPORTANCE).intValue());
|
||||
assertEquals("1", t.getValue(Task.UUID));
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
fail("JSONException");
|
||||
}
|
||||
}
|
||||
|
||||
public void testMakeChangesWithUuidCollision() {
|
||||
Task t = createTask();
|
||||
String oldUuid = t.getUuid();
|
||||
try {
|
||||
String newUuid = oldUuid + "blorp";
|
||||
JSONObject makeChanges = getMakeChanges("uuid", newUuid);
|
||||
makeChanges.put("uuid", oldUuid);
|
||||
ServerToClientMessage message = ServerToClientMessage.instantiateMessage(makeChanges);
|
||||
message.processMessage(null);
|
||||
|
||||
TodorooCursor<Task> cursor = taskDao.query(Query.select(Task.UUID).where(Task.UUID.eq(newUuid)));
|
||||
try {
|
||||
assertEquals(1, cursor.getCount());
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
cursor = taskDao.query(Query.select(Task.UUID).where(Task.UUID.eq(oldUuid)));
|
||||
try {
|
||||
assertEquals(0, cursor.getCount());
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
fail("JSONException");
|
||||
}
|
||||
}
|
||||
|
||||
public void testReplayOutstandingEntries() {
|
||||
Task t = createTask();
|
||||
|
||||
t.setValue(Task.TITLE, "change title");
|
||||
t.setValue(Task.IMPORTANCE, Task.IMPORTANCE_NONE);
|
||||
t.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
taskDao.save(t);
|
||||
|
||||
new ReplayOutstandingEntries<Task, TaskOutstanding>(Task.class, NameMaps.TABLE_ID_TASKS, taskDao, taskOutstandingDao, false).execute();
|
||||
|
||||
t = taskDao.fetch(t.getId(), Task.TITLE, Task.IMPORTANCE);
|
||||
assertEquals(SYNC_TASK_TITLE, t.getValue(Task.TITLE));
|
||||
assertEquals(SYNC_TASK_IMPORTANCE, t.getValue(Task.IMPORTANCE).intValue());
|
||||
}
|
||||
|
||||
public void testUserMigratedUpdatesUserIds() {
|
||||
Task t = createTask("Someone else's", true);
|
||||
TagData td = createTagData("Someone else's tag", true);
|
||||
|
||||
t.setValue(Task.USER_ID, "1");
|
||||
taskDao.save(t);
|
||||
td.setValue(TagData.USER_ID, "1");
|
||||
tagDataDao.saveExisting(td);
|
||||
|
||||
JSONObject userMigrated = new JSONObject();
|
||||
try {
|
||||
userMigrated.put("type", ServerToClientMessage.TYPE_USER_MIGRATED);
|
||||
userMigrated.put("new_user_id", "1");
|
||||
} catch (JSONException e) {
|
||||
fail("JSONException");
|
||||
}
|
||||
|
||||
ServerToClientMessage message = ServerToClientMessage.instantiateMessage(userMigrated);
|
||||
message.processMessage(null);
|
||||
|
||||
t = taskDao.fetch(t.getId(), Task.USER_ID);
|
||||
td = tagDataDao.fetch(td.getId(), TagData.USER_ID);
|
||||
assertEquals("1", ActFmPreferenceService.userId());
|
||||
assertEquals(Task.USER_ID_SELF, t.getValue(Task.USER_ID));
|
||||
assertEquals(Task.USER_ID_SELF, td.getValue(TagData.USER_ID));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,320 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.entity.mime.MultipartEntity;
|
||||
import org.apache.http.entity.mime.content.StringBody;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.tasks.R;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.RestClient;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.andlib.utility.Pair;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.utility.Constants;
|
||||
|
||||
public class ActFmInvoker {
|
||||
|
||||
/** NOTE: these values are development values & will not work on production */
|
||||
private static final String URL = "//10.0.2.2:3000/api/";
|
||||
private static final String APP_ID = "a4732a32859dbcd3e684331acd36432c";
|
||||
private static final String APP_SECRET = "e389bfc82a0d932332f9a8bd8203735f";
|
||||
|
||||
public static final String PROVIDER_GOOGLE= "google";
|
||||
public static final String PROVIDER_PASSWORD = "password";
|
||||
|
||||
private static final int API_VERSION = 7;
|
||||
|
||||
public static final boolean SYNC_DEBUG = Constants.DEBUG || true;
|
||||
|
||||
@Autowired private RestClient restClient;
|
||||
|
||||
private String token = null;
|
||||
|
||||
// --- initialization, getters, setters
|
||||
|
||||
/**
|
||||
* Create new api invoker service without a token
|
||||
*/
|
||||
public ActFmInvoker() {
|
||||
//
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new api invoker service with a token
|
||||
* @token access token
|
||||
*/
|
||||
public ActFmInvoker(String token) {
|
||||
this.token = token;
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
// --- special method invocations
|
||||
|
||||
/**
|
||||
* Authentication user with Act.fm server, returning a token
|
||||
*/
|
||||
public JSONObject authenticate(String email, String firstName, String lastName, String provider,
|
||||
String secret) throws ActFmServiceException, IOException {
|
||||
JSONObject result = invoke(
|
||||
"user_signin",
|
||||
"email", email,
|
||||
"first_name", firstName,
|
||||
"last_name", lastName,
|
||||
"provider", provider,
|
||||
"secret", secret,
|
||||
"timezone", TimeZone.getDefault().getID());
|
||||
try {
|
||||
token = result.getString("token");
|
||||
} catch (JSONException e) {
|
||||
throw new IOException(e.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// --- invocation
|
||||
|
||||
/**
|
||||
* Invokes API method using HTTP GET
|
||||
*
|
||||
* @param method
|
||||
* API method to invoke
|
||||
* @param getParameters
|
||||
* Name/Value pairs. Values will be URL encoded.
|
||||
* @return response object
|
||||
*/
|
||||
public JSONObject invoke(String method, Object... getParameters) throws IOException,
|
||||
ActFmServiceException {
|
||||
return invokeWithApi(null, method, getParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes API method using HTTP GET
|
||||
*
|
||||
* @param method
|
||||
* API method to invoke
|
||||
* @param getParameters
|
||||
* Name/Value pairs. Values will be URL encoded.
|
||||
* @return response object
|
||||
*/
|
||||
public JSONObject invokeWithApi(String api, String method, Object... getParameters) throws IOException,
|
||||
ActFmServiceException {
|
||||
try {
|
||||
String request = createFetchUrl(api, method, getParameters);
|
||||
|
||||
if (SYNC_DEBUG) {
|
||||
Log.e("act-fm-invoke", request);
|
||||
}
|
||||
|
||||
String response = restClient.get(request);
|
||||
JSONObject object = new JSONObject(response);
|
||||
|
||||
if (SYNC_DEBUG) {
|
||||
AndroidUtilities.logJSONObject("act-fm-invoke-response", object);
|
||||
}
|
||||
if(object.getString("status").equals("error")) {
|
||||
throw new ActFmServiceException(object.getString("message"), object);
|
||||
}
|
||||
return object;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes API method using HTTP POST
|
||||
*
|
||||
* @param method
|
||||
* API method to invoke
|
||||
* @param data
|
||||
* data to transmit
|
||||
* @param getParameters
|
||||
* Name/Value pairs. Values will be URL encoded.
|
||||
* @return response object
|
||||
*/
|
||||
public JSONObject post(String method, HttpEntity data, Object... getParameters) throws IOException,
|
||||
ActFmServiceException {
|
||||
try {
|
||||
String request = createFetchUrl(null, method, getParameters);
|
||||
|
||||
if (SYNC_DEBUG) {
|
||||
Log.e("act-fm-post", request);
|
||||
}
|
||||
|
||||
String response = restClient.post(request, data);
|
||||
JSONObject object = new JSONObject(response);
|
||||
|
||||
if (SYNC_DEBUG) {
|
||||
AndroidUtilities.logJSONObject("act-fm-post-response", object);
|
||||
}
|
||||
|
||||
if(object.getString("status").equals("error")) {
|
||||
throw new ActFmServiceException(object.getString("message"), object);
|
||||
}
|
||||
return object;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject postSync(String data, MultipartEntity entity, boolean changesHappened, String tok) throws IOException {
|
||||
try {
|
||||
String timeString = DateUtilities.timeToIso8601(DateUtilities.now(), true);
|
||||
|
||||
Object[] params = { "token", tok, "data", data, "time", timeString };
|
||||
|
||||
String request = createFetchUrl("api/" + API_VERSION, "synchronize", params);
|
||||
if (SYNC_DEBUG) {
|
||||
Log.e("act-fm-post", request);
|
||||
}
|
||||
Charset chars;
|
||||
try {
|
||||
chars = Charset.forName("UTF-8");
|
||||
} catch (Exception e) {
|
||||
chars = null;
|
||||
}
|
||||
|
||||
entity.addPart("token", new StringBody(tok));
|
||||
entity.addPart("data", new StringBody(data, chars));
|
||||
entity.addPart("time", new StringBody(timeString));
|
||||
|
||||
String response = restClient.post(request, entity);
|
||||
JSONObject object = new JSONObject(response);
|
||||
|
||||
if (SYNC_DEBUG) {
|
||||
AndroidUtilities.logJSONObject("act-fm-post-response", object);
|
||||
}
|
||||
|
||||
if(object.getString("status").equals("error")) {
|
||||
throw new ActFmServiceException(object.getString("message"), object);
|
||||
}
|
||||
return object;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException(e.getMessage());
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a URL for invoking an HTTP GET/POST on the given method
|
||||
* @param method
|
||||
* @param getParameters
|
||||
* @return
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
private String createFetchUrl(String api, String method, Object... getParameters) throws UnsupportedEncodingException, NoSuchAlgorithmException {
|
||||
ArrayList<Pair<String, Object>> params = new ArrayList<Pair<String, Object>>();
|
||||
for(int i = 0; i < getParameters.length; i += 2) {
|
||||
if(getParameters[i+1] instanceof ArrayList) {
|
||||
ArrayList<?> list = (ArrayList<?>) getParameters[i+1];
|
||||
for(int j = 0; j < list.size(); j++) {
|
||||
params.add(new Pair<String, Object>(getParameters[i].toString() + "[]",
|
||||
list.get(j)));
|
||||
}
|
||||
} else {
|
||||
params.add(new Pair<String, Object>(getParameters[i].toString(), getParameters[i + 1]));
|
||||
}
|
||||
}
|
||||
params.add(new Pair<String, Object>("app_id", APP_ID));
|
||||
boolean syncMethod = "synchronize".equals(method);
|
||||
|
||||
if (!syncMethod) {
|
||||
params.add(new Pair<String, Object>("time", System.currentTimeMillis() / 1000L));
|
||||
}
|
||||
if(token != null) {
|
||||
boolean foundTokenKey = false;
|
||||
for (Pair<String, Object> curr : params) {
|
||||
if (curr.getLeft().equals("token")) {
|
||||
foundTokenKey = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundTokenKey) {
|
||||
params.add(new Pair<String, Object>("token", token));
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(params, new Comparator<Pair<String, Object>>() {
|
||||
@Override
|
||||
public int compare(Pair<String, Object> object1,
|
||||
Pair<String, Object> object2) {
|
||||
int result = object1.getLeft().compareTo(object2.getLeft());
|
||||
if(result == 0) {
|
||||
return object1.getRight().toString().compareTo(object2.getRight().toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
String url = URL;
|
||||
boolean customApi = false;
|
||||
if (api != null) {
|
||||
customApi = true;
|
||||
url = url.replace("api", api);
|
||||
}
|
||||
if (Preferences.getBoolean(R.string.actfm_https_key, false)) {
|
||||
url = "https:" + url;
|
||||
} else {
|
||||
url = "http:" + url;
|
||||
}
|
||||
|
||||
StringBuilder requestBuilder = new StringBuilder(url);
|
||||
if (!customApi) {
|
||||
requestBuilder.append(API_VERSION).append("/");
|
||||
}
|
||||
requestBuilder.append(method).append('?');
|
||||
StringBuilder sigBuilder = new StringBuilder(method);
|
||||
for(Pair<String, Object> entry : params) {
|
||||
if(entry.getRight() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = entry.getLeft();
|
||||
String value = entry.getRight().toString();
|
||||
String encoded = URLEncoder.encode(value, "UTF-8");
|
||||
|
||||
if (!syncMethod || "app_id".equals(key)) {
|
||||
requestBuilder.append(key).append('=').append(encoded).append('&');
|
||||
}
|
||||
|
||||
sigBuilder.append(key).append(value);
|
||||
}
|
||||
|
||||
sigBuilder.append(APP_SECRET);
|
||||
// String signature = DigestUtils.md5Hex(sigBuilder.toString());
|
||||
// requestBuilder.append("sig").append('=').append(signature);
|
||||
return requestBuilder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/**
|
||||
* Exception that wraps an exception encountered during API invocation or
|
||||
* processing.
|
||||
*
|
||||
* @author timsu
|
||||
*
|
||||
*/
|
||||
public class ActFmServiceException extends IOException {
|
||||
|
||||
private static final long serialVersionUID = -2803924196075428257L;
|
||||
|
||||
public JSONObject result;
|
||||
|
||||
public ActFmServiceException(String detailMessage, JSONObject result) {
|
||||
super(detailMessage);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public ActFmServiceException(Throwable throwable, JSONObject result) {
|
||||
super(throwable.getMessage());
|
||||
initCause(throwable);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public ActFmServiceException(JSONObject result) {
|
||||
super();
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + ": " + getMessage(); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
public class ActFmSyncMonitor {
|
||||
private ActFmSyncMonitor() {/**/}
|
||||
private static final ActFmSyncMonitor INSTANCE = new ActFmSyncMonitor();
|
||||
|
||||
public static ActFmSyncMonitor getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
@ -1,156 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 Todoroo Inc
|
||||
*
|
||||
* See the file "LICENSE" for the full license governing this code.
|
||||
*/
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.User;
|
||||
import com.todoroo.astrid.service.TagDataService;
|
||||
import com.todoroo.astrid.tags.reusable.FeaturedListFilterExposer;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Service for synchronizing data on Astrid.com server with local.
|
||||
*
|
||||
* @author Tim Su <tim@todoroo.com>
|
||||
*
|
||||
*/
|
||||
public final class ActFmSyncService {
|
||||
|
||||
// --- instance variables
|
||||
|
||||
@Autowired
|
||||
private TagDataService tagDataService;
|
||||
@Autowired
|
||||
private ActFmPreferenceService actFmPreferenceService;
|
||||
@Autowired
|
||||
private ActFmInvoker actFmInvoker;
|
||||
|
||||
private String token;
|
||||
|
||||
public ActFmSyncService() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
// --- data fetch methods
|
||||
public int fetchFeaturedLists(int serverTime) throws JSONException, IOException {
|
||||
if (!checkForToken()) {
|
||||
return 0;
|
||||
}
|
||||
JSONObject result = actFmInvoker.invoke("featured_lists",
|
||||
"token", token, "modified_after", serverTime);
|
||||
JSONArray featuredLists = result.getJSONArray("list");
|
||||
if (featuredLists.length() > 0) {
|
||||
Preferences.setBoolean(FeaturedListFilterExposer.PREF_SHOULD_SHOW_FEATURED_LISTS, true);
|
||||
}
|
||||
|
||||
for (int i = 0; i < featuredLists.length(); i++) {
|
||||
JSONObject featObject = featuredLists.getJSONObject(i);
|
||||
tagDataService.saveFeaturedList(featObject);
|
||||
}
|
||||
|
||||
return result.optInt("time", 0);
|
||||
}
|
||||
|
||||
// --- generic invokation
|
||||
|
||||
/** invoke authenticated method against the server */
|
||||
public JSONObject invoke(String method, Object... getParameters) throws IOException,
|
||||
ActFmServiceException {
|
||||
if(!checkForToken()) {
|
||||
throw new ActFmServiceException("not logged in", null);
|
||||
}
|
||||
Object[] parameters = new Object[getParameters.length + 2];
|
||||
parameters[0] = "token";
|
||||
parameters[1] = token;
|
||||
for(int i = 0; i < getParameters.length; i++) {
|
||||
parameters[i + 2] = getParameters[i];
|
||||
}
|
||||
return actFmInvoker.invoke(method, parameters);
|
||||
}
|
||||
|
||||
protected void handleException(String message, Exception exception) {
|
||||
Log.w("actfm-sync", message, exception);
|
||||
}
|
||||
|
||||
private boolean checkForToken() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// --- json reader helper
|
||||
|
||||
/**
|
||||
* Read data models from JSON
|
||||
*/
|
||||
public static class JsonHelper {
|
||||
|
||||
protected static long readDate(JSONObject item, String key) {
|
||||
return item.optLong(key, 0) * 1000L;
|
||||
}
|
||||
|
||||
public static void jsonFromUser(JSONObject json, User model) throws JSONException {
|
||||
json.put("id", model.getValue(User.UUID));
|
||||
json.put("name", model.getDisplayName());
|
||||
json.put("email", model.getValue(User.EMAIL));
|
||||
json.put("picture", model.getPictureUrl(User.PICTURE, RemoteModel.PICTURE_THUMB));
|
||||
json.put("first_name", model.getValue(User.FIRST_NAME));
|
||||
}
|
||||
|
||||
public static void featuredListFromJson(JSONObject json, TagData model) throws JSONException {
|
||||
parseTagDataFromJson(json, model, true);
|
||||
}
|
||||
|
||||
private static void parseTagDataFromJson(JSONObject json, TagData model, boolean featuredList) throws JSONException {
|
||||
model.clearValue(TagData.UUID);
|
||||
model.setValue(TagData.UUID, Long.toString(json.getLong("id")));
|
||||
model.setValue(TagData.NAME, json.getString("name"));
|
||||
|
||||
if (featuredList) {
|
||||
model.setFlag(TagData.FLAGS, TagData.FLAG_FEATURED, true);
|
||||
}
|
||||
|
||||
if(json.has("picture")) {
|
||||
model.setValue(TagData.PICTURE, json.optString("picture", ""));
|
||||
}
|
||||
if(json.has("thumb")) {
|
||||
model.setValue(TagData.THUMB, json.optString("thumb", ""));
|
||||
}
|
||||
|
||||
if(json.has("is_silent")) {
|
||||
model.setFlag(TagData.FLAGS, TagData.FLAG_SILENT, json.getBoolean("is_silent"));
|
||||
}
|
||||
|
||||
if(!json.isNull("description")) {
|
||||
model.setValue(TagData.TAG_DESCRIPTION, json.getString("description"));
|
||||
}
|
||||
|
||||
if(json.has("members")) {
|
||||
JSONArray members = json.getJSONArray("members");
|
||||
model.setValue(TagData.MEMBERS, members.toString());
|
||||
model.setValue(TagData.MEMBER_COUNT, members.length());
|
||||
}
|
||||
|
||||
if (json.has("deleted_at")) {
|
||||
model.setValue(TagData.DELETION_DATE, readDate(json, "deleted_at"));
|
||||
}
|
||||
|
||||
if(json.has("tasks")) {
|
||||
model.setValue(TagData.TASK_COUNT, json.getInt("tasks"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,271 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.ContextManager;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.andlib.service.NotificationManager;
|
||||
import com.todoroo.andlib.service.NotificationManager.AndroidNotificationManager;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ChangesHappened;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ClientToServerMessage;
|
||||
import com.todoroo.astrid.actfm.sync.messages.NameMaps;
|
||||
import com.todoroo.astrid.actfm.sync.messages.TaskListMetadataChangesHappened;
|
||||
import com.todoroo.astrid.api.AstridApiConstants;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.dao.OutstandingEntryDao;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.dao.TagDataDao;
|
||||
import com.todoroo.astrid.dao.TagOutstandingDao;
|
||||
import com.todoroo.astrid.dao.TaskAttachmentDao;
|
||||
import com.todoroo.astrid.dao.TaskDao;
|
||||
import com.todoroo.astrid.dao.TaskListMetadataDao;
|
||||
import com.todoroo.astrid.dao.TaskListMetadataOutstandingDao;
|
||||
import com.todoroo.astrid.dao.TaskOutstandingDao;
|
||||
import com.todoroo.astrid.dao.UserActivityDao;
|
||||
import com.todoroo.astrid.dao.UserActivityOutstandingDao;
|
||||
import com.todoroo.astrid.data.OutstandingEntry;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.TaskAttachment;
|
||||
import com.todoroo.astrid.data.TaskListMetadata;
|
||||
import com.todoroo.astrid.data.TaskListMetadataOutstanding;
|
||||
import com.todoroo.astrid.data.UserActivity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ActFmSyncThread {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-sync-thread"; //$NON-NLS-1$
|
||||
|
||||
private final List<ClientToServerMessage<?>> pendingMessages;
|
||||
private final Map<ClientToServerMessage<?>, SyncMessageCallback> pendingCallbacks;
|
||||
private final Object monitor;
|
||||
private Thread thread;
|
||||
|
||||
@Autowired
|
||||
private ActFmInvoker actFmInvoker;
|
||||
|
||||
@Autowired
|
||||
private ActFmPreferenceService actFmPreferenceService;
|
||||
|
||||
@Autowired
|
||||
private TaskDao taskDao;
|
||||
|
||||
@Autowired
|
||||
private TaskOutstandingDao taskOutstandingDao;
|
||||
|
||||
@Autowired
|
||||
private TagDataDao tagDataDao;
|
||||
|
||||
@Autowired
|
||||
private TagOutstandingDao tagOutstandingDao;
|
||||
|
||||
@Autowired
|
||||
private UserActivityDao userActivityDao;
|
||||
|
||||
@Autowired
|
||||
private UserActivityOutstandingDao userActivityOutstandingDao;
|
||||
|
||||
@Autowired
|
||||
private TaskListMetadataDao taskListMetadataDao;
|
||||
|
||||
@Autowired
|
||||
private TaskListMetadataOutstandingDao taskListMetadataOutstandingDao;
|
||||
|
||||
private final NotificationManager notificationManager;
|
||||
|
||||
private int notificationId = -1;
|
||||
|
||||
public static interface SyncMessageCallback {
|
||||
public void runOnSuccess();
|
||||
}
|
||||
|
||||
public static enum ModelType {
|
||||
TYPE_TASK,
|
||||
TYPE_TAG,
|
||||
TYPE_ACTIVITY,
|
||||
TYPE_ATTACHMENT,
|
||||
TYPE_TASK_LIST_METADATA
|
||||
}
|
||||
|
||||
private static volatile ActFmSyncThread instance;
|
||||
|
||||
public static ActFmSyncThread getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized(ActFmSyncThread.class) {
|
||||
if (instance == null) {
|
||||
initializeSyncComponents(PluginServices.getTaskDao(), PluginServices.getTagDataDao(), PluginServices.getUserActivityDao(),
|
||||
PluginServices.getTaskAttachmentDao(), PluginServices.getTaskListMetadataDao());
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static ActFmSyncThread initializeSyncComponents(TaskDao taskDao, TagDataDao tagDataDao, UserActivityDao userActivityDao,
|
||||
TaskAttachmentDao taskAttachmentDao, TaskListMetadataDao taskListMetadataDao) {
|
||||
if (instance == null) {
|
||||
synchronized(ActFmSyncThread.class) {
|
||||
if (instance == null) {
|
||||
List<ClientToServerMessage<?>> syncQueue = Collections.synchronizedList(new LinkedList<ClientToServerMessage<?>>());
|
||||
ActFmSyncMonitor monitor = ActFmSyncMonitor.getInstance();
|
||||
ActFmSyncWaitingPool waitingPool = ActFmSyncWaitingPool.getInstance();
|
||||
|
||||
instance = new ActFmSyncThread(syncQueue, monitor);
|
||||
|
||||
taskDao.addListener(new SyncDatabaseListener<Task>(instance, ModelType.TYPE_TASK));
|
||||
tagDataDao.addListener(new SyncDatabaseListener<TagData>(instance, ModelType.TYPE_TAG));
|
||||
userActivityDao.addListener(new SyncDatabaseListener<UserActivity>(instance, ModelType.TYPE_ACTIVITY));
|
||||
taskAttachmentDao.addListener(new SyncDatabaseListener<TaskAttachment>(instance, ModelType.TYPE_ATTACHMENT));
|
||||
taskListMetadataDao.addListener(new TaskListMetadataSyncDatabaseListener(instance, waitingPool, ModelType.TYPE_TASK_LIST_METADATA));
|
||||
|
||||
instance.startSyncThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ActFmSyncThread(List<ClientToServerMessage<?>> messageQueue, Object syncMonitor) {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
this.pendingMessages = messageQueue;
|
||||
this.pendingCallbacks = Collections.synchronizedMap(new HashMap<ClientToServerMessage<?>, SyncMessageCallback>());
|
||||
this.monitor = syncMonitor;
|
||||
this.notificationManager = new AndroidNotificationManager(ContextManager.getContext());
|
||||
}
|
||||
|
||||
public synchronized void startSyncThread() {
|
||||
if (thread == null || !thread.isAlive()) {
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sync();
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearTablePushedAtValues() {
|
||||
String[] pushedAtPrefs = new String[] { NameMaps.PUSHED_AT_TASKS, NameMaps.PUSHED_AT_TAGS, NameMaps.PUSHED_AT_ACTIVITY,
|
||||
NameMaps.PUSHED_AT_USERS, NameMaps.PUSHED_AT_TASK_LIST_METADATA};
|
||||
for (String key : pushedAtPrefs) {
|
||||
Preferences.clear(key);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void enqueueMessage(ClientToServerMessage<?> message, SyncMessageCallback callback) {
|
||||
if (!RemoteModelDao.getOutstandingEntryFlag(RemoteModelDao.OUTSTANDING_ENTRY_FLAG_ENQUEUE_MESSAGES)) {
|
||||
return;
|
||||
}
|
||||
if (!pendingMessages.contains(message)) {
|
||||
pendingMessages.add(message);
|
||||
if (callback != null) {
|
||||
pendingCallbacks.put(message, callback);
|
||||
}
|
||||
synchronized(monitor) {
|
||||
monitor.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static final SyncMessageCallback DEFAULT_REFRESH_RUNNABLE = new SyncMessageCallback() {
|
||||
@Override
|
||||
public void runOnSuccess() {
|
||||
Intent refresh = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH);
|
||||
ContextManager.getContext().sendBroadcast(refresh);
|
||||
}
|
||||
};
|
||||
|
||||
private void sync() {
|
||||
try {
|
||||
while(true) {
|
||||
synchronized(monitor) {
|
||||
while (true) {
|
||||
try {
|
||||
if (notificationId >= 0) {
|
||||
notificationManager.cancel(notificationId);
|
||||
notificationId = -1;
|
||||
}
|
||||
monitor.wait();
|
||||
AndroidUtilities.sleepDeep(500L); // Wait briefly for large database operations to finish (e.g. adding a task with several tags may trigger a message before all saves are done--fix this?)
|
||||
} catch (InterruptedException e) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// In the worst case, restart thread if something goes wrong
|
||||
Log.e(ERROR_TAG, "Unexpected sync thread exception", e);
|
||||
thread = null;
|
||||
startSyncThread();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void repopulateQueueFromOutstandingTables() {
|
||||
syncLog("Constructing queue from outstanding tables"); //$NON-NLS-1$
|
||||
constructChangesHappenedFromOutstandingTable(Task.class, taskDao, taskOutstandingDao);
|
||||
constructChangesHappenedFromOutstandingTable(TagData.class, tagDataDao, tagOutstandingDao);
|
||||
constructChangesHappenedFromOutstandingTable(UserActivity.class, userActivityDao, userActivityOutstandingDao);
|
||||
constructChangesHappenedForTaskListMetadata(taskListMetadataDao, taskListMetadataOutstandingDao);
|
||||
}
|
||||
|
||||
private <T extends RemoteModel, OE extends OutstandingEntry<T>> void constructChangesHappenedFromOutstandingTable(Class<T> modelClass, RemoteModelDao<T> modelDao, OutstandingEntryDao<OE> oustandingDao) {
|
||||
TodorooCursor<OE> outstanding = oustandingDao.query(Query.select(OutstandingEntry.ENTITY_ID_PROPERTY).groupBy(OutstandingEntry.ENTITY_ID_PROPERTY));
|
||||
try {
|
||||
for (outstanding.moveToFirst(); !outstanding.isAfterLast(); outstanding.moveToNext()) {
|
||||
Long id = outstanding.get(OutstandingEntry.ENTITY_ID_PROPERTY);
|
||||
enqueueMessage(new ChangesHappened<T, OE>(id, modelClass, modelDao, oustandingDao), null);
|
||||
}
|
||||
} finally {
|
||||
outstanding.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void constructChangesHappenedForTaskListMetadata(TaskListMetadataDao dao, TaskListMetadataOutstandingDao outstandingDao) {
|
||||
TodorooCursor<TaskListMetadataOutstanding> outstanding = outstandingDao.query(Query.select(OutstandingEntry.ENTITY_ID_PROPERTY).groupBy(OutstandingEntry.ENTITY_ID_PROPERTY));
|
||||
try {
|
||||
for (outstanding.moveToFirst(); !outstanding.isAfterLast(); outstanding.moveToNext()) {
|
||||
Long id = outstanding.get(OutstandingEntry.ENTITY_ID_PROPERTY);
|
||||
ActFmSyncWaitingPool.getInstance().enqueueMessage(new TaskListMetadataChangesHappened(id, TaskListMetadata.class, dao, outstandingDao));
|
||||
}
|
||||
} finally {
|
||||
outstanding.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void syncLog(String message) {
|
||||
if (ActFmInvoker.SYNC_DEBUG) {
|
||||
Log.e(ERROR_TAG, message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class NetworkStateChangedReceiver extends BroadcastReceiver {
|
||||
private static long lastSyncFromNetworkChange = 0;
|
||||
private static final String PREF_LAST_SYNC_FROM_NETWORK_CHANGE = "p_last_sync_from_net_change"; //$NON-NLS-1$
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
lastSyncFromNetworkChange = Preferences.getLong(PREF_LAST_SYNC_FROM_NETWORK_CHANGE, 0L);
|
||||
if (DateUtilities.now() - lastSyncFromNetworkChange > DateUtilities.ONE_MINUTE * 10) {
|
||||
intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ClientToServerMessage;
|
||||
|
||||
public class ActFmSyncWaitingPool {
|
||||
|
||||
private static volatile ActFmSyncWaitingPool instance;
|
||||
|
||||
public static ActFmSyncWaitingPool getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized(ActFmSyncWaitingPool.class) {
|
||||
if (instance == null) {
|
||||
instance = new ActFmSyncWaitingPool();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static final long WAIT_TIME = 15 * 1000L;
|
||||
|
||||
private final ExecutorService singleThreadPool;
|
||||
private final List<ClientToServerMessage<?>> pendingMessages;
|
||||
private final Runnable delayMessageRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (pendingMessages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
AndroidUtilities.sleepDeep(WAIT_TIME);
|
||||
while (!pendingMessages.isEmpty()) {
|
||||
ActFmSyncThread.getInstance().enqueueMessage(pendingMessages.remove(0), ActFmSyncThread.DEFAULT_REFRESH_RUNNABLE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private ActFmSyncWaitingPool() {
|
||||
super();
|
||||
singleThreadPool = Executors.newSingleThreadExecutor();
|
||||
pendingMessages = Collections.synchronizedList(new LinkedList<ClientToServerMessage<?>>());
|
||||
}
|
||||
|
||||
public synchronized void enqueueMessage(ClientToServerMessage<?> message) {
|
||||
if (!pendingMessages.contains(message)) {
|
||||
pendingMessages.add(message);
|
||||
singleThreadPool.submit(delayMessageRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.DatabaseDao.ModelUpdateListener;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmSyncThread.ModelType;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ChangesHappened;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ClientToServerMessage;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
|
||||
public class SyncDatabaseListener<MTYPE extends AbstractModel> implements ModelUpdateListener<MTYPE> {
|
||||
|
||||
private final ModelType modelType;
|
||||
protected final ActFmSyncThread actFmSyncThread;
|
||||
public SyncDatabaseListener(ActFmSyncThread actFmSyncThread, ModelType modelType) {
|
||||
this.actFmSyncThread = actFmSyncThread;
|
||||
this.modelType = modelType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onModelUpdated(MTYPE model, boolean outstandingEntries) {
|
||||
if (outstandingEntries && RemoteModelDao.getOutstandingEntryFlag(RemoteModelDao.OUTSTANDING_ENTRY_FLAG_ENQUEUE_MESSAGES)) {
|
||||
ChangesHappened<?, ?> ch = ChangesHappened.instantiateChangesHappened(model.getId(), modelType);
|
||||
enqueueMessage(model, ch);
|
||||
}
|
||||
}
|
||||
|
||||
protected void enqueueMessage(MTYPE model, ClientToServerMessage<?> message) {
|
||||
actFmSyncThread.enqueueMessage(message, null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync;
|
||||
|
||||
import com.todoroo.astrid.actfm.sync.ActFmSyncThread.ModelType;
|
||||
import com.todoroo.astrid.actfm.sync.messages.ClientToServerMessage;
|
||||
import com.todoroo.astrid.data.TaskListMetadata;
|
||||
|
||||
public class TaskListMetadataSyncDatabaseListener extends SyncDatabaseListener<TaskListMetadata> {
|
||||
|
||||
private final ActFmSyncWaitingPool waitingPool;
|
||||
|
||||
public TaskListMetadataSyncDatabaseListener(ActFmSyncThread actFmSyncThread, ActFmSyncWaitingPool waitingPool, ModelType modelType) {
|
||||
super(actFmSyncThread, modelType);
|
||||
this.waitingPool = waitingPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueMessage(TaskListMetadata model, ClientToServerMessage<?> message) {
|
||||
if (model.getSetValues().containsKey(TaskListMetadata.TASK_IDS.name)) {
|
||||
waitingPool.enqueueMessage(message);
|
||||
} else {
|
||||
actFmSyncThread.enqueueMessage(message, ActFmSyncThread.DEFAULT_REFRESH_RUNNABLE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.dao.OutstandingEntryDao;
|
||||
|
||||
public class AcknowledgeChange extends ServerToClientMessage {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-acknowledge-change"; //$NON-NLS-1$
|
||||
|
||||
private final OutstandingEntryDao<?> dao;
|
||||
|
||||
public AcknowledgeChange(JSONObject json) {
|
||||
super(json);
|
||||
String table = json.optString("table"); //$NON-NLS-1$
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
dao = PluginServices.getTaskOutstandingDao();
|
||||
} else if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
dao = PluginServices.getTagOutstandingDao();
|
||||
} else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) {
|
||||
dao = PluginServices.getUserActivityOutstandingDao();
|
||||
} else if (NameMaps.TABLE_ID_ATTACHMENTS.equals(table)) {
|
||||
dao = PluginServices.getTaskAttachmentOutstandingDao();
|
||||
} else if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table)) {
|
||||
dao = PluginServices.getTaskListMetadataOutstandingDao();
|
||||
} else {
|
||||
dao = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(String serverTime) {
|
||||
JSONArray idsArray = json.optJSONArray("ids"); //$NON-NLS-1$
|
||||
if (idsArray != null && dao != null) {
|
||||
ArrayList<Long> idsList = new ArrayList<Long>();
|
||||
for (int i = 0; i < idsArray.length(); i++) {
|
||||
try {
|
||||
Long id = idsArray.getLong(i);
|
||||
if (id <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idsList.add(id);
|
||||
} catch (JSONException e) {
|
||||
Log.e(ERROR_TAG, "Error getting long from " + idsArray + " at index " + i, e); //$NON-NLS-1$//$NON-NLS-2$
|
||||
}
|
||||
}
|
||||
dao.deleteWhere(AbstractModel.ID_PROPERTY.in(idsList.toArray(new Long[idsList.size()])));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,61 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.apache.http.entity.mime.MultipartEntity;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
|
||||
public class BriefMe<TYPE extends RemoteModel> extends ClientToServerMessage<TYPE> {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-brief-me"; //$NON-NLS-1$
|
||||
|
||||
public static final String TASK_ID_KEY = "task_id"; //$NON-NLS-1$
|
||||
public static final String TAG_ID_KEY = "tag_id"; //$NON-NLS-1$
|
||||
public static final String USER_ID_KEY = "user_id"; //$NON-NLS-1$
|
||||
|
||||
public static <TYPE extends RemoteModel> BriefMe<TYPE> instantiateBriefMeForClass(Class<TYPE> cls, String pushedAtKey) {
|
||||
long pushedAt = Preferences.getLong(pushedAtKey, 0);
|
||||
return new BriefMe<TYPE>(cls, null, pushedAt);
|
||||
}
|
||||
|
||||
public BriefMe(long id, Class<TYPE> modelClass, RemoteModelDao<TYPE> modelDao) {
|
||||
super(id, modelClass, modelDao);
|
||||
this.extraParameters = null;
|
||||
}
|
||||
|
||||
private final Object[] extraParameters;
|
||||
|
||||
public BriefMe(Class<TYPE> modelClass, String uuid, long pushedAt, Object...extraParameters) {
|
||||
super(modelClass, uuid, pushedAt);
|
||||
this.extraParameters = extraParameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean serializeExtrasToJSON(JSONObject serializeTo, MultipartEntity entity) throws JSONException {
|
||||
if (extraParameters != null && extraParameters.length > 0) {
|
||||
for (int i = 0; i < extraParameters.length - 1; i+=2) {
|
||||
try {
|
||||
String key = (String) extraParameters[i];
|
||||
Object value = extraParameters[i + 1];
|
||||
serializeTo.put(key, value);
|
||||
} catch (ClassCastException e) {
|
||||
Log.e(ERROR_TAG, "ClassCastException serializing BriefMe", e); //$NON-NLS-1$
|
||||
} catch (JSONException e) {
|
||||
Log.e(ERROR_TAG, "JSONException serializing BriefMe", e); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTypeString() {
|
||||
return "BriefMe"; //$NON-NLS-1$
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,322 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.Property.PropertyVisitor;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.sql.Order;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmSyncThread.ModelType;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.dao.DaoReflectionHelpers;
|
||||
import com.todoroo.astrid.dao.OutstandingEntryDao;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.OutstandingEntry;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.TagOutstanding;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.TaskAttachment;
|
||||
import com.todoroo.astrid.data.TaskAttachmentOutstanding;
|
||||
import com.todoroo.astrid.data.TaskListMetadata;
|
||||
import com.todoroo.astrid.data.TaskOutstanding;
|
||||
import com.todoroo.astrid.data.UserActivity;
|
||||
import com.todoroo.astrid.data.UserActivityOutstanding;
|
||||
|
||||
public class ChangesHappened<TYPE extends RemoteModel, OE extends OutstandingEntry<TYPE>> extends ClientToServerMessage<TYPE> {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-changes-happened";
|
||||
|
||||
protected final Class<OE> outstandingClass;
|
||||
protected final List<OE> changes;
|
||||
protected final OutstandingEntryDao<OE> outstandingDao;
|
||||
|
||||
public static final String CHANGES_KEY = "changes";
|
||||
|
||||
public static ChangesHappened<?, ?> instantiateChangesHappened(long id, ModelType modelType) {
|
||||
switch(modelType) {
|
||||
case TYPE_TASK:
|
||||
return new ChangesHappened<Task, TaskOutstanding>(id, Task.class,
|
||||
PluginServices.getTaskDao(), PluginServices.getTaskOutstandingDao());
|
||||
case TYPE_TAG:
|
||||
return new ChangesHappened<TagData, TagOutstanding>(id, TagData.class,
|
||||
PluginServices.getTagDataDao(), PluginServices.getTagOutstandingDao());
|
||||
case TYPE_ACTIVITY:
|
||||
return new ChangesHappened<UserActivity, UserActivityOutstanding>(id, UserActivity.class,
|
||||
PluginServices.getUserActivityDao(), PluginServices.getUserActivityOutstandingDao());
|
||||
case TYPE_ATTACHMENT:
|
||||
return new ChangesHappened<TaskAttachment, TaskAttachmentOutstanding>(id, TaskAttachment.class,
|
||||
PluginServices.getTaskAttachmentDao(), PluginServices.getTaskAttachmentOutstandingDao());
|
||||
case TYPE_TASK_LIST_METADATA:
|
||||
return new TaskListMetadataChangesHappened(id, TaskListMetadata.class,
|
||||
PluginServices.getTaskListMetadataDao(), PluginServices.getTaskListMetadataOutstandingDao());
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ChangesHappened(long id, Class<TYPE> modelClass, RemoteModelDao<TYPE> modelDao,
|
||||
OutstandingEntryDao<OE> outstandingDao) {
|
||||
super(id, modelClass, modelDao);
|
||||
|
||||
this.outstandingClass = DaoReflectionHelpers.getOutstandingClass(modelClass);
|
||||
this.outstandingDao = outstandingDao;
|
||||
this.changes = new ArrayList<OE>();
|
||||
|
||||
if (!foundEntity) // Stop sending changes for entities that don't exist anymore
|
||||
{
|
||||
outstandingDao.deleteWhere(OutstandingEntry.ENTITY_ID_PROPERTY.eq(id));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean serializeExtrasToJSON(JSONObject serializeTo, MultipartEntity entity) throws JSONException {
|
||||
// Process changes list and serialize to JSON
|
||||
JSONArray changesJson = changesToJSON(entity);
|
||||
if (changesJson == null || changesJson.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
serializeTo.put(CHANGES_KEY, changesJson);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTypeString() {
|
||||
return "ChangesHappened";
|
||||
}
|
||||
|
||||
public List<OE> getChanges() {
|
||||
return changes;
|
||||
}
|
||||
|
||||
private JSONArray changesToJSON(MultipartEntity entity) {
|
||||
if (!RemoteModel.NO_UUID.equals(uuid)) {
|
||||
populateChanges();
|
||||
}
|
||||
|
||||
JSONArray array = new JSONArray();
|
||||
AtomicInteger uploadCounter = new AtomicInteger();
|
||||
PropertyToJSONVisitor visitor = new PropertyToJSONVisitor();
|
||||
for (OE change : changes) {
|
||||
try {
|
||||
String localColumn = change.getValue(OutstandingEntry.COLUMN_STRING_PROPERTY);
|
||||
JSONObject changeJson = new JSONObject();
|
||||
changeJson.put("id", change.getId());
|
||||
String serverColumn;
|
||||
if (NameMaps.TAG_ADDED_COLUMN.equals(localColumn)) {
|
||||
serverColumn = NameMaps.TAG_ADDED_COLUMN;
|
||||
changeJson.put("value", change.getValue(OutstandingEntry.VALUE_STRING_PROPERTY));
|
||||
} else if (NameMaps.TAG_REMOVED_COLUMN.equals(localColumn)) {
|
||||
serverColumn = NameMaps.TAG_REMOVED_COLUMN;
|
||||
changeJson.put("value", change.getValue(OutstandingEntry.VALUE_STRING_PROPERTY));
|
||||
} else if (NameMaps.MEMBER_ADDED_COLUMN.equals(localColumn)) {
|
||||
serverColumn = NameMaps.MEMBER_ADDED_COLUMN;
|
||||
changeJson.put("value", change.getValue(OutstandingEntry.VALUE_STRING_PROPERTY));
|
||||
} else if (NameMaps.MEMBER_REMOVED_COLUMN.equals(localColumn)) {
|
||||
serverColumn = NameMaps.MEMBER_REMOVED_COLUMN;
|
||||
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));
|
||||
String name = fileJson == null ? null : addToEntityFromFileJson(entity, fileJson, uploadCounter);
|
||||
if (name == null) {
|
||||
PluginServices.getTaskAttachmentDao().delete(id);
|
||||
PluginServices.getTaskAttachmentOutstandingDao().deleteWhere(TaskAttachmentOutstanding.ENTITY_ID_PROPERTY.eq(id));
|
||||
return null;
|
||||
}
|
||||
changeJson.put("value", name);
|
||||
} else {
|
||||
Property<?> localProperty = NameMaps.localColumnNameToProperty(table, localColumn);
|
||||
if (localProperty == null) {
|
||||
throw new RuntimeException("No local property found for local column " + localColumn + " in table " + table);
|
||||
}
|
||||
|
||||
serverColumn = NameMaps.localColumnNameToServerColumnName(table, localColumn);
|
||||
if (serverColumn == null) {
|
||||
throw new RuntimeException("No server column found for local column " + localColumn + " in table " + table);
|
||||
}
|
||||
|
||||
Object value = localProperty.accept(visitor, change);
|
||||
if (!validateValue(localProperty, value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
changeJson.put("value", JSONObject.NULL);
|
||||
} else {
|
||||
if (localProperty.checkFlag(Property.PROP_FLAG_PICTURE) && value instanceof JSONObject) {
|
||||
JSONObject json = (JSONObject) value;
|
||||
String name = addToEntityFromFileJson(entity, json, uploadCounter);
|
||||
if (name != null) {
|
||||
changeJson.put("value", name);
|
||||
}
|
||||
} else {
|
||||
changeJson.put("value", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changeJson.put("column", serverColumn);
|
||||
|
||||
String createdAt = DateUtilities.timeToIso8601(change.getValue(OutstandingEntry.CREATED_AT_PROPERTY), true);
|
||||
changeJson.put("created_at", createdAt != null ? createdAt : 0);
|
||||
|
||||
array.put(changeJson);
|
||||
} catch (JSONException e) {
|
||||
Log.e(ERROR_TAG, "Error writing change to JSON", e);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private String addToEntityFromFileJson(MultipartEntity entity, JSONObject json, AtomicInteger uploadCounter) {
|
||||
if (json.has("path")) {
|
||||
String path = json.optString("path");
|
||||
String name = String.format("upload-%s-%s-%d", table, uuid, uploadCounter.get());
|
||||
String type = json.optString("type");
|
||||
File f = new File(path);
|
||||
if (f.exists()) {
|
||||
json.remove("path");
|
||||
entity.addPart(name, new FileBody(f, type));
|
||||
return name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void populateChanges() {
|
||||
TodorooCursor<OE> cursor = outstandingDao.query(Query.select(DaoReflectionHelpers.getModelProperties(outstandingClass))
|
||||
.where(OutstandingEntry.ENTITY_ID_PROPERTY.eq(id)).orderBy(Order.asc(OutstandingEntry.CREATED_AT_PROPERTY)));
|
||||
try {
|
||||
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||||
try {
|
||||
OE instance = outstandingClass.newInstance();
|
||||
instance.readPropertiesFromCursor(cursor);
|
||||
changes.add(instance);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e("ChangesHappened", "Error instantiating outstanding model class", e);
|
||||
} catch (InstantiationException e2) {
|
||||
Log.e("ChangesHappened", "Error instantiating outstanding model class", e2);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Return false if value is detected to be something that would definitely cause a server error
|
||||
// (e.g. empty task title, etc.)
|
||||
private boolean validateValue(Property<?> property, Object value) {
|
||||
if (Task.TITLE.equals(property)) {
|
||||
if (!(value instanceof String) || TextUtils.isEmpty((String) value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return obj;
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class PropertyToJSONVisitor implements PropertyVisitor<Object, OE> {
|
||||
|
||||
private String getAsString(OE data) {
|
||||
return data.getValue(OutstandingEntry.VALUE_STRING_PROPERTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitInteger(Property<Integer> property, OE data) {
|
||||
Integer i = data.getMergedValues().getAsInteger(OutstandingEntry.VALUE_STRING_PROPERTY.name);
|
||||
if (i != null) {
|
||||
if (property.checkFlag(Property.PROP_FLAG_BOOLEAN)) {
|
||||
return i > 0;
|
||||
}
|
||||
return i;
|
||||
} else {
|
||||
return getAsString(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitLong(Property<Long> property, OE data) {
|
||||
Long l = data.getMergedValues().getAsLong(OutstandingEntry.VALUE_STRING_PROPERTY.name);
|
||||
if (l != null) {
|
||||
if (property.checkFlag(Property.PROP_FLAG_DATE)) {
|
||||
boolean includeTime = true;
|
||||
if (Task.DUE_DATE.equals(property) && !Task.hasDueTime(l)) {
|
||||
includeTime = false;
|
||||
}
|
||||
return DateUtilities.timeToIso8601(l, includeTime);
|
||||
}
|
||||
return l;
|
||||
} else {
|
||||
return getAsString(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitDouble(Property<Double> property, OE data) {
|
||||
Double d = data.getMergedValues().getAsDouble(OutstandingEntry.VALUE_STRING_PROPERTY.name);
|
||||
if (d != null) {
|
||||
return d;
|
||||
} else {
|
||||
return getAsString(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitString(Property<String> property, OE data) {
|
||||
String value = getAsString(data);
|
||||
if (RemoteModel.NO_UUID.equals(value) && property.checkFlag(Property.PROP_FLAG_USER_ID)) {
|
||||
return ActFmPreferenceService.userId();
|
||||
}
|
||||
if (property.checkFlag(Property.PROP_FLAG_JSON)) {
|
||||
if (TextUtils.isEmpty(value)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (value != null && value.startsWith("[")) {
|
||||
return new JSONArray(value);
|
||||
} else {
|
||||
return new JSONObject(value);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.apache.http.entity.mime.MultipartEntity;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.Table;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.astrid.dao.DaoReflectionHelpers;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
|
||||
public abstract class ClientToServerMessage<TYPE extends RemoteModel> {
|
||||
|
||||
protected final Class<TYPE> modelClass;
|
||||
protected final String table;
|
||||
protected final long id;
|
||||
protected final String uuid;
|
||||
protected final long pushedAt;
|
||||
protected final boolean foundEntity;
|
||||
|
||||
public static final String TYPE_KEY = "type";
|
||||
public static final String TABLE_KEY = "table";
|
||||
public static final String UUID_KEY = "uuid";
|
||||
public static final String PUSHED_AT_KEY = "pushed_at";
|
||||
|
||||
public ClientToServerMessage(Class<TYPE> modelClass, String uuid, long pushedAt) {
|
||||
this.modelClass = modelClass;
|
||||
Table tableClass = DaoReflectionHelpers.getStaticFieldByReflection(modelClass, Table.class, "TABLE");
|
||||
this.table = NameMaps.getServerNameForTable(tableClass);
|
||||
this.uuid = uuid;
|
||||
this.pushedAt = pushedAt;
|
||||
this.foundEntity = true;
|
||||
this.id = AbstractModel.NO_ID;
|
||||
}
|
||||
|
||||
public ClientToServerMessage(long id, Class<TYPE> modelClass, RemoteModelDao<TYPE> modelDao) {
|
||||
this.id = id;
|
||||
this.modelClass = modelClass;
|
||||
Table tableClass = DaoReflectionHelpers.getStaticFieldByReflection(modelClass, Table.class, "TABLE");
|
||||
this.table = NameMaps.getServerNameForTable(tableClass);
|
||||
|
||||
TYPE entity = getEntity(id, modelDao);
|
||||
this.foundEntity = entity != null;
|
||||
if (entity == null) {
|
||||
this.uuid = RemoteModel.NO_UUID;
|
||||
this.pushedAt = 0;
|
||||
} else {
|
||||
this.uuid = entity.getValue(RemoteModel.UUID_PROPERTY);
|
||||
this.pushedAt = entity.getValue(RemoteModel.PUSHED_AT_PROPERTY);
|
||||
}
|
||||
}
|
||||
|
||||
private TYPE getEntity(long localId, RemoteModelDao<TYPE> modelDao) {
|
||||
return modelDao.fetch(localId, RemoteModel.UUID_PROPERTY, RemoteModel.PUSHED_AT_PROPERTY);
|
||||
}
|
||||
|
||||
public final String getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public final long getPushedAt() {
|
||||
return pushedAt;
|
||||
}
|
||||
|
||||
public final JSONObject serializeToJSON(MultipartEntity entity) {
|
||||
JSONObject json = new JSONObject();
|
||||
try {
|
||||
json.put(TYPE_KEY, getTypeString());
|
||||
json.put(TABLE_KEY, table);
|
||||
json.put(UUID_KEY, uuid);
|
||||
String dateValue = DateUtilities.timeToIso8601(pushedAt, true);
|
||||
json.put(PUSHED_AT_KEY, dateValue != null ? dateValue : 0);
|
||||
if (serializeExtrasToJSON(json, entity)) {
|
||||
return json;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((table == null) ? 0 : table.hashCode());
|
||||
result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ClientToServerMessage<?> other = (ClientToServerMessage<?>) obj;
|
||||
if (table == null) {
|
||||
if (other.table != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!table.equals(other.table)) {
|
||||
return false;
|
||||
}
|
||||
if (uuid == null) {
|
||||
if (other.uuid != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!uuid.equals(other.uuid)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract boolean serializeExtrasToJSON(JSONObject serializeTo, MultipartEntity entity) throws JSONException;
|
||||
protected abstract String getTypeString();
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.Property.LongProperty;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.sql.Criterion;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.astrid.dao.OutstandingEntryDao;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.OutstandingEntry;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
|
||||
public class ConstructOutstandingTableFromMasterTable<TYPE extends RemoteModel, OE extends OutstandingEntry<TYPE>> {
|
||||
|
||||
protected final String table;
|
||||
protected final RemoteModelDao<TYPE> dao;
|
||||
protected final OutstandingEntryDao<OE> outstandingDao;
|
||||
protected final LongProperty createdAtProperty;
|
||||
|
||||
public ConstructOutstandingTableFromMasterTable(String table, RemoteModelDao<TYPE> dao,
|
||||
OutstandingEntryDao<OE> outstandingDao, LongProperty createdAtProperty) {
|
||||
this.table = table;
|
||||
this.dao = dao;
|
||||
this.outstandingDao = outstandingDao;
|
||||
this.createdAtProperty = createdAtProperty;
|
||||
}
|
||||
|
||||
protected void extras(long itemId, long createdAt) {
|
||||
// Subclasses can override
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
execute(Criterion.all);
|
||||
}
|
||||
|
||||
public void execute(Criterion criterion) {
|
||||
Property<?>[] syncableProperties = NameMaps.syncableProperties(table);
|
||||
TodorooCursor<TYPE> items = dao.query(Query.select(AndroidUtilities.addToArray(Property.class, syncableProperties, AbstractModel.ID_PROPERTY, RemoteModel.UUID_PROPERTY)).where(criterion));
|
||||
try {
|
||||
OE oe = outstandingDao.getModelClass().newInstance();
|
||||
for (items.moveToFirst(); !items.isAfterLast(); items.moveToNext()) {
|
||||
long createdAt;
|
||||
if (createdAtProperty != null) {
|
||||
createdAt = items.get(createdAtProperty);
|
||||
} else {
|
||||
createdAt = DateUtilities.now();
|
||||
}
|
||||
long itemId = items.get(AbstractModel.ID_PROPERTY);
|
||||
for (Property<?> p : syncableProperties) {
|
||||
oe.clear();
|
||||
oe.setValue(OutstandingEntry.ENTITY_ID_PROPERTY, itemId);
|
||||
oe.setValue(OutstandingEntry.COLUMN_STRING_PROPERTY, p.name);
|
||||
Object value = items.get(p);
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
oe.setValue(OutstandingEntry.VALUE_STRING_PROPERTY, value.toString());
|
||||
oe.setValue(OutstandingEntry.CREATED_AT_PROPERTY, createdAt);
|
||||
outstandingDao.createNew(oe);
|
||||
}
|
||||
extras(itemId, createdAt);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e("ConstructOutstanding", "Error instantiating outstanding model class", e);
|
||||
} catch (InstantiationException e2) {
|
||||
Log.e("ConstructOutstanding", "Error instantiating outstanding model class", e2);
|
||||
} finally {
|
||||
items.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import com.todoroo.andlib.data.Property.LongProperty;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.sql.Criterion;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.astrid.dao.MetadataDao;
|
||||
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
||||
import com.todoroo.astrid.dao.OutstandingEntryDao;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.TaskOutstanding;
|
||||
import com.todoroo.astrid.tags.TaskToTagMetadata;
|
||||
|
||||
public class ConstructTaskOutstandingTableFromMasterTable extends ConstructOutstandingTableFromMasterTable<Task, TaskOutstanding> {
|
||||
|
||||
private final MetadataDao metadataDao;
|
||||
|
||||
public ConstructTaskOutstandingTableFromMasterTable(String table, RemoteModelDao<Task> dao, OutstandingEntryDao<TaskOutstanding> outstandingDao, MetadataDao metadataDao, LongProperty createdAtProperty) {
|
||||
super(table, dao, outstandingDao, createdAtProperty);
|
||||
this.metadataDao = metadataDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void extras(long itemId, long createdAt) {
|
||||
super.extras(itemId, createdAt);
|
||||
TodorooCursor<Metadata> tagMetadata = metadataDao.query(Query.select(Metadata.PROPERTIES)
|
||||
.where(Criterion.and(MetadataCriteria.byTaskAndwithKey(itemId, TaskToTagMetadata.KEY), Metadata.DELETION_DATE.eq(0))));
|
||||
Metadata m = new Metadata();
|
||||
try {
|
||||
for (tagMetadata.moveToFirst(); !tagMetadata.isAfterLast(); tagMetadata.moveToNext()) {
|
||||
m.clear();
|
||||
m.readFromCursor(tagMetadata);
|
||||
|
||||
if (m.containsNonNullValue(TaskToTagMetadata.TAG_UUID)) {
|
||||
TaskOutstanding oe = new TaskOutstanding();
|
||||
oe.setValue(TaskOutstanding.ENTITY_ID_PROPERTY, itemId);
|
||||
oe.setValue(TaskOutstanding.COLUMN_STRING, NameMaps.TAG_ADDED_COLUMN);
|
||||
oe.setValue(TaskOutstanding.VALUE_STRING, m.getValue(TaskToTagMetadata.TAG_UUID));
|
||||
oe.setValue(TaskOutstanding.CREATED_AT, createdAt);
|
||||
outstandingDao.createNew(oe);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tagMetadata.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.DatabaseDao;
|
||||
import com.todoroo.andlib.data.Property.StringProperty;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
|
||||
import com.todoroo.astrid.dao.HistoryDao;
|
||||
import com.todoroo.astrid.dao.TagDataDao;
|
||||
import com.todoroo.astrid.dao.TaskDao;
|
||||
import com.todoroo.astrid.dao.UserActivityDao;
|
||||
import com.todoroo.astrid.data.History;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.SyncFlags;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.UserActivity;
|
||||
|
||||
public class ConvertSelfUserIdsToZero {
|
||||
|
||||
@Autowired
|
||||
private TaskDao taskDao;
|
||||
|
||||
@Autowired
|
||||
private TagDataDao tagDataDao;
|
||||
|
||||
@Autowired
|
||||
private HistoryDao historyDao;
|
||||
|
||||
@Autowired
|
||||
private UserActivityDao userActivityDao;
|
||||
|
||||
public ConvertSelfUserIdsToZero() {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
}
|
||||
|
||||
public synchronized void execute(String selfId) {
|
||||
if (RemoteModel.isValidUuid(selfId)) {
|
||||
updateDatabase(taskDao, new Task(), Task.CREATOR_ID, selfId);
|
||||
updateDatabase(taskDao, new Task(), Task.USER_ID, selfId);
|
||||
updateDatabase(tagDataDao, new TagData(), TagData.USER_ID, selfId);
|
||||
updateDatabase(historyDao, new History(), History.USER_UUID, selfId);
|
||||
updateDatabase(userActivityDao, new UserActivity(), UserActivity.USER_UUID, selfId);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void execute() {
|
||||
String selfId = ActFmPreferenceService.userId();
|
||||
execute(selfId);
|
||||
}
|
||||
|
||||
private <T extends AbstractModel> void updateDatabase(DatabaseDao<T> dao, T instance, StringProperty property, String selfId) {
|
||||
instance.setValue(property, Task.USER_ID_SELF);
|
||||
instance.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
dao.update(property.eq(selfId), instance);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
public class Debug extends ServerToClientMessage {
|
||||
|
||||
public Debug(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(String serverTime) {
|
||||
String message = json.optString("message");
|
||||
if (!TextUtils.isEmpty(message)) {
|
||||
Log.w("actfm-debug-message", message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class DoubleCheck extends ServerToClientMessage {
|
||||
|
||||
public DoubleCheck(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(String serverTime) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,199 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.data.Property.IntegerProperty;
|
||||
import com.todoroo.andlib.data.Property.LongProperty;
|
||||
import com.todoroo.andlib.service.Autowired;
|
||||
import com.todoroo.andlib.service.DependencyInjectionService;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmInvoker;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmSyncThread.SyncMessageCallback;
|
||||
import com.todoroo.astrid.dao.HistoryDao;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.dao.UserDao;
|
||||
import com.todoroo.astrid.data.History;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.User;
|
||||
|
||||
public class FetchHistory<TYPE extends RemoteModel> {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-fetch-history"; //$NON-NLS-1$
|
||||
|
||||
private final RemoteModelDao<TYPE> dao;
|
||||
private final LongProperty historyTimeProperty;
|
||||
private final IntegerProperty historyHasMoreProperty;
|
||||
private final String table;
|
||||
private final String uuid;
|
||||
private final String taskTitle;
|
||||
private final long modifiedAfter;
|
||||
private final int offset;
|
||||
private final SyncMessageCallback done;
|
||||
|
||||
@Autowired
|
||||
private ActFmInvoker actFmInvoker;
|
||||
|
||||
@Autowired
|
||||
private HistoryDao historyDao;
|
||||
|
||||
@Autowired
|
||||
private UserDao userDao;
|
||||
|
||||
@Autowired
|
||||
private ActFmPreferenceService actFmPreferenceService;
|
||||
|
||||
public FetchHistory(RemoteModelDao<TYPE> dao, LongProperty historyTimeProperty, IntegerProperty historyHasMoreProperty,
|
||||
String table, String uuid, String taskTitle, long modifiedAfter, int offset, SyncMessageCallback done) {
|
||||
DependencyInjectionService.getInstance().inject(this);
|
||||
this.dao = dao;
|
||||
this.historyTimeProperty = historyTimeProperty;
|
||||
this.historyHasMoreProperty = historyHasMoreProperty;
|
||||
this.table = table;
|
||||
this.uuid = uuid;
|
||||
this.taskTitle = taskTitle;
|
||||
this.modifiedAfter = modifiedAfter;
|
||||
this.offset = offset;
|
||||
this.done = done;
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String token = actFmPreferenceService.getToken();
|
||||
if (TextUtils.isEmpty(token) || TextUtils.isEmpty(uuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<Object> params = new ArrayList<Object>();
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
params.add("task_id");
|
||||
} else if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
params.add("tag_id");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
params.add(uuid);
|
||||
|
||||
if (modifiedAfter > 0) {
|
||||
params.add("modified_after"); params.add(modifiedAfter / 1000L);
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
params.add("offset"); params.add(offset);
|
||||
}
|
||||
|
||||
params.add("token"); params.add(token);
|
||||
try {
|
||||
JSONObject result = actFmInvoker.invoke("model_history_list", params.toArray(new Object[params.size()]));
|
||||
JSONArray list = result.optJSONArray("list");
|
||||
boolean hasMore = result.optInt("has_more") > 0;
|
||||
long time = result.optLong("time") * 1000;
|
||||
if (hasMore && offset == 0) {
|
||||
historyDao.deleteWhere(History.TARGET_ID.eq(uuid));
|
||||
}
|
||||
if (list != null) {
|
||||
for (int i = 0; i < list.length(); i++) {
|
||||
JSONObject historyJson = list.optJSONObject(i);
|
||||
if (historyJson != null) {
|
||||
History history = new History();
|
||||
history.setValue(History.TABLE_ID, table);
|
||||
history.setValue(History.TARGET_ID, uuid);
|
||||
history.setValue(History.UUID, historyJson.optString("id") + ":" + uuid);
|
||||
|
||||
String userId = historyJson.optString("user_id");
|
||||
if (userId.equals(ActFmPreferenceService.userId())) {
|
||||
userId = Task.USER_ID_SELF;
|
||||
}
|
||||
history.setValue(History.USER_UUID, historyJson.optString("user_id"));
|
||||
history.setValue(History.COLUMN, historyJson.optString("column"));
|
||||
history.setValue(History.OLD_VALUE, historyJson.optString("prev"));
|
||||
history.setValue(History.NEW_VALUE, historyJson.optString("value"));
|
||||
history.setValue(History.CREATED_AT, historyJson.optLong("created_at") * 1000);
|
||||
|
||||
JSONArray taskObj = historyJson.optJSONArray("task");
|
||||
if (taskObj != null) {
|
||||
history.setValue(History.TABLE_ID, NameMaps.TABLE_ID_TASKS);
|
||||
history.setValue(History.TARGET_ID, taskObj.optString(0));
|
||||
history.setValue(History.TASK, taskObj.toString());
|
||||
} else if (NameMaps.TABLE_ID_TASKS.equals(table) && !TextUtils.isEmpty(taskTitle)) {
|
||||
taskObj = new JSONArray();
|
||||
taskObj.put(uuid);
|
||||
taskObj.put(taskTitle);
|
||||
history.setValue(History.TASK, taskObj.toString());
|
||||
}
|
||||
|
||||
if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
history.setValue(History.TAG_ID, uuid);
|
||||
}
|
||||
|
||||
if (historyDao.update(History.UUID.eq(history.getValue(History.UUID)), history) <= 0) {
|
||||
historyDao.createNew(history);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (time > 0) {
|
||||
TYPE template;
|
||||
try {
|
||||
template = dao.getModelClass().newInstance();
|
||||
template.setValue(historyTimeProperty, time);
|
||||
if (modifiedAfter == 0 || hasMore) {
|
||||
template.setValue(historyHasMoreProperty, hasMore ? 1 : 0);
|
||||
}
|
||||
dao.update(RemoteModel.UUID_PROPERTY.eq(uuid), template);
|
||||
} catch (InstantiationException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model for recording time", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model for recording time", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject users = result.optJSONObject("users");
|
||||
if (users != null) {
|
||||
Iterator<String> keys = users.keys();
|
||||
while (keys.hasNext()) {
|
||||
String key = keys.next();
|
||||
JSONObject userObj = users.optJSONObject(key);
|
||||
if (userObj != null) {
|
||||
String userUuid = userObj.optString("id");
|
||||
if (RemoteModel.isUuidEmpty(uuid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
User user = new User();
|
||||
user.setValue(User.FIRST_NAME, userObj.optString("first_name"));
|
||||
user.setValue(User.LAST_NAME, userObj.optString("last_name"));
|
||||
user.setValue(User.NAME, userObj.optString("name"));
|
||||
user.setValue(User.PICTURE, userObj.optString("picture"));
|
||||
user.setValue(User.UUID, userUuid);
|
||||
|
||||
if (userDao.update(User.UUID.eq(userUuid), user) <= 0) {
|
||||
userDao.createNew(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(ERROR_TAG, "Error getting model history", e);
|
||||
}
|
||||
|
||||
if (done != null) {
|
||||
done.runOnSuccess();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,112 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.Property.DoubleProperty;
|
||||
import com.todoroo.andlib.data.Property.IntegerProperty;
|
||||
import com.todoroo.andlib.data.Property.LongProperty;
|
||||
import com.todoroo.andlib.data.Property.PropertyVisitor;
|
||||
import com.todoroo.andlib.data.Property.StringProperty;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
|
||||
|
||||
public class JSONChangeToPropertyVisitor implements PropertyVisitor<Void, String> {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-make-changes";
|
||||
|
||||
private final AbstractModel model;
|
||||
private final JSONObject data;
|
||||
|
||||
public JSONChangeToPropertyVisitor(AbstractModel model, JSONObject data) {
|
||||
this.model = model;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitInteger(Property<Integer> property, String key) {
|
||||
try {
|
||||
int value;
|
||||
if (property.checkFlag(Property.PROP_FLAG_BOOLEAN)) {
|
||||
try {
|
||||
value = data.getBoolean(key) ? 1 : 0;
|
||||
} catch (JSONException e) {
|
||||
value = data.getInt(key);
|
||||
}
|
||||
} else {
|
||||
value = data.getInt(key);
|
||||
}
|
||||
model.setValue((IntegerProperty) property, value);
|
||||
} catch (JSONException e) {
|
||||
Log.e(ERROR_TAG, "Error reading int value with key " + key + " from JSON " + data, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLong(Property<Long> property, String key) {
|
||||
try {
|
||||
long value = data.optLong(key, 0);
|
||||
if (property.checkFlag(Property.PROP_FLAG_DATE)) {
|
||||
String valueString = data.getString(key);
|
||||
try {
|
||||
value = DateUtilities.parseIso8601(valueString);
|
||||
if (Task.DUE_DATE.equals(property)) {
|
||||
value = Task.createDueDate(DateUtilities.isoStringHasTime(valueString) ? Task.URGENCY_SPECIFIC_DAY_TIME : Task.URGENCY_SPECIFIC_DAY, value);
|
||||
}
|
||||
} catch (Exception e){
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
model.setValue((LongProperty) property, value);
|
||||
} catch (JSONException e) {
|
||||
Log.e(ERROR_TAG, "Error reading long value with key " + key + " from JSON " + data, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitDouble(Property<Double> property, String key) {
|
||||
try {
|
||||
double value = data.getDouble(key);
|
||||
model.setValue((DoubleProperty) property, value);
|
||||
} catch (JSONException e) {
|
||||
Log.e(ERROR_TAG, "Error reading double value with key " + key + " from JSON " + data, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitString(Property<String> property, String key) {
|
||||
try {
|
||||
String value = data.getString(key);
|
||||
if ("null".equals(value)) {
|
||||
value = "";
|
||||
} else if (property.checkFlag(Property.PROP_FLAG_USER_ID) && ActFmPreferenceService.userId().equals(value)) {
|
||||
value = Task.USER_ID_SELF;
|
||||
}
|
||||
if (property.equals(Task.USER_ID)) {
|
||||
model.setValue(Task.USER, ""); // Clear this value for migration purposes
|
||||
}
|
||||
|
||||
model.setValue((StringProperty) property, value);
|
||||
} catch (JSONException e) {
|
||||
try {
|
||||
JSONObject object = data.getJSONObject(key);
|
||||
if (object != null) {
|
||||
model.setValue((StringProperty) property, object.toString());
|
||||
}
|
||||
} catch (JSONException e2) {
|
||||
Log.e(ERROR_TAG, "Error reading JSON value with key " + key + " from JSON " + data, e);
|
||||
}
|
||||
Log.e(ERROR_TAG, "Error reading string value with key " + key + " from JSON " + data, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.apache.http.entity.mime.MultipartEntity;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.todoroo.astrid.actfm.sync.ActFmSyncThread;
|
||||
|
||||
public class JSONPayloadBuilder {
|
||||
|
||||
private final StringBuilder sb = new StringBuilder("["); //$NON-NLS-1$
|
||||
private final StringBuilder temp = new StringBuilder();
|
||||
|
||||
private int messageCount = 0;
|
||||
|
||||
public boolean addMessage(ClientToServerMessage<?> message, MultipartEntity entity) {
|
||||
try {
|
||||
JSONObject serialized = message.serializeToJSON(entity);
|
||||
return addJSONObject(serialized);
|
||||
} catch (OutOfMemoryError e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addJSONObject(JSONObject obj) {
|
||||
try {
|
||||
temp.delete(0, temp.length());
|
||||
if (obj != null) {
|
||||
temp.append(obj)
|
||||
.append(","); //$NON-NLS-1$
|
||||
ActFmSyncThread.syncLog(temp.toString());
|
||||
|
||||
sb.append(temp);
|
||||
|
||||
messageCount++;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMessageCount() {
|
||||
return messageCount;
|
||||
}
|
||||
|
||||
public String closeAndReturnString() {
|
||||
if (messageCount > 0) {
|
||||
sb.deleteCharAt(sb.length() - 1); // Remove final comma
|
||||
}
|
||||
sb.append("]"); //$NON-NLS-1$
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,452 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import org.tasks.R;
|
||||
import com.todoroo.andlib.data.AbstractModel;
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.Property.StringProperty;
|
||||
import com.todoroo.andlib.sql.Criterion;
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmInvoker;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.dao.HistoryDao;
|
||||
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.dao.TagMetadataDao;
|
||||
import com.todoroo.astrid.dao.TaskListMetadataDao;
|
||||
import com.todoroo.astrid.dao.UserActivityDao;
|
||||
import com.todoroo.astrid.data.History;
|
||||
import com.todoroo.astrid.data.Metadata;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.SyncFlags;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.TagMetadata;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.TaskListMetadata;
|
||||
import com.todoroo.astrid.data.UserActivity;
|
||||
import com.todoroo.astrid.reminders.Notifications;
|
||||
import com.todoroo.astrid.reminders.ReminderService;
|
||||
import com.todoroo.astrid.service.MetadataService;
|
||||
import com.todoroo.astrid.tags.TagService;
|
||||
import com.todoroo.astrid.tags.TaskToTagMetadata;
|
||||
|
||||
public class MakeChanges<TYPE extends RemoteModel> extends ServerToClientMessage {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-make-changes";
|
||||
|
||||
private final RemoteModelDao<TYPE> dao;
|
||||
private final String table;
|
||||
|
||||
public MakeChanges(JSONObject json, RemoteModelDao<TYPE> dao) {
|
||||
super(json);
|
||||
this.table = json.optString("table");
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
public static <T extends RemoteModel> T changesToModel(RemoteModelDao<T> dao, JSONObject changes, String table) throws IllegalAccessException, InstantiationException {
|
||||
T model = dao.getModelClass().newInstance();
|
||||
JSONChangeToPropertyVisitor visitor = new JSONChangeToPropertyVisitor(model, changes);
|
||||
Iterator<String> keys = changes.keys();
|
||||
while (keys.hasNext()) {
|
||||
String column = keys.next();
|
||||
Property<?> property = NameMaps.serverColumnNameToLocalProperty(table, column);
|
||||
if (property != null) { // Unsupported property
|
||||
property.accept(visitor, column);
|
||||
}
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private static <T extends RemoteModel> void saveOrUpdateModelAfterChanges(RemoteModelDao<T> dao, T model, String oldUuid, String uuid, String serverTime, Criterion orCriterion) {
|
||||
Criterion uuidCriterion;
|
||||
if (oldUuid == null) {
|
||||
uuidCriterion = RemoteModel.UUID_PROPERTY.eq(uuid);
|
||||
} else {
|
||||
uuidCriterion = RemoteModel.UUID_PROPERTY.eq(oldUuid);
|
||||
}
|
||||
|
||||
if (orCriterion != null) {
|
||||
uuidCriterion = Criterion.or(uuidCriterion, orCriterion);
|
||||
}
|
||||
|
||||
if (model.getSetValues() != null && model.getSetValues().size() > 0) {
|
||||
long pushedAt;
|
||||
try {
|
||||
pushedAt = DateUtilities.parseIso8601(serverTime);
|
||||
} catch (ParseException e) {
|
||||
pushedAt = 0;
|
||||
}
|
||||
|
||||
if (pushedAt > 0) {
|
||||
model.setValue(RemoteModel.PUSHED_AT_PROPERTY, pushedAt);
|
||||
}
|
||||
|
||||
model.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
if (dao.update(uuidCriterion, model) <= 0) { // If update doesn't update rows. create a new model
|
||||
model.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
dao.createNew(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(String serverTime) {
|
||||
JSONObject changes = json.optJSONObject("changes");
|
||||
String uuid = json.optString("uuid");
|
||||
if (changes != null && !TextUtils.isEmpty(uuid)) {
|
||||
if (dao != null) {
|
||||
try {
|
||||
TYPE model = changesToModel(dao, changes, table);
|
||||
|
||||
StringProperty uuidProperty = (StringProperty) NameMaps.serverColumnNameToLocalProperty(table, "uuid");
|
||||
String oldUuid = null; // For indicating that a uuid collision has occurred
|
||||
if (model.getSetValues() != null && model.getSetValues().containsKey(uuidProperty.name)) {
|
||||
oldUuid = uuid;
|
||||
uuid = model.getValue(uuidProperty);
|
||||
}
|
||||
|
||||
beforeSaveChanges(changes, model, uuid);
|
||||
|
||||
if (model.getSetValues() != null && !model.getSetValues().containsKey(uuidProperty.name)) {
|
||||
model.setValue(uuidProperty, uuid);
|
||||
}
|
||||
|
||||
saveOrUpdateModelAfterChanges(dao, model, oldUuid, uuid, serverTime, getMatchCriterion(model));
|
||||
afterSaveChanges(changes, model, uuid, oldUuid);
|
||||
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model for MakeChanges", e);
|
||||
} catch (InstantiationException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model for MakeChanges", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Criterion getMatchCriterion(TYPE model) {
|
||||
if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table)) {
|
||||
if (model.getSetValues().containsKey(TaskListMetadata.FILTER.name)) {
|
||||
return TaskListMetadata.FILTER.eq(model.getSetValues().getAsString(TaskListMetadata.FILTER.name));
|
||||
} else if (model.getSetValues().containsKey(TaskListMetadata.TAG_UUID.name)) {
|
||||
return TaskListMetadata.TAG_UUID.eq(model.getSetValues().getAsString(TaskListMetadata.TAG_UUID.name));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void beforeSaveChanges(JSONObject changes, TYPE model, String uuid) {
|
||||
ChangeHooks beforeSaveChanges = null;
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
beforeSaveChanges = new BeforeSaveTaskChanges(model, changes, uuid);
|
||||
} else if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
beforeSaveChanges = new BeforeSaveTagChanges(model, changes, uuid);
|
||||
}
|
||||
|
||||
if (beforeSaveChanges != null) {
|
||||
beforeSaveChanges.performChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private void afterSaveChanges(JSONObject changes, TYPE model, String uuid, String oldUuid) {
|
||||
ChangeHooks afterSaveChanges = null;
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
afterSaveChanges = new AfterSaveTaskChanges(model, changes, uuid, oldUuid);
|
||||
} else if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
afterSaveChanges = new AfterSaveTagChanges(model, changes, uuid, oldUuid);
|
||||
} else if (NameMaps.TABLE_ID_USERS.equals(table)) {
|
||||
afterSaveChanges = new AfterSaveUserChanges(model, changes, uuid);
|
||||
}
|
||||
|
||||
if (afterSaveChanges != null) {
|
||||
afterSaveChanges.performChanges();
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class ChangeHooks {
|
||||
protected final TYPE model;
|
||||
protected final JSONObject changes;
|
||||
protected final String uuid;
|
||||
|
||||
public ChangeHooks(TYPE model, JSONObject changes, String uuid) {
|
||||
this.model = model;
|
||||
this.changes = changes;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public abstract void performChanges();
|
||||
|
||||
protected long getLocalId() {
|
||||
long localId;
|
||||
if (!model.isSaved()) { // We don't have the local task id
|
||||
localId = dao.localIdFromUuid(uuid);
|
||||
model.setId(localId);
|
||||
} else {
|
||||
localId = model.getId();
|
||||
}
|
||||
return localId;
|
||||
}
|
||||
}
|
||||
|
||||
private class BeforeSaveTaskChanges extends ChangeHooks {
|
||||
|
||||
public BeforeSaveTaskChanges(TYPE model, JSONObject changes, String uuid) {
|
||||
super(model, changes, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChanges() {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
private class BeforeSaveTagChanges extends ChangeHooks {
|
||||
|
||||
public BeforeSaveTagChanges(TYPE model, JSONObject changes, String uuid) {
|
||||
super(model, changes, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChanges() {
|
||||
JSONArray addMembers = changes.optJSONArray("member_added");
|
||||
boolean membersAdded = (addMembers != null && addMembers.length() > 0);
|
||||
if (membersAdded) {
|
||||
model.setValue(TagData.MEMBERS, ""); // Clear this value for migration purposes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AfterSaveTaskChanges extends ChangeHooks {
|
||||
|
||||
private final String oldUuid;
|
||||
|
||||
public AfterSaveTaskChanges(TYPE model, JSONObject changes, String uuid, String oldUuid) {
|
||||
super(model, changes, uuid);
|
||||
this.oldUuid = oldUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChanges() {
|
||||
if (!TextUtils.isEmpty(oldUuid) && !oldUuid.equals(uuid)) {
|
||||
uuidChanged(oldUuid, uuid);
|
||||
}
|
||||
|
||||
if (changes.has(NameMaps.localPropertyToServerColumnName(NameMaps.TABLE_ID_TASKS, Task.DUE_DATE)) ||
|
||||
changes.has(NameMaps.localPropertyToServerColumnName(NameMaps.TABLE_ID_TASKS, Task.COMPLETION_DATE))) {
|
||||
Task t = PluginServices.getTaskDao().fetch(uuid, ReminderService.NOTIFICATION_PROPERTIES);
|
||||
if (t != null) {
|
||||
if ((changes.has("task_repeated") && t.getValue(Task.DUE_DATE) > DateUtilities.now()) || t.getValue(Task.COMPLETION_DATE) > 0) {
|
||||
Notifications.cancelNotifications(t.getId());
|
||||
}
|
||||
ReminderService.getInstance().scheduleAlarm(t);
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray addTags = changes.optJSONArray("tag_added");
|
||||
JSONArray removeTags = changes.optJSONArray("tag_removed");
|
||||
boolean tagsAdded = (addTags != null && addTags.length() > 0);
|
||||
boolean tagsRemoved = (removeTags != null && removeTags.length() > 0);
|
||||
if (!tagsAdded && !tagsRemoved) {
|
||||
return;
|
||||
}
|
||||
|
||||
long localId = AbstractModel.NO_ID;
|
||||
if (tagsAdded || tagsRemoved) {
|
||||
localId = getLocalId();
|
||||
}
|
||||
|
||||
if (tagsAdded) {
|
||||
if (model.isSaved()) {
|
||||
TagService tagService = TagService.getInstance();
|
||||
for (int i = 0; i < addTags.length(); i++) {
|
||||
try {
|
||||
String tagUuid = addTags.getString(i);
|
||||
tagService.createLink(model.getId(), uuid, tagUuid, true);
|
||||
} catch (JSONException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tagsRemoved) {
|
||||
ArrayList<String> toRemove = new ArrayList<String>(removeTags.length());
|
||||
for (int i = 0; i < removeTags.length(); i++) {
|
||||
try {
|
||||
String tagUuid = removeTags.getString(i);
|
||||
toRemove.add(tagUuid);
|
||||
} catch (JSONException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
TagService.getInstance().deleteLinks(localId, uuid, toRemove.toArray(new String[toRemove.size()]), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void uuidChanged(String fromUuid, String toUuid) {
|
||||
if (ActFmInvoker.SYNC_DEBUG) {
|
||||
Log.e(ERROR_TAG, "Task UUID collision -- old uuid: " + fromUuid + ", new uuid: " + toUuid);
|
||||
}
|
||||
|
||||
// Update reference from UserActivity to task uuid
|
||||
UserActivityDao activityDao = PluginServices.getUserActivityDao();
|
||||
UserActivity activityTemplate = new UserActivity();
|
||||
activityTemplate.setValue(UserActivity.TARGET_ID, toUuid);
|
||||
activityTemplate.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
activityDao.update(Criterion.and(UserActivity.ACTION.eq(UserActivity.ACTION_TASK_COMMENT), UserActivity.TARGET_ID.eq(fromUuid)), activityTemplate);
|
||||
|
||||
// Update reference from task to tag metadata to task uuid
|
||||
MetadataService metadataService = PluginServices.getMetadataService();
|
||||
Metadata taskToTagTemplate = new Metadata();
|
||||
taskToTagTemplate.setValue(TaskToTagMetadata.TASK_UUID, toUuid);
|
||||
taskToTagTemplate.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
metadataService.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TASK_UUID.eq(fromUuid)), taskToTagTemplate);
|
||||
|
||||
HistoryDao historyDao = PluginServices.getHistoryDao();
|
||||
History histTemplate = new History();
|
||||
histTemplate.setValue(History.TARGET_ID, toUuid);
|
||||
historyDao.update(Criterion.and(History.TABLE_ID.eq(NameMaps.TABLE_ID_TAGS), History.TARGET_ID.eq(oldUuid)), histTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class AfterSaveTagChanges extends ChangeHooks {
|
||||
|
||||
private final String oldUuid;
|
||||
|
||||
public AfterSaveTagChanges(TYPE model, JSONObject changes, String uuid, String oldUuid) {
|
||||
super(model, changes, uuid);
|
||||
this.oldUuid = oldUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChanges() {
|
||||
if (!TextUtils.isEmpty(oldUuid) && !oldUuid.equals(uuid)) {
|
||||
uuidChanged(oldUuid, uuid);
|
||||
}
|
||||
|
||||
String nameCol = NameMaps.localPropertyToServerColumnName(NameMaps.TABLE_ID_TAGS, TagData.NAME);
|
||||
String deletedCol = NameMaps.localPropertyToServerColumnName(NameMaps.TABLE_ID_TAGS, TagData.DELETION_DATE);
|
||||
if (changes.has(nameCol)) {
|
||||
Metadata template = new Metadata();
|
||||
template.setValue(TaskToTagMetadata.TAG_NAME, changes.optString(nameCol));
|
||||
PluginServices.getMetadataService().update(
|
||||
Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY),
|
||||
TaskToTagMetadata.TAG_UUID.eq(uuid)), template);
|
||||
} else if (changes.has(deletedCol)) {
|
||||
Metadata template = new Metadata();
|
||||
String valueString = changes.optString(deletedCol);
|
||||
long deletedValue = 0;
|
||||
if (!TextUtils.isEmpty(valueString)) {
|
||||
try {
|
||||
deletedValue = DateUtilities.parseIso8601(valueString);
|
||||
} catch (Exception e){
|
||||
//
|
||||
}
|
||||
}
|
||||
template.setValue(Metadata.DELETION_DATE, deletedValue);
|
||||
template.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
PluginServices.getMetadataService().update(
|
||||
Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY),
|
||||
TaskToTagMetadata.TAG_UUID.eq(uuid)), template);
|
||||
}
|
||||
|
||||
TagMetadataDao tagMetadataDao = PluginServices.getTagMetadataDao();
|
||||
|
||||
JSONArray addMembers = changes.optJSONArray("member_added");
|
||||
JSONArray removeMembers = changes.optJSONArray("member_removed");
|
||||
boolean membersAdded = (addMembers != null && addMembers.length() > 0);
|
||||
boolean membersRemoved = (removeMembers != null && removeMembers.length() > 0);
|
||||
|
||||
long localId = AbstractModel.NO_ID;
|
||||
if (membersAdded || membersRemoved) {
|
||||
localId = getLocalId();
|
||||
}
|
||||
|
||||
if (membersAdded) {
|
||||
for (int i = 0; i < addMembers.length(); i++) {
|
||||
try {
|
||||
String memberId = addMembers.getString(i);
|
||||
tagMetadataDao.createMemberLink(localId, uuid, memberId, true);
|
||||
} catch (JSONException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (membersRemoved) {
|
||||
ArrayList<String> toRemove = new ArrayList<String>(removeMembers.length());
|
||||
for (int i = 0; i < removeMembers.length(); i++) {
|
||||
try {
|
||||
String tagUuid = removeMembers.getString(i);
|
||||
toRemove.add(tagUuid);
|
||||
} catch (JSONException e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
tagMetadataDao.removeMemberLinks(localId, uuid, toRemove.toArray(new String[toRemove.size()]), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void uuidChanged(String fromUuid, String toUuid) {
|
||||
if (ActFmInvoker.SYNC_DEBUG) {
|
||||
Log.e(ERROR_TAG, "Tag UUID collision -- old uuid: " + fromUuid + ", new uuid: " + toUuid);
|
||||
}
|
||||
|
||||
UserActivityDao activityDao = PluginServices.getUserActivityDao();
|
||||
UserActivity activityTemplate = new UserActivity();
|
||||
activityTemplate.setValue(UserActivity.TARGET_ID, toUuid);
|
||||
activityTemplate.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
activityDao.update(Criterion.and(UserActivity.ACTION.eq(UserActivity.ACTION_TAG_COMMENT), UserActivity.TARGET_ID.eq(fromUuid)), activityTemplate);
|
||||
|
||||
// Update reference from task to tag metadata to tag uuid
|
||||
MetadataService metadataService = PluginServices.getMetadataService();
|
||||
Metadata taskToTagTemplate = new Metadata();
|
||||
taskToTagTemplate.setValue(TaskToTagMetadata.TAG_UUID, toUuid);
|
||||
taskToTagTemplate.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
metadataService.update(Criterion.and(MetadataCriteria.withKey(TaskToTagMetadata.KEY), TaskToTagMetadata.TAG_UUID.eq(fromUuid)), taskToTagTemplate);
|
||||
|
||||
// Update reference from tag metadata to tag uuid
|
||||
TagMetadataDao tagMetadataDao = PluginServices.getTagMetadataDao();
|
||||
TagMetadata memberMetadataTemplate = new TagMetadata();
|
||||
memberMetadataTemplate.setValue(TagMetadata.TAG_UUID, toUuid);
|
||||
memberMetadataTemplate.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
tagMetadataDao.update(TagMetadata.TAG_UUID.eq(fromUuid), memberMetadataTemplate);
|
||||
|
||||
HistoryDao historyDao = PluginServices.getHistoryDao();
|
||||
History histTemplate = new History();
|
||||
histTemplate.setValue(History.TARGET_ID, toUuid);
|
||||
historyDao.update(Criterion.and(History.TABLE_ID.eq(NameMaps.TABLE_ID_TAGS), History.TARGET_ID.eq(oldUuid)), histTemplate);
|
||||
|
||||
TaskListMetadataDao taskListMetadataDao = PluginServices.getTaskListMetadataDao();
|
||||
TaskListMetadata tlm = new TaskListMetadata();
|
||||
tlm.setValue(TaskListMetadata.TAG_UUID, toUuid);
|
||||
tlm.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
taskListMetadataDao.update(TaskListMetadata.TAG_UUID.eq(fromUuid), tlm);
|
||||
}
|
||||
}
|
||||
|
||||
private class AfterSaveUserChanges extends ChangeHooks {
|
||||
|
||||
public AfterSaveUserChanges(TYPE model, JSONObject changes, String uuid) {
|
||||
super(model, changes, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performChanges() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,116 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.utility.DateUtilities;
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.User;
|
||||
|
||||
public class NowBriefed<TYPE extends RemoteModel> extends ServerToClientMessage {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-now-briefed";
|
||||
|
||||
private final RemoteModelDao<TYPE> dao;
|
||||
private final String table;
|
||||
private final String uuid;
|
||||
private final String taskId;
|
||||
private final String tagId;
|
||||
private final String userId;
|
||||
private long pushedAt;
|
||||
|
||||
public NowBriefed(JSONObject json, RemoteModelDao<TYPE> dao) {
|
||||
super(json);
|
||||
this.table = json.optString("table");
|
||||
this.uuid = json.optString("uuid");
|
||||
this.taskId = json.optString(BriefMe.TASK_ID_KEY);
|
||||
this.tagId = json.optString(BriefMe.TAG_ID_KEY);
|
||||
this.userId = json.optString(BriefMe.USER_ID_KEY);
|
||||
this.dao = dao;
|
||||
try {
|
||||
this.pushedAt = DateUtilities.parseIso8601(json.optString("pushed_at"));
|
||||
} catch (ParseException e) {
|
||||
this.pushedAt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(String serverTime) {
|
||||
if (pushedAt > 0) {
|
||||
if (TextUtils.isEmpty(uuid)) {
|
||||
if (!TextUtils.isEmpty(taskId)) {
|
||||
Task template = new Task();
|
||||
if (NameMaps.TABLE_ID_ATTACHMENTS.equals(table)) {
|
||||
template.setValue(Task.ATTACHMENTS_PUSHED_AT, pushedAt);
|
||||
} else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) {
|
||||
template.setValue(Task.USER_ACTIVITIES_PUSHED_AT, pushedAt);
|
||||
}
|
||||
|
||||
if (template.getSetValues() != null) {
|
||||
PluginServices.getTaskDao().update(Task.UUID.eq(taskId), template);
|
||||
}
|
||||
|
||||
} else if (!TextUtils.isEmpty(tagId)) {
|
||||
TagData template = new TagData();
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
template.setValue(TagData.TASKS_PUSHED_AT, pushedAt);
|
||||
}
|
||||
if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table)) {
|
||||
template.setValue(TagData.METADATA_PUSHED_AT, pushedAt);
|
||||
} else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) {
|
||||
template.setValue(TagData.USER_ACTIVITIES_PUSHED_AT, pushedAt);
|
||||
}
|
||||
|
||||
if (template.getSetValues() != null) {
|
||||
PluginServices.getTagDataDao().update(TagData.UUID.eq(tagId), template);
|
||||
}
|
||||
|
||||
} else if (!TextUtils.isEmpty(userId)) {
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
User template = new User();
|
||||
template.setValue(User.TASKS_PUSHED_AT, pushedAt);
|
||||
PluginServices.getUserDao().update(User.UUID.eq(userId), template);
|
||||
}
|
||||
} else {
|
||||
String pushedAtKey = null;
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
pushedAtKey = NameMaps.PUSHED_AT_TASKS;
|
||||
} else if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
pushedAtKey = NameMaps.PUSHED_AT_TAGS;
|
||||
} else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) {
|
||||
pushedAtKey = NameMaps.PUSHED_AT_ACTIVITY;
|
||||
} else if (NameMaps.TABLE_ID_USERS.equals(table)) {
|
||||
pushedAtKey = NameMaps.PUSHED_AT_USERS;
|
||||
} else if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table)) {
|
||||
pushedAtKey = NameMaps.PUSHED_AT_TASK_LIST_METADATA;
|
||||
}
|
||||
|
||||
if (pushedAtKey != null) {
|
||||
Preferences.setLong(pushedAtKey, pushedAt);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
TYPE instance = dao.getModelClass().newInstance();
|
||||
instance.setValue(RemoteModel.PUSHED_AT_PROPERTY, pushedAt);
|
||||
dao.update(RemoteModel.UUID_PROPERTY.eq(uuid), instance);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model for NowBriefed", e);
|
||||
} catch (InstantiationException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model for NowBriefed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.todoroo.andlib.data.Property;
|
||||
import com.todoroo.andlib.data.Property.PropertyVisitor;
|
||||
import com.todoroo.andlib.data.TodorooCursor;
|
||||
import com.todoroo.andlib.sql.Order;
|
||||
import com.todoroo.andlib.sql.Query;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmSyncThread;
|
||||
import com.todoroo.astrid.dao.DaoReflectionHelpers;
|
||||
import com.todoroo.astrid.dao.OutstandingEntryDao;
|
||||
import com.todoroo.astrid.dao.RemoteModelDao;
|
||||
import com.todoroo.astrid.data.OutstandingEntry;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.SyncFlags;
|
||||
|
||||
public class ReplayOutstandingEntries<T extends RemoteModel, OE extends OutstandingEntry<T>> {
|
||||
|
||||
private static final String ERROR_TAG = "actfm-replay-outstanding";
|
||||
|
||||
protected final Class<T> modelClass;
|
||||
private final Class<OE> outstandingClass;
|
||||
private final String table;
|
||||
protected final RemoteModelDao<T> dao;
|
||||
protected final OutstandingEntryDao<OE> outstandingDao;
|
||||
private final boolean afterErrors;
|
||||
|
||||
public ReplayOutstandingEntries(Class<T> modelClass, String table, RemoteModelDao<T> dao, OutstandingEntryDao<OE> outstandingDao, boolean afterErrors) {
|
||||
this.modelClass = modelClass;
|
||||
this.outstandingClass = DaoReflectionHelpers.getOutstandingClass(modelClass);
|
||||
this.table = table;
|
||||
this.dao = dao;
|
||||
this.outstandingDao = outstandingDao;
|
||||
this.afterErrors = afterErrors;
|
||||
}
|
||||
|
||||
public void execute() {
|
||||
TodorooCursor<OE> outstanding = outstandingDao.query(Query.select(DaoReflectionHelpers.getModelProperties(outstandingClass))
|
||||
.orderBy(Order.asc(OutstandingEntry.ENTITY_ID_PROPERTY), Order.asc(OutstandingEntry.CREATED_AT_PROPERTY)));
|
||||
try {
|
||||
OE instance = outstandingClass.newInstance();
|
||||
for (outstanding.moveToFirst(); !outstanding.isAfterLast(); outstanding.moveToNext()) {
|
||||
instance.clear();
|
||||
instance.readPropertiesFromCursor(outstanding);
|
||||
processItem(instance.getValue(OutstandingEntry.ENTITY_ID_PROPERTY), instance, outstanding);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating outstanding entry", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating outstanding entry", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(ERROR_TAG, "Unexpected exception in replay outstanding entries", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void enqueueChangesHappenedMessage(long id) {
|
||||
ActFmSyncThread.getInstance().enqueueMessage(new ChangesHappened<T, OE>(id, modelClass, dao, outstandingDao), null);
|
||||
}
|
||||
|
||||
protected boolean shouldSaveModel(T model) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void processItem(long id, OE instance, TodorooCursor<OE> outstanding) {
|
||||
try {
|
||||
T model = modelClass.newInstance();
|
||||
model.setId(id);
|
||||
OutstandingToModelVisitor<T> visitor = new OutstandingToModelVisitor<T>(model);
|
||||
int count = 0;
|
||||
for (; !outstanding.isAfterLast(); outstanding.moveToNext()) {
|
||||
instance.clear();
|
||||
instance.readPropertiesFromCursor(outstanding);
|
||||
if (instance.getValue(OutstandingEntry.ENTITY_ID_PROPERTY) != id) {
|
||||
break;
|
||||
}
|
||||
count ++;
|
||||
String column = instance.getValue(OutstandingEntry.COLUMN_STRING_PROPERTY);
|
||||
Property<?> property = NameMaps.localColumnNameToProperty(table, column);
|
||||
// set values to model
|
||||
if (property != null) {
|
||||
property.accept(visitor, instance);
|
||||
}
|
||||
}
|
||||
|
||||
model.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
if (shouldSaveModel(model)) {
|
||||
dao.saveExisting(model);
|
||||
|
||||
if (count > 0 && !afterErrors) {
|
||||
enqueueChangesHappenedMessage(id);
|
||||
}
|
||||
}
|
||||
|
||||
outstanding.moveToPrevious(); // Move back one to undo the last iteration of the for loop
|
||||
} catch (InstantiationException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(ERROR_TAG, "Error instantiating model", e);
|
||||
}
|
||||
}
|
||||
|
||||
private class OutstandingToModelVisitor<MTYPE extends T> implements PropertyVisitor<Void, OE> {
|
||||
|
||||
private final MTYPE model;
|
||||
|
||||
public OutstandingToModelVisitor(MTYPE model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitInteger(Property<Integer> property, OE data) {
|
||||
Integer i = data.getMergedValues().getAsInteger(OutstandingEntry.VALUE_STRING_PROPERTY.name);
|
||||
if (i != null) {
|
||||
model.setValue(property, i);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLong(Property<Long> property, OE data) {
|
||||
Long l = data.getMergedValues().getAsLong(OutstandingEntry.VALUE_STRING_PROPERTY.name);
|
||||
if (l != null) {
|
||||
model.setValue(property, l);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitDouble(Property<Double> property, OE data) {
|
||||
Double d = data.getMergedValues().getAsDouble(OutstandingEntry.VALUE_STRING_PROPERTY.name);
|
||||
if (d != null) {
|
||||
model.setValue(property, d);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitString(Property<String> property, OE data) {
|
||||
String s = data.getValue(OutstandingEntry.VALUE_STRING_PROPERTY);
|
||||
model.setValue(property, s);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import com.todoroo.astrid.dao.TaskListMetadataDao;
|
||||
import com.todoroo.astrid.dao.TaskListMetadataOutstandingDao;
|
||||
import com.todoroo.astrid.data.TaskListMetadata;
|
||||
import com.todoroo.astrid.data.TaskListMetadataOutstanding;
|
||||
|
||||
public class ReplayTaskListMetadataOutstanding extends ReplayOutstandingEntries<TaskListMetadata, TaskListMetadataOutstanding> {
|
||||
|
||||
public ReplayTaskListMetadataOutstanding(TaskListMetadataDao dao, TaskListMetadataOutstandingDao outstandingDao, boolean afterErrors) {
|
||||
super(TaskListMetadata.class, NameMaps.TABLE_ID_TASK_LIST_METADATA, dao, outstandingDao, afterErrors);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldSaveModel(TaskListMetadata model) {
|
||||
if (model.containsNonNullValue(TaskListMetadata.TASK_IDS) &&
|
||||
TaskListMetadata.taskIdsIsEmpty(model.getValue(TaskListMetadata.TASK_IDS))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enqueueChangesHappenedMessage(long id) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.data.TagData;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.data.TaskAttachment;
|
||||
import com.todoroo.astrid.data.TaskListMetadata;
|
||||
import com.todoroo.astrid.data.User;
|
||||
import com.todoroo.astrid.data.UserActivity;
|
||||
|
||||
public abstract class ServerToClientMessage {
|
||||
|
||||
public abstract void processMessage(String serverTime);
|
||||
|
||||
public static final String TYPE_MAKE_CHANGES = "MakeChanges";
|
||||
public static final String TYPE_NOW_BRIEFED = "NowBriefed";
|
||||
public static final String TYPE_ACKNOWLEDGE_CHANGE = "AcknowledgeChange";
|
||||
public static final String TYPE_USER_DATA = "UserData";
|
||||
public static final String TYPE_DOUBLE_CHECK = "DoubleCheck";
|
||||
public static final String TYPE_USER_MIGRATED = "UserMigrated";
|
||||
public static final String TYPE_DEBUG = "Debug";
|
||||
|
||||
protected final JSONObject json;
|
||||
|
||||
public ServerToClientMessage(JSONObject json) {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public static ServerToClientMessage instantiateMessage(JSONObject json) {
|
||||
String type = json.optString("type");
|
||||
if (TYPE_MAKE_CHANGES.equals(type)) {
|
||||
return instantiateMakeChanges(json);
|
||||
} else if (TYPE_NOW_BRIEFED.equals(type)) {
|
||||
return instantiateNowBriefed(json);
|
||||
} else if (TYPE_ACKNOWLEDGE_CHANGE.equals(type)) {
|
||||
return new AcknowledgeChange(json);
|
||||
} else if (TYPE_USER_DATA.equals(type)) {
|
||||
return new UserData(json);
|
||||
} else if (TYPE_DOUBLE_CHECK.equals(type)) {
|
||||
return new DoubleCheck(json);
|
||||
} else if (TYPE_USER_MIGRATED.equals(type)) {
|
||||
return new UserMigrated(json);
|
||||
} else if (TYPE_DEBUG.equals(type)) {
|
||||
return new Debug(json);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static MakeChanges<?> instantiateMakeChanges(JSONObject json) {
|
||||
String table = json.optString("table");
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
return new MakeChanges<Task>(json, PluginServices.getTaskDao());
|
||||
} else if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
return new MakeChanges<TagData>(json, PluginServices.getTagDataDao());
|
||||
} else if (NameMaps.TABLE_ID_USERS.equals(table)) {
|
||||
return new MakeChanges<User>(json, PluginServices.getUserDao());
|
||||
} else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) {
|
||||
return new MakeChanges<UserActivity>(json, PluginServices.getUserActivityDao());
|
||||
} else if (NameMaps.TABLE_ID_ATTACHMENTS.equals(table)) {
|
||||
return new MakeChanges<TaskAttachment>(json, PluginServices.getTaskAttachmentDao());
|
||||
} else if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table)) {
|
||||
return new MakeChanges<TaskListMetadata>(json, PluginServices.getTaskListMetadataDao());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static NowBriefed<?> instantiateNowBriefed(JSONObject json) {
|
||||
String table = json.optString("table");
|
||||
if (NameMaps.TABLE_ID_TASKS.equals(table)) {
|
||||
return new NowBriefed<Task>(json, PluginServices.getTaskDao());
|
||||
} else if (NameMaps.TABLE_ID_TAGS.equals(table)) {
|
||||
return new NowBriefed<TagData>(json, PluginServices.getTagDataDao());
|
||||
} else if (NameMaps.TABLE_ID_USER_ACTIVITY.equals(table)) {
|
||||
return new NowBriefed<UserActivity>(json, PluginServices.getUserActivityDao());
|
||||
} else if (NameMaps.TABLE_ID_USERS.equals(table)) {
|
||||
return new NowBriefed<User>(json, PluginServices.getUserDao());
|
||||
} else if (NameMaps.TABLE_ID_ATTACHMENTS.equals(table)) {
|
||||
return new NowBriefed<TaskAttachment>(json, PluginServices.getTaskAttachmentDao());
|
||||
} else if (NameMaps.TABLE_ID_TASK_LIST_METADATA.equals(table)) {
|
||||
return new NowBriefed<TaskListMetadata>(json, PluginServices.getTaskListMetadataDao());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,83 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.todoroo.astrid.dao.TaskListMetadataDao;
|
||||
import com.todoroo.astrid.dao.TaskListMetadataOutstandingDao;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
import com.todoroo.astrid.data.TaskListMetadata;
|
||||
import com.todoroo.astrid.data.TaskListMetadataOutstanding;
|
||||
|
||||
public class TaskListMetadataChangesHappened extends ChangesHappened<TaskListMetadata, TaskListMetadataOutstanding> {
|
||||
|
||||
private final TaskListMetadataDao dao;
|
||||
|
||||
public TaskListMetadataChangesHappened(long id, Class<TaskListMetadata> modelClass, TaskListMetadataDao modelDao, TaskListMetadataOutstandingDao outstandingDao) {
|
||||
super(id, modelClass, modelDao, outstandingDao);
|
||||
this.dao = modelDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populateChanges() {
|
||||
super.populateChanges();
|
||||
|
||||
// Collapses/removes redundant task list orders from the list--only send the most recent ordering
|
||||
Set<Long> removedChanges = new HashSet<Long>();
|
||||
boolean foundOrderChange = false;
|
||||
boolean foundTagOrFilterId = false;
|
||||
for (int i = changes.size() - 1; i >= 0; i--) {
|
||||
TaskListMetadataOutstanding oe = changes.get(i);
|
||||
String column = oe.getValue(TaskListMetadataOutstanding.COLUMN_STRING);
|
||||
if (TaskListMetadata.TASK_IDS.name.equals(column)) {
|
||||
if (foundOrderChange || TaskListMetadata.taskIdsIsEmpty(oe.getValue(TaskListMetadataOutstanding.VALUE_STRING))) {
|
||||
changes.remove(i);
|
||||
removedChanges.add(oe.getId());
|
||||
} else {
|
||||
foundOrderChange = true;
|
||||
}
|
||||
} else if (TaskListMetadata.FILTER.name.equals(column) || TaskListMetadata.TAG_UUID.name.equals(column)) {
|
||||
if (RemoteModel.isUuidEmpty(oe.getValue(TaskListMetadataOutstanding.VALUE_STRING))) {
|
||||
changes.remove(i);
|
||||
removedChanges.add(oe.getId());
|
||||
} else {
|
||||
foundTagOrFilterId = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pushedAt == 0 && !foundTagOrFilterId) { // Try to validate message
|
||||
TaskListMetadata tlm = dao.fetch(id, TaskListMetadata.FILTER, TaskListMetadata.TAG_UUID);
|
||||
if (tlm != null) {
|
||||
String filterId = tlm.getValue(TaskListMetadata.FILTER);
|
||||
String tagUuid = tlm.getValue(TaskListMetadata.TAG_UUID);
|
||||
|
||||
TaskListMetadataOutstanding tlmo = new TaskListMetadataOutstanding();
|
||||
boolean validChange = false;
|
||||
|
||||
if (!TextUtils.isEmpty(filterId)) {
|
||||
validChange = true;
|
||||
tlmo.setValue(TaskListMetadataOutstanding.ENTITY_ID_PROPERTY, id);
|
||||
tlmo.setValue(TaskListMetadataOutstanding.COLUMN_STRING, TaskListMetadata.FILTER.name);
|
||||
tlmo.setValue(TaskListMetadataOutstanding.VALUE_STRING, filterId);
|
||||
tlmo.setValue(TaskListMetadataOutstanding.CREATED_AT, 0L);
|
||||
} else if (!RemoteModel.isUuidEmpty(tagUuid)) {
|
||||
validChange = true;
|
||||
tlmo.setValue(TaskListMetadataOutstanding.ENTITY_ID_PROPERTY, id);
|
||||
tlmo.setValue(TaskListMetadataOutstanding.COLUMN_STRING, TaskListMetadata.TAG_UUID.name);
|
||||
tlmo.setValue(TaskListMetadataOutstanding.VALUE_STRING, tagUuid);
|
||||
tlmo.setValue(TaskListMetadataOutstanding.CREATED_AT, 0L);
|
||||
}
|
||||
|
||||
if (validChange) {
|
||||
changes.add(tlmo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outstandingDao.deleteWhere(TaskListMetadataOutstanding.ID.in(removedChanges.toArray(new Long[removedChanges.size()])));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.todoroo.andlib.sql.Criterion;
|
||||
import com.todoroo.astrid.core.PluginServices;
|
||||
import com.todoroo.astrid.dao.TagMetadataDao.TagMetadataCriteria;
|
||||
import com.todoroo.astrid.data.SyncFlags;
|
||||
import com.todoroo.astrid.data.TagMetadata;
|
||||
import com.todoroo.astrid.data.Task;
|
||||
import com.todoroo.astrid.tags.TagMemberMetadata;
|
||||
|
||||
public class UserData extends ServerToClientMessage {
|
||||
|
||||
public UserData(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(String serverTime) {
|
||||
String uuid = json.optString("uuid");
|
||||
String email = json.optString("email");
|
||||
|
||||
if (TextUtils.isEmpty(uuid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Task taskTemplate = new Task();
|
||||
taskTemplate.setValue(Task.USER_ID, uuid);
|
||||
taskTemplate.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
PluginServices.getTaskDao().update(Task.USER_ID.eq(email), taskTemplate);
|
||||
|
||||
TagMetadata metadataTemplate = new TagMetadata();
|
||||
metadataTemplate.setValue(TagMemberMetadata.USER_UUID, uuid);
|
||||
metadataTemplate.putTransitory(SyncFlags.ACTFM_SUPPRESS_OUTSTANDING_ENTRIES, true);
|
||||
PluginServices.getTagMetadataDao().update(Criterion.and(TagMetadataCriteria.withKey(TagMemberMetadata.KEY),
|
||||
TagMemberMetadata.USER_UUID.eq(email)), metadataTemplate);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package com.todoroo.astrid.actfm.sync.messages;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.todoroo.andlib.utility.Preferences;
|
||||
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
|
||||
import com.todoroo.astrid.data.RemoteModel;
|
||||
|
||||
public class UserMigrated extends ServerToClientMessage {
|
||||
|
||||
public UserMigrated(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processMessage(String serverTime) {
|
||||
String oldUuid = json.optString("prev_user_id"); //$NON-NLS-1$
|
||||
String newUuid = json.optString("new_user_id"); //$NON-NLS-1$
|
||||
if (RemoteModel.isValidUuid(newUuid)) {
|
||||
Preferences.setString(ActFmPreferenceService.PREF_USER_ID, newUuid);
|
||||
new ConvertSelfUserIdsToZero().execute();
|
||||
}
|
||||
|
||||
if (RemoteModel.isValidUuid(oldUuid)) {
|
||||
new ConvertSelfUserIdsToZero().execute(oldUuid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue