Merge remote-tracking branch 'origin/4.0' into 120220-st

pull/14/head
Tim Su 13 years ago
commit 9cff87724e

@ -10,6 +10,7 @@ import java.util.ArrayList;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
@ -230,7 +231,15 @@ abstract public class AbstractDatabase {
* @see android.database.sqlite.SQLiteDatabase#insert(String table, String nullColumnHack, ContentValues values)
*/
public synchronized long insert(String table, String nullColumnHack, ContentValues values) {
long result = getDatabase().insert(table, nullColumnHack, values);
long result = -1;
try {
result = getDatabase().insertOrThrow(table, nullColumnHack, values);
} catch (SQLiteConstraintException e) { // Throw these exceptions
throw e;
} catch (Exception e) { // Suppress others
Log.e("SQLiteDatabase", "Error inserting " + values, e);
result = -1;
}
onDatabaseUpdated();
return result;
}

@ -30,6 +30,9 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
/** The database column name for this property */
public final String name;
/** Can this field be null? */
public boolean nullable = false;
/**
* Create a property by table and column name. Uses the default property
* expression which is derived from default table name
@ -38,6 +41,15 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
this(table, columnName, (table == null) ? (columnName) : (table.name + "." + columnName));
}
/**
* Create a property by table and column name. Uses the default property
* expression which is derived from default table name
*/
protected Property(Table table, String columnName, boolean nullable) {
this(table, columnName, (table == null) ? (columnName) : (table.name + "." + columnName));
this.nullable = nullable;
}
/**
* Create a property by table and column name, manually specifying an
* expression to use in SQL
@ -98,6 +110,10 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
super(table, name);
}
public IntegerProperty(Table table, String name, boolean nullable) {
super(table, name, nullable);
}
protected IntegerProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@ -121,6 +137,10 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
super(table, name);
}
public StringProperty(Table table, String name, boolean nullable) {
super(table, name, nullable);
}
protected StringProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@ -144,6 +164,10 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
super(table, name);
}
public DoubleProperty(Table table, String name, boolean nullable) {
super(table, name, nullable);
}
protected DoubleProperty(Table table, String name, String expression) {
super(table, name, expression);
}
@ -168,6 +192,10 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
super(table, name);
}
public LongProperty(Table table, String name, boolean nullable) {
super(table, name, nullable);
}
protected LongProperty(Table table, String name, String expression) {
super(table, name, expression);
}

@ -98,23 +98,38 @@ public class TodorooCursor<TYPE extends AbstractModel> extends CursorWrapper {
public Object visitDouble(Property<Double> property,
TodorooCursor<?> cursor) {
return cursor.getDouble(cursor.getColumnIndexFromCache(property.name));
int column = columnIndex(property, cursor);
if(property.nullable && cursor.isNull(column))
return null;
return cursor.getDouble(column);
}
public Object visitInteger(Property<Integer> property,
TodorooCursor<?> cursor) {
return cursor.getInt(cursor.getColumnIndexFromCache(property.name));
int column = columnIndex(property, cursor);
if(property.nullable && cursor.isNull(column))
return null;
return cursor.getInt(column);
}
public Object visitLong(Property<Long> property, TodorooCursor<?> cursor) {
return cursor.getLong(cursor.getColumnIndexFromCache(property.name));
int column = columnIndex(property, cursor);
if(property.nullable && cursor.isNull(column))
return null;
return cursor.getLong(column);
}
public Object visitString(Property<String> property,
TodorooCursor<?> cursor) {
return cursor.getString(cursor.getColumnIndexFromCache(property.name));
int column = columnIndex(property, cursor);
if(property.nullable && cursor.isNull(column))
return null;
return cursor.getString(column);
}
private int columnIndex(Property<?> property, TodorooCursor<?> cursor) {
return cursor.getColumnIndexFromCache(property.name);
}
}
}

@ -135,7 +135,7 @@ public final class Task extends RemoteModel {
/** Remote id */
public static final LongProperty REMOTE_ID = new LongProperty(
TABLE, REMOTE_ID_PROPERTY_NAME);
TABLE, REMOTE_ID_PROPERTY_NAME, true);
/** Assigned user id */
public static final LongProperty USER_ID = new LongProperty(
@ -252,7 +252,7 @@ public final class Task extends RemoteModel {
defaultValues.put(DETAILS_DATE.name, 0);
defaultValues.put(LAST_SYNC.name, 0);
defaultValues.put(REMOTE_ID.name, 0);
defaultValues.putNull(REMOTE_ID.name);
defaultValues.put(USER_ID.name, 0);
defaultValues.put(CREATOR_ID.name, 0);
defaultValues.put(USER.name, "{}");

@ -3,6 +3,7 @@ package com.todoroo.astrid.data;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.SQLException;
import com.todoroo.andlib.data.ContentResolverDao;
import com.todoroo.andlib.data.TodorooCursor;
@ -143,9 +144,24 @@ public class TaskApiDao extends ContentResolverDao<Task> {
@Override
public boolean save(Task model) {
ContentValues setValues = model.getSetValues();
if(super.save(model)) {
afterSave(model, setValues);
return true;
try {
if(super.save(model)) {
afterSave(model, setValues);
return true;
}
} catch (SQLException e) {
if (model.containsNonNullValue(Task.REMOTE_ID)) {
TodorooCursor<Task> cursor = query(Query.select(Task.ID).where(Task.REMOTE_ID.eq(model.getValue(Task.REMOTE_ID))));
if (cursor.getCount() > 0) {
cursor.moveToFirst();
model.setId(cursor.get(Task.ID));
setValues = model.getSetValues();
if (super.save(model)) {
afterSave(model, setValues);
return true;
}
}
}
}
return false;
}

@ -3,7 +3,7 @@
<classpathentry excluding="javax/xml/validation/|javax/xml/transform/dom/|javax/xml/parsers/|org/jaxp/transform/dom/|org/jaxp/transform/sax/|org/jaxp/transform/stax/|org/jaxp/transform/stream/|org/jaxp/stream/events/|org/jaxp/stream/util/|org/jaxp/parsers/|org/jaxp/stream/|org/jaxp/validation/" kind="src" path="src"/>
<classpathentry kind="src" path="src-legacy"/>
<classpathentry kind="src" path="common-src"/>
<classpathentry excluding="com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java|com/todoroo/astrid/actfm/TaskFields.java|com/todoroo/astrid/actfm/ShowProjectExposer.java|com/todoroo/astrid/actfm/ProjectDetailExposer.java|com/todoroo/astrid/actfm/ProjectListActivity.java" kind="src" path="plugin-src"/>
<classpathentry excluding="com/todoroo/astrid/actfm/ProjectDetailExposer.java|com/todoroo/astrid/actfm/ProjectListActivity.java|com/todoroo/astrid/actfm/ShowProjectExposer.java|com/todoroo/astrid/actfm/TaskFields.java|com/todoroo/astrid/rmilk/EditOperationExposer.java|com/todoroo/astrid/rmilk/MilkEditActivity.java" kind="src" path="plugin-src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="rmilk-src"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>

@ -106,4 +106,4 @@ public final class ReflectionUtils
throw new RuntimeException(e);
}
}
}
}

@ -40,4 +40,4 @@ public class Api4GestureDetector implements OnGesturePerformedListener {
}
}
}
}
}

@ -138,4 +138,4 @@ public class DateControlSet implements OnTimeSetListener,
});
}
}
}
}

@ -66,4 +66,4 @@ public class DateWithNullControlSet extends DateControlSet {
super.setDate(newDate);
}
}
}

@ -132,7 +132,7 @@ public class C2DMReceiver extends BroadcastReceiver {
try {
TagData tagData = new TagData();
if(cursor.getCount() == 0) {
tagData.setValue(Task.REMOTE_ID, Long.parseLong(intent.getStringExtra("tag_id")));
tagData.setValue(TagData.REMOTE_ID, Long.parseLong(intent.getStringExtra("tag_id")));
Flags.set(Flags.ACTFM_SUPPRESS_SYNC);
tagDataService.save(tagData);
} else {
@ -192,7 +192,17 @@ public class C2DMReceiver extends BroadcastReceiver {
Intent notifyIntent = null;
int notifId;
final long user_id = intent.getLongExtra("oid", -2L);
long user_idTemp = -2;
final String user_idString = intent.getStringExtra("oid");
if (user_idString != null) {
try {
user_idTemp = Long.parseLong(user_idString);
} catch(NumberFormatException e) {
// We tried
Log.e("c2dm-receive", "oid-parse", e);
}
}
final long user_id = user_idTemp;
final String token_id = intent.getStringExtra("tid");
// unregister
if (!actFmPreferenceService.isLoggedIn() || user_id != ActFmPreferenceService.userId()) {

@ -191,4 +191,4 @@ public class ActFmGoogleAuthActivity extends ListActivity {
}
}
}
}

@ -76,4 +76,4 @@ public class ActFmPreferences extends SyncProviderPreferences {
}
}
}
}

@ -695,9 +695,12 @@ public class EditPeopleControlSet extends PopupControlSet {
@SuppressWarnings("nls")
protected Object[] buildSharingArgs(JSONArray emails) throws JSONException {
ArrayList<Object> values = new ArrayList<Object>();
long currentTaskID = task.getValue(Task.REMOTE_ID);
values.add("id");
values.add(currentTaskID);
if(task.containsNonNullValue(Task.REMOTE_ID)) {
long currentTaskID = task.getValue(Task.REMOTE_ID);
values.add("id");
values.add(currentTaskID);
}
if(emails != null) {
for(int i = 0; i < emails.length(); i++) {

@ -126,4 +126,4 @@ public class OAuthLoginActivity extends FragmentActivity {
webView.loadUrl(urlParam);
}
}
}

@ -41,7 +41,6 @@ import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.adapter.UpdateAdapter;
import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
import com.todoroo.astrid.helper.ImageDiskCache;
import com.todoroo.astrid.helper.ProgressBarSyncResultCallback;
@ -205,7 +204,7 @@ public class TagUpdatesFragment extends ListFragment {
}
public void setLastViewed() {
if(tagData != null && tagData.getValue(Task.REMOTE_ID) > 0) {
if(tagData != null && tagData.getValue(TagData.REMOTE_ID) > 0) {
Preferences.setLong(UPDATES_LAST_VIEWED + tagData.getValue(TagData.REMOTE_ID), DateUtilities.now());
Activity activity = getActivity();
if (activity instanceof TaskListActivity)

@ -39,6 +39,7 @@ import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.sync.ActFmPreferenceService;
import com.todoroo.astrid.actfm.sync.ActFmSyncService;
import com.todoroo.astrid.activity.FilterListFragment;
import com.todoroo.astrid.activity.TaskListActivity;
import com.todoroo.astrid.activity.TaskListFragment;
import com.todoroo.astrid.api.AstridApiConstants;
@ -454,9 +455,16 @@ public class TagViewFragment extends TaskListFragment {
if (requestCode == REQUEST_CODE_SETTINGS && resultCode == Activity.RESULT_OK) {
tagData = tagDataService.fetchById(tagData.getId(), TagData.PROPERTIES);
filter = TagFilterExposer.filterFromTagData(getActivity(), tagData);
getActivity().getIntent().putExtra(TOKEN_FILTER, filter);
Activity activity = getActivity();
if (activity instanceof TaskListActivity) {
((TaskListActivity) activity).setListsTitle(filter.title);
FilterListFragment flf = ((TaskListActivity) activity).getFilterListFragment();
if (flf != null)
flf.clear();
}
taskAdapter = null;
refresh();
//loadTaskListContent(true);
} else {
super.onActivityResult(requestCode, resultCode, data);
}

@ -64,7 +64,7 @@ public final class ActFmDataService {
*/
public void clearMetadata() {
ContentValues values = new ContentValues();
values.put(Task.REMOTE_ID.name, 0);
values.putNull(Task.REMOTE_ID.name);
taskDao.updateMultiple(values, Criterion.all);
}
@ -75,7 +75,7 @@ public final class ActFmDataService {
*/
public TodorooCursor<Task> getLocallyCreated(Property<?>[] properties) {
return taskDao.query(Query.select(properties).where(Criterion.and(TaskCriteria.isActive(),
Task.REMOTE_ID.eq(0))));
Task.REMOTE_ID.isNull())));
}
/**
@ -89,7 +89,7 @@ public final class ActFmDataService {
return taskDao.query(Query.select(properties).where(Criterion.none));
return
taskDao.query(Query.select(properties).
where(Criterion.and(Task.REMOTE_ID.gt(0),
where(Criterion.and(Task.REMOTE_ID.isNotNull(),
Task.MODIFICATION_DATE.gt(lastSyncDate),
Task.MODIFICATION_DATE.gt(Task.LAST_SYNC))).groupBy(Task.ID));
}

@ -123,4 +123,4 @@ public class ActFmPreferenceService extends SyncProviderUtilities {
return Preferences.getStringValue(PREF_NAME);
}
}
}

@ -1,363 +0,0 @@
/**
* See the file "LICENSE" for the full license governing this code.
*/
package com.todoroo.astrid.actfm.sync;
import java.io.IOException;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.timsu.astrid.C2DMReceiver;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.ContextManager;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.actfm.ActFmBackgroundService;
import com.todoroo.astrid.actfm.ActFmLoginActivity;
import com.todoroo.astrid.actfm.ActFmPreferences;
import com.todoroo.astrid.actfm.sync.ActFmSyncService.JsonHelper;
import com.todoroo.astrid.api.AstridApiConstants;
import com.todoroo.astrid.core.PluginServices;
import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Metadata;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.notes.NoteMetadata;
import com.todoroo.astrid.service.AstridDependencyInjector;
import com.todoroo.astrid.service.StatisticsConstants;
import com.todoroo.astrid.service.StatisticsService;
import com.todoroo.astrid.sync.SyncProvider;
import com.todoroo.astrid.sync.SyncProviderUtilities;
import com.todoroo.astrid.utility.Constants;
@SuppressWarnings("nls")
public class ActFmSyncProvider extends SyncProvider<ActFmTaskContainer> {
private ActFmInvoker invoker = null;
@Autowired ActFmDataService actFmDataService;
@Autowired ActFmSyncService actFmSyncService;
@Autowired ActFmPreferenceService actFmPreferenceService;
@Autowired TagDataDao tagDataDao;
static {
AstridDependencyInjector.initialize();
}
// ----------------------------------------------------------------------
// ------------------------------------------------------ utility methods
// ----------------------------------------------------------------------
@Override
protected SyncProviderUtilities getUtilities() {
return actFmPreferenceService;
}
/**
* Sign out of service, deleting all synchronization metadata
*/
public void signOut() {
actFmPreferenceService.setToken(null);
actFmPreferenceService.clearLastSyncDate();
C2DMReceiver.unregister();
}
// ----------------------------------------------------------------------
// ------------------------------------------------------ initiating sync
// ----------------------------------------------------------------------
/**
* initiate sync in background
*/
@Override
protected void initiateBackground() {
try {
C2DMReceiver.register();
String authToken = actFmPreferenceService.getToken();
invoker = new ActFmInvoker(authToken);
// check if we have a token & it works
if(authToken != null) {
performSync();
}
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (Exception e) {
handleException("actfm-authenticate", e, false);
} finally {
actFmPreferenceService.stopOngoing();
}
}
/**
* If user isn't already signed in, show sign in dialog. Else perform sync.
*/
@Override
protected void initiateManual(Activity activity) {
String authToken = actFmPreferenceService.getToken();
actFmPreferenceService.stopOngoing();
// check if we have a token & it works
if(authToken == null) {
// display login-activity
Intent intent = new Intent(activity, ActFmLoginActivity.class);
activity.startActivityForResult(intent, 0);
} else {
activity.startService(new Intent(null, null,
activity, ActFmBackgroundService.class));
}
}
// ----------------------------------------------------------------------
// ----------------------------------------------------- synchronization!
// ----------------------------------------------------------------------
protected void performSync() {
actFmPreferenceService.recordSyncStart();
String syncSuccess = "failed";
try {
int serverTime = Preferences.getInt(ActFmPreferenceService.PREF_SERVER_TIME, 0);
ArrayList<ActFmTaskContainer> remoteTasks = new ArrayList<ActFmTaskContainer>();
// int newServerTime = fetchRemoteTasks(serverTime, remoteTasks);
if (serverTime == 0) { // If we've never synced, we may lose some empty tags
pushUnsavedTagData();
}
// fetchRemoteTagData(serverTime);
/* SyncData<ActFmTaskContainer> syncData = populateSyncData(remoteTasks);
try {
synchronizeTasks(syncData);
} finally {
syncData.localCreated.close();
syncData.localUpdated.close();
}
Preferences.setInt(ActFmPreferenceService.PREF_SERVER_TIME, newServerTime); */
actFmPreferenceService.recordSuccessfulSync();
syncSuccess = getFinalSyncStatus();
Intent broadcastIntent = new Intent(AstridApiConstants.BROADCAST_EVENT_REFRESH);
ContextManager.getContext().sendBroadcast(broadcastIntent, AstridApiConstants.PERMISSION_READ);
} catch (IllegalStateException e) {
// occurs when application was closed
} catch (Exception e) {
handleException("actfm-sync", e, false); //$NON-NLS-1$
} finally {
StatisticsService.reportEvent(StatisticsConstants.ACTFM_SYNC_FINISHED,
"success", syncSuccess); //$NON-NLS-1$
}
}
//Pushes unsaved, empty tag data, which will otherwise be deleted by the fetch tags call
private void pushUnsavedTagData() {
TodorooCursor<TagData> unsavedTagData = tagDataDao.query(Query.select(TagData.ID, TagData.TASK_COUNT).where(Criterion.and(TagData.TASK_COUNT.eq(0), TagData.REMOTE_ID.eq(0))));
TagData data = new TagData();
for (unsavedTagData.moveToFirst(); !unsavedTagData.isAfterLast(); unsavedTagData.moveToNext()) {
data.readFromCursor(unsavedTagData);
actFmSyncService.pushTag(data.getId());
}
}
/**
* Read remote tag data and merge with local
* @param serverTime last sync time
*/
private void fetchRemoteTagData(int serverTime) throws ActFmServiceException, IOException, JSONException {
actFmSyncService.fetchTags(serverTime);
}
/**
* Read remote task data into remote task array
* @param serverTime last sync time
*/
private int fetchRemoteTasks(int serverTime,
ArrayList<ActFmTaskContainer> remoteTasks) throws IOException,
ActFmServiceException, JSONException {
JSONObject result;
if(serverTime == 0)
result = invoker.invoke("task_list", "active", 1);
else
result = invoker.invoke("task_list", "modified_after", serverTime);
JSONArray taskList = result.getJSONArray("list");
for(int i = 0; i < taskList.length(); i++) {
ActFmTaskContainer remote = parseRemoteTask(taskList.getJSONObject(i));
// update reminder flags for incoming remote tasks to prevent annoying
if(remote.task.hasDueDate() && remote.task.getValue(Task.DUE_DATE) < DateUtilities.now())
remote.task.setFlag(Task.REMINDER_FLAGS, Task.NOTIFY_AFTER_DEADLINE, false);
actFmDataService.findLocalMatch(remote);
remoteTasks.add(remote);
}
return result.optInt("time", 0);
}
// ----------------------------------------------------------------------
// ------------------------------------------------------------ sync data
// ----------------------------------------------------------------------
/**
* Populate SyncData data structure
* @throws JSONException
*/
private SyncData<ActFmTaskContainer> populateSyncData(ArrayList<ActFmTaskContainer> remoteTasks) throws JSONException {
// fetch locally created tasks
TodorooCursor<Task> localCreated = actFmDataService.getLocallyCreated(Task.PROPERTIES);
// fetch locally updated tasks
TodorooCursor<Task> localUpdated = actFmDataService.getLocallyUpdated(Task.PROPERTIES);
return new SyncData<ActFmTaskContainer>(remoteTasks, localCreated, localUpdated);
}
// ----------------------------------------------------------------------
// ------------------------------------------------- create / push / pull
// ----------------------------------------------------------------------
@Override
protected ActFmTaskContainer create(ActFmTaskContainer local) throws IOException {
return push(local, null);
}
/** Create a task container for the given remote task
* @throws JSONException */
private ActFmTaskContainer parseRemoteTask(JSONObject remoteTask) throws JSONException {
Task task = new Task();
ArrayList<Metadata> metadata = new ArrayList<Metadata>();
JsonHelper.taskFromJson(remoteTask, task, metadata);
ActFmTaskContainer container = new ActFmTaskContainer(task, metadata, remoteTask);
return container;
}
@Override
protected ActFmTaskContainer pull(ActFmTaskContainer task) throws IOException {
if(task.task.getValue(Task.REMOTE_ID) == 0)
throw new ActFmServiceException("Tried to read an invalid task"); //$NON-NLS-1$
JSONObject remote = invoker.invoke("task_show", "id", task.task.getValue(Task.REMOTE_ID));
try {
return parseRemoteTask(remote);
} catch (JSONException e) {
throw new ActFmServiceException(e);
}
}
/**
* Send changes for the given Task across the wire.
*/
@Override
protected ActFmTaskContainer push(ActFmTaskContainer local, ActFmTaskContainer remote) throws IOException {
long id = local.task.getValue(Task.REMOTE_ID);
actFmSyncService.pushTaskOnSave(local.task, local.task.getDatabaseValues());
// push unsaved comments
for(Metadata item : local.metadata) {
if(NoteMetadata.METADATA_KEY.equals(item.getValue(Metadata.KEY)))
if(TextUtils.isEmpty(item.getValue(NoteMetadata.EXT_ID))) {
JSONObject comment = invoker.invoke("comment_add",
"task_id", id,
"message", item.getValue(NoteMetadata.BODY));
item.setValue(NoteMetadata.EXT_ID, comment.optString("id"));
}
}
return local;
}
@Override
protected void readRemotelyUpdated(SyncData<ActFmTaskContainer> data) throws IOException {
int serverTime = Preferences.getInt(ActFmPreferenceService.PREF_SERVER_TIME, 0);
ArrayList<ActFmTaskContainer> remoteTasks = new ArrayList<ActFmTaskContainer>();
try {
fetchRemoteTasks(serverTime, remoteTasks);
data.remoteUpdated = remoteTasks;
} catch (JSONException e) {
// Ingnored
}
super.readRemotelyUpdated(data);
}
// ----------------------------------------------------------------------
// --------------------------------------------------------- read / write
// ----------------------------------------------------------------------
@Override
protected ActFmTaskContainer read(TodorooCursor<Task> cursor) throws IOException {
return actFmDataService.readTaskAndMetadata(cursor);
}
@Override
protected void write(ActFmTaskContainer task) throws IOException {
if(task.task.isSaved()) {
Task local = PluginServices.getTaskService().fetchById(task.task.getId(), Task.COMPLETION_DATE);
if(task.task.isCompleted() && !local.isCompleted())
StatisticsService.reportEvent(StatisticsConstants.ACTFM_TASK_COMPLETED);
} else { // Set default reminders for remotely created tasks
TaskDao.setDefaultReminders(task.task);
}
task.task.setValue(Task.LAST_SYNC, DateUtilities.now() + 1000);
actFmDataService.saveTaskAndMetadata(task);
}
// ----------------------------------------------------------------------
// --------------------------------------------------------- misc helpers
// ----------------------------------------------------------------------
@Override
protected int matchTask(ArrayList<ActFmTaskContainer> tasks, ActFmTaskContainer target) {
int length = tasks.size();
for(int i = 0; i < length; i++) {
ActFmTaskContainer task = tasks.get(i);
if (task.task.getValue(Task.REMOTE_ID) == target.task.getValue(Task.REMOTE_ID))
return i;
}
return -1;
}
@Override
protected int updateNotification(Context context, Notification notification) {
String notificationTitle = context.getString(R.string.actfm_notification_title);
Intent intent = new Intent(context, ActFmPreferences.class);
PendingIntent notificationIntent = PendingIntent.getActivity(context, 0,
intent, 0);
notification.setLatestEventInfo(context,
notificationTitle, context.getString(R.string.SyP_progress),
notificationIntent);
return Constants.NOTIFICATION_SYNC;
}
@Override
protected void transferIdentifiers(ActFmTaskContainer source,
ActFmTaskContainer destination) {
destination.task.setValue(Task.REMOTE_ID, source.task.getValue(Task.REMOTE_ID));
}
}

@ -298,13 +298,14 @@ public final class ActFmSyncService {
public void pushTaskOnSave(Task task, ContentValues values) {
Task taskForRemote = taskService.fetchById(task.getId(), Task.REMOTE_ID, Task.CREATION_DATE);
long remoteId;
if(task.containsValue(Task.REMOTE_ID)) {
long remoteId = 0;
if(task.containsNonNullValue(Task.REMOTE_ID)) {
remoteId = task.getValue(Task.REMOTE_ID);
} else {
if(taskForRemote == null)
return;
remoteId = taskForRemote.getValue(Task.REMOTE_ID);
if(taskForRemote.containsNonNullValue(Task.REMOTE_ID))
remoteId = taskForRemote.getValue(Task.REMOTE_ID);
}
long creationDate;
@ -627,6 +628,12 @@ public final class ActFmSyncService {
Order.asc(TagData.REMOTE_ID)));
return cursorToMap(cursor, taskDao, TagData.REMOTE_ID, TagData.ID);
}
@Override
protected Class<TagData> typeClass() {
return TagData.class;
}
}, done, "goals");
}
@ -747,6 +754,11 @@ public final class ActFmSyncService {
Order.asc(Task.REMOTE_ID)));
return cursorToMap(cursor, taskDao, Task.REMOTE_ID, Task.ID);
}
@Override
protected Class<Task> typeClass() {
return Task.class;
}
}, done, "active_tasks");
}
@ -796,6 +808,11 @@ public final class ActFmSyncService {
Order.asc(Task.REMOTE_ID)));
return cursorToMap(cursor, taskDao, Task.REMOTE_ID, Task.ID);
}
@Override
protected Class<Task> typeClass() {
return Task.class;
}
}, done, "tasks:" + tagData.getId(), "tag_id", tagData.getValue(TagData.REMOTE_ID));
}
@ -860,22 +877,15 @@ public final class ActFmSyncService {
TodorooCursor<Update> cursor = updateDao.query(Query.select(Update.ID, Update.PICTURE).where(criterion));
pushQueuedUpdates(cursor);
Log.d("ActFmSyncService", "Push queued updates for tag");
}
private void pushQueuedUpdates(Task task) {
Criterion criterion = null;
if (task.getValue(Task.REMOTE_ID) < 1) {
criterion = Criterion.and(Update.REMOTE_ID.eq(0),
Update.TASK_LOCAL.eq(task.getId()));
}
else {
if (task.containsNonNullValue(Task.REMOTE_ID)) {
criterion = Criterion.and(Update.REMOTE_ID.eq(0),
Criterion.or(Update.TASK.eq(task.getValue(Task.REMOTE_ID)), Update.TASK_LOCAL.eq(task.getId())));
}
} else
return;
Update template = new Update();
template.setValue(Update.TASK, task.getValue(Task.REMOTE_ID)); //$NON-NLS-1$
@ -883,8 +893,6 @@ public final class ActFmSyncService {
TodorooCursor<Update> cursor = updateDao.query(Query.select(Update.ID, Update.PICTURE).where(criterion));
pushQueuedUpdates(cursor);
Log.d("ActFmSyncService", "Push queued updates for task");
}
private void pushQueuedUpdates( TodorooCursor<Update> cursor) {
@ -896,18 +904,15 @@ public final class ActFmSyncService {
final Update update = new Update(cursor);
new Thread(new Runnable() {
public void run() {
try {
Bitmap picture = null;
if(imageCache != null && imageCache.contains(update.getValue(update.PICTURE))) {
try {
picture = imageCache.get(update.getValue(update.PICTURE));
} catch (IOException e) {
e.printStackTrace();
}
Bitmap picture = null;
if(imageCache != null && imageCache.contains(update.getValue(Update.PICTURE))) {
try {
picture = imageCache.get(update.getValue(Update.PICTURE));
} catch (IOException e) {
e.printStackTrace();
}
pushUpdate(update.getId(), picture);
} finally {
}
pushUpdate(update.getId(), picture);
}
}).start();
}
@ -916,11 +921,6 @@ public final class ActFmSyncService {
}
}
private class UpdateListItemProcessor extends ListItemProcessor<Update> {
@Override
protected void mergeAndSave(JSONArray list, HashMap<Long,Long> locals) throws JSONException {
@ -946,6 +946,11 @@ public final class ActFmSyncService {
Order.asc(Update.REMOTE_ID)));
return cursorToMap(cursor, updateDao, Update.REMOTE_ID, Update.ID);
}
@Override
protected Class<Update> typeClass() {
return Update.class;
}
}
/**
@ -999,16 +1004,19 @@ public final class ActFmSyncService {
abstract protected HashMap<Long, Long> getLocalModels();
abstract protected Class<TYPE> typeClass();
abstract protected void mergeAndSave(JSONArray list,
HashMap<Long,Long> locals) throws JSONException;
public void process(JSONArray list) throws JSONException {
readRemoteIds(list);
HashMap<Long, Long> locals = getLocalModels();
mergeAndSave(list, locals);
synchronized (typeClass()) {
HashMap<Long, Long> locals = getLocalModels();
mergeAndSave(list, locals);
}
}
protected void readRemoteIds(JSONArray list) throws JSONException {
remoteIds = new Long[list.length()];
for(int i = 0; i < list.length(); i++)

@ -213,8 +213,8 @@ public class ActFmSyncV2Provider extends SyncV2Provider {
TodorooCursor<Task> taskCursor = taskService.query(Query.select(Task.PROPERTIES).
where(Criterion.or(
Criterion.and(TaskCriteria.isActive(),
Task.REMOTE_ID.eq(0)),
Criterion.and(Task.REMOTE_ID.gt(0),
Task.REMOTE_ID.isNull()),
Criterion.and(Task.REMOTE_ID.isNotNull(),
Task.MODIFICATION_DATE.gt(Task.LAST_SYNC)))));
pushQueued(callback, finisher, taskCursor, false, taskPusher);

@ -128,4 +128,4 @@ public class BackupPreferences extends TodorooPreferenceActivity {
}
}
}

@ -103,4 +103,4 @@ public class DefaultsPreferences extends TodorooPreferenceActivity {
}
}
}
}

@ -291,4 +291,4 @@ public class OldTaskPreferences extends TodorooPreferenceActivity {
// :)
}
}
}

@ -233,4 +233,4 @@ public class Calendars {
return Preferences.getStringValue(R.string.gcal_p_default);
}
}
}

@ -77,7 +77,7 @@ public class GtasksListService {
}
@SuppressWarnings("nls")
public void updateLists(TaskLists remoteLists) {
public synchronized void updateLists(TaskLists remoteLists) {
readLists();
for(StoreObject list : lists)

@ -78,7 +78,7 @@ public final class GtasksMetadataService extends SyncMetadataService<GtasksTaskC
}
@Override
public void findLocalMatch(GtasksTaskContainer remoteTask) {
public synchronized void findLocalMatch(GtasksTaskContainer remoteTask) {
if(remoteTask.task.getId() != Task.NO_ID)
return;
TodorooCursor<Metadata> cursor = metadataDao.query(Query.select(Metadata.PROPERTIES).

@ -71,4 +71,4 @@ public class GtasksPreferences extends SyncProviderPreferences {
super.onPause();
new GtasksBackgroundService().scheduleService();
}
}
}

@ -233,4 +233,4 @@ public class GtasksLoginActivity extends ListActivity {
}
}
}
}

@ -230,7 +230,7 @@ public final class GtasksSyncService {
}
task.setValue(Task.MODIFICATION_DATE, DateUtilities.now());
gtasksMetadata.setValue(GtasksMetadata.LAST_SYNC, DateUtilities.now());
gtasksMetadata.setValue(GtasksMetadata.LAST_SYNC, DateUtilities.now() + 1000L);
metadataService.save(gtasksMetadata);
Flags.set(Flags.GTASKS_SUPPRESS_SYNC);
taskDao.saveExisting(task);

@ -208,7 +208,7 @@ public class GtasksSyncV2Provider extends SyncV2Provider {
gtasksMetadataService.findLocalMatch(container);
container.gtaskMetadata.setValue(GtasksMetadata.GTASKS_ORDER, Long.parseLong(t.getPosition()));
container.gtaskMetadata.setValue(GtasksMetadata.PARENT_TASK, gtasksMetadataService.localIdForGtasksId(t.getParent()));
container.gtaskMetadata.setValue(GtasksMetadata.LAST_SYNC, DateUtilities.now());
container.gtaskMetadata.setValue(GtasksMetadata.LAST_SYNC, DateUtilities.now() + 1000L);
write(container);
callback.incrementProgress(10);
}

@ -53,4 +53,4 @@ public class GtasksTaskContainer extends SyncContainer {
super.prepareForSaving();
metadata.add(gtaskMetadata);
}
}
}

@ -337,4 +337,4 @@ public final class LocaleEditAlerts extends ListActivity {
return super.onOptionsItemSelected(item);
}
}
}

@ -121,7 +121,6 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
}
public void loadViewForTaskID(long t){
task = PluginServices.getTaskService().fetchById(t, Task.NOTES, Task.ID, Task.REMOTE_ID, Task.TITLE);
if(task == null) {
return;
@ -130,7 +129,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
setUpListAdapter();
if(actFmPreferenceService.isLoggedIn()) {
if(task.getValue(Task.REMOTE_ID) == 0)
if(!task.containsNonNullValue(Task.REMOTE_ID))
refreshData(true, null);
else {
String fetchKey = LAST_FETCH_KEY + task.getId();
@ -257,7 +256,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
TodorooCursor<Update> updates;
if (task.getValue(Task.REMOTE_ID) < 1) {
if (!task.containsNonNullValue(Task.REMOTE_ID)) {
updates = updateDao.query(Query.select(Update.PROPERTIES).where(Update.TASK_LOCAL.eq(task.getId())));
}
else {
@ -406,7 +405,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
}
// push task if it hasn't been pushed
if(task.getValue(Task.REMOTE_ID) == 0 && !TextUtils.isEmpty(task.getValue(Task.TITLE))) {
if(!task.containsNonNullValue(Task.REMOTE_ID) && !TextUtils.isEmpty(task.getValue(Task.TITLE))) {
new Thread(new Runnable() {
@Override
public void run() {
@ -446,7 +445,8 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
update.setValue(Update.MESSAGE, message);
update.setValue(Update.ACTION_CODE, actionCode);
update.setValue(Update.USER_ID, 0L);
update.setValue(Update.TASK, task.getValue(Task.REMOTE_ID));
if(task.containsNonNullValue(Task.REMOTE_ID))
update.setValue(Update.TASK, task.getValue(Task.REMOTE_ID));
update.setValue(Update.TASK_LOCAL, task.getId());
update.setValue(Update.CREATION_DATE, DateUtilities.now());

@ -405,4 +405,4 @@ public class OpencrxControlSet extends PopupControlSet {
// Nothing to do
}
}
}

@ -277,4 +277,4 @@ public class ProducteevControlSet extends PopupControlSet {
// TODO Auto-generated method stub
}
}
}

@ -94,4 +94,4 @@ public class ProducteevPreferences extends SyncProviderPreferences {
((ListPreference)preference).getEntries()[index]));
}
}
}
}

@ -76,4 +76,4 @@ public class ProducteevUtilities extends SyncProviderUtilities {
return Preferences.getStringValue(R.string.producteev_PPr_email);
}
}
}

@ -101,4 +101,4 @@ public class ReminderPreferences extends TodorooPreferenceActivity {
}
}
}

@ -420,4 +420,4 @@ public class RepeatControlSet extends PopupControlSet {
return d;
}
}
}

@ -212,7 +212,7 @@ public class TaskRabbitControlSet extends TaskEditControlSet implements Assigned
statusText.setText(status);
getDisplayView().setVisibility(View.VISIBLE);
}
else if (taskRabbitTask.getLocalTaskData() != null) {
else if (taskRabbitTask != null && taskRabbitTask.getLocalTaskData() != null) {
TextView statusText = (TextView) getDisplayView().findViewById(R.id.display_row_edit);
statusText.setText(fragment.getActivity().getString(R.string.tr_status_draft));
getDisplayView().setVisibility(View.VISIBLE);

@ -81,4 +81,4 @@ public class TaskRabbitTaskContainer extends SyncContainer {
public boolean isTaskRabbit() {
return getTaskID() > 0;
}
}
}

@ -104,4 +104,4 @@ public class TimerControlSet extends PopupControlSet implements TimerActionListe
return;
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 311 B

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 B

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 521 B

@ -40,8 +40,9 @@
<com.todoroo.astrid.ui.CheckableImageView
android:id="@+id/completeBox"
android:layout_width="45dip"
android:padding="8dip"
android:layout_height="fill_parent"
android:layout_height="wrap_content"
android:minHeight="30dip"
android:scaleType="center"
android:layout_alignParentTop="true"
android:layout_marginLeft="2dip"
android:button="@drawable/btn_check" />

@ -19,8 +19,8 @@
android:id="@+id/rowBody"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dip"
android:layout_marginBottom="6dip"
android:layout_marginTop="5dip"
android:layout_marginBottom="5dip"
android:minHeight="45dip">
<ImageView
@ -48,10 +48,9 @@
<com.todoroo.astrid.ui.CheckableImageView
android:id="@+id/completeBox"
android:layout_width="45dip"
android:padding="8dip"
android:layout_marginLeft="5dip"
android:layout_height="fill_parent"
android:minHeight="40dip"
android:layout_height="wrap_content"
android:minHeight="30dip"
android:scaleType="center"
android:layout_centerVertical="true" />
@ -82,7 +81,6 @@
android:id="@+id/taskActionContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="40dip"
android:layout_marginRight="4dip"
android:orientation="vertical"
android:gravity="right|center_vertical">

@ -251,7 +251,7 @@
<!-- ================================================ Synchronization == -->
<!-- Preferences Title: Act.fm -->
<string name="actfm_APr_header">Astrid.com (Beta!)</string>
<string name="actfm_APr_header">Astrid.com</string>
<string name="actfm_https_title">Use HTTPS</string>
<string name="actfm_https_enabled">HTTPS enabled (slower)</string>

@ -403,7 +403,7 @@
<string name="TEA_control_title">Task Title</string>
<string name="TEA_control_who">Who</string>
<string name="TEA_control_when">When</string>
<string name="TEA_control_more_section">----More Section----</string>
<string name="TEA_control_more_section">----Details----</string>
<string name="TEA_control_importance">Importance</string>
<string name="TEA_control_lists">Lists</string>
<string name="TEA_control_notes">Notes</string>

@ -86,7 +86,7 @@
<!-- ============================================== GtasksPreferences == -->
<!-- GTasks Preferences Title -->
<string name="gtasks_GPr_header">Google Tasks (Beta!)</string>
<string name="gtasks_GPr_header">Google Tasks</string>
<!-- ================================================ Synchronization == -->

@ -6,6 +6,7 @@
<PreferenceScreen android:title="@string/p_about" android:key="@string/p_about" />
<PreferenceScreen android:title="@string/welcome_setting" android:key="@string/p_tutorial"/>
<PreferenceScreen android:title="@string/p_help" android:key="@string/p_help"/>
<PreferenceCategory
android:title="@string/EPr_appearance_header">
@ -54,6 +55,12 @@
android:key="@string/p_showSmartConfirmation_key"
android:title="@string/EPr_showSmartConfirmation_title"
android:defaultValue="true"/>
<com.todoroo.astrid.ui.MultilineCheckboxPreference
android:key="@string/p_autoIdea"
android:title="@string/EPr_ideaAuto_title"
android:defaultValue="true" />
</PreferenceCategory>
@ -85,11 +92,6 @@
android:key="@string/p_statistics"
android:title="@string/EPr_statistics_title"
android:defaultValue="true" />
<com.todoroo.astrid.ui.MultilineCheckboxPreference
android:key="@string/p_autoIdea"
android:title="@string/EPr_ideaAuto_title"
android:defaultValue="true" />
</PreferenceCategory>

@ -36,4 +36,4 @@ public class MilkPreferences extends SyncProviderPreferences {
return MilkUtilities.INSTANCE;
}
}
}

@ -65,4 +65,4 @@ public class MilkTaskContainer extends SyncContainer {
metadata.add(MilkTaskFields.create(this));
}
}
}

@ -65,4 +65,4 @@ class About {
private About() {
// don't construct me
}
}
}

@ -12,6 +12,7 @@ import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
import android.view.WindowManager.BadTokenException;
import com.timsu.astrid.R;
import com.todoroo.andlib.service.ContextManager;
@ -130,8 +131,16 @@ public class AstridActivity extends FragmentActivity
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(reminderReceiver);
unregisterReceiver(repeatConfirmationReceiver);
tryUnregisterReceiver(reminderReceiver);
tryUnregisterReceiver(repeatConfirmationReceiver);
}
public void tryUnregisterReceiver(BroadcastReceiver receiver) {
try {
unregisterReceiver(receiver);
} catch (IllegalArgumentException e) {
// Receiver wasn't registered for some reason
}
}
/**
@ -337,13 +346,24 @@ public class AstridActivity extends FragmentActivity
private class ReminderReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
public void onReceive(Context context, final Intent intent) {
// Process in app notification
Intent customIntent = intent.getExtras().getParcelable(Notifications.EXTRAS_CUSTOM_INTENT);
long taskId = customIntent.getLongExtra(NotificationFragment.TOKEN_ID, 0);
if (taskId > 0) {
String text = intent.getStringExtra(Notifications.EXTRAS_TEXT);
new ReminderDialog(AstridActivity.this, taskId, text).show();
try {
new ReminderDialog(AstridActivity.this, taskId, text).show();
} catch (BadTokenException e) { // Activity not running when tried to show dialog--rebroadcast
new Thread() {
@Override
public void run() {
AndroidUtilities.sleepDeep(500L);
sendBroadcast(intent);
}
}.start();
return;
}
}
// Remove broadcast
@ -354,7 +374,7 @@ public class AstridActivity extends FragmentActivity
private class RepeatConfirmationReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
public void onReceive(Context context, final Intent intent) {
long taskId = intent.getLongExtra(
AstridApiConstants.EXTRAS_TASK_ID, 0);
if (taskId > 0) {
@ -365,8 +385,17 @@ public class AstridActivity extends FragmentActivity
Task task = PluginServices.getTaskService().fetchById(taskId,
DateChangedAlerts.REPEAT_RESCHEDULED_PROPERTIES);
DateChangedAlerts.showRepeatTaskRescheduledDialog(
AstridActivity.this, task, oldDueDate, newDueDate);
try {
DateChangedAlerts.showRepeatTaskRescheduledDialog(
AstridActivity.this, task, oldDueDate, newDueDate);
} catch (BadTokenException e) { // Activity not running when tried to show dialog--rebroadcast
new Thread() {
@Override
public void run() {
sendBroadcast(intent);
}
}.start();
}
}
}
}

@ -465,4 +465,4 @@ public class EditPreferences extends TodorooPreferenceActivity {
super.onStop();
}
}
}

@ -351,4 +351,4 @@ public class ExpandableListFragment extends Fragment
int groupPosition, int childPosition, long id) {
return false;
}
}
}

@ -497,12 +497,15 @@ public class FilterListFragment extends ListFragment {
if(intent == null || !AstridApiConstants.BROADCAST_EVENT_REFRESH.equals(intent.getAction()))
return;
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
refresh();
}
});
Activity activity = getActivity();
if (activity != null) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
refresh();
}
});
}
}
}
}

@ -655,28 +655,30 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
public void onUiThread() {
// prepare and set listener for voice-button
if (addOnService.hasPowerPack()) {
voiceAddNoteButton = (ImageButton) notesControlSet.getView().findViewById(
R.id.voiceAddNoteButton);
voiceAddNoteButton.setVisibility(View.VISIBLE);
int prompt = R.string.voice_edit_note_prompt;
voiceNoteAssistant = new VoiceInputAssistant(TaskEditFragment.this,
voiceAddNoteButton, notesEditText, REQUEST_VOICE_RECOG);
voiceNoteAssistant.setAppend(true);
voiceNoteAssistant.setLanguageModel(RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
voiceNoteAssistant.configureMicrophoneButton(prompt);
}
if (getActivity() != null) {
if (addOnService.hasPowerPack()) {
voiceAddNoteButton = (ImageButton) notesControlSet.getView().findViewById(
R.id.voiceAddNoteButton);
voiceAddNoteButton.setVisibility(View.VISIBLE);
int prompt = R.string.voice_edit_note_prompt;
voiceNoteAssistant = new VoiceInputAssistant(TaskEditFragment.this,
voiceAddNoteButton, notesEditText, REQUEST_VOICE_RECOG);
voiceNoteAssistant.setAppend(true);
voiceNoteAssistant.setLanguageModel(RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
voiceNoteAssistant.configureMicrophoneButton(prompt);
}
// re-read all
synchronized (controls) {
for (TaskEditControlSet controlSet : controls)
controlSet.readFromTask(model);
if (isNewTask) {
hideUntilControls.setDefaults();
// re-read all
synchronized (controls) {
for (TaskEditControlSet controlSet : controls)
controlSet.readFromTask(model);
if (isNewTask) {
hideUntilControls.setDefaults();
}
}
}
loadMoreContainer();
loadMoreContainer();
}
}
@Override
@ -719,10 +721,9 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
if (idParam > -1L) {
model = taskService.fetchById(idParam, Task.PROPERTIES);
if (model != null) {
if (model != null && model.containsNonNullValue(Task.REMOTE_ID)) {
remoteId = model.getValue(Task.REMOTE_ID);
model.clearValue(Task.REMOTE_ID); // Having this can screw up
// autosync
model.clearValue(Task.REMOTE_ID); // Having this can screw up autosync
}
}
@ -738,6 +739,7 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
}
model = TaskService.createWithValues(values, null,
taskService, metadataService);
getActivity().getIntent().putExtra(TOKEN_ID, model.getId());
}
if (model.getValue(Task.TITLE).length() == 0) {
@ -874,7 +876,8 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
R.anim.slide_right_in, R.anim.slide_right_out);
}
if (title.getText().length() == 0 && isNewTask && model.isSaved()) {
if (title.getText().length() == 0 && isNewTask
&& model != null && model.isSaved()) {
taskService.delete(model);
}
}
@ -959,12 +962,6 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
Toast.LENGTH_SHORT).show();
}
protected void commentsButtonClick() {
Intent launchIntent = new Intent(getActivity(), EditNoteActivity.class);
launchIntent.putExtra(EditNoteActivity.EXTRA_TASK_ID, model.getId());
startActivity(launchIntent);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {

@ -74,4 +74,4 @@ public class TaskEditViewPager extends PagerAdapter implements TitleProvider {
return null;
}
}
}

@ -923,17 +923,20 @@ public class TaskListFragment extends ListFragment implements OnScrollListener,
return;
if (!Preferences.getBoolean(
R.string.p_showed_lists_help, false)) {
if (AndroidUtilities.isTabletSized(getActivity())) {
((AstridActivity) getActivity()).getFilterListFragment().showAddListPopover();
} else {
ActionBar ab = ((AstridActivity) getActivity()).getSupportActionBar();
View anchor = ab.getCustomView().findViewById(R.id.lists_nav);
HelpInfoPopover.showPopover(getActivity(),
anchor, R.string.help_popover_switch_lists, null);
AstridActivity activity = (AstridActivity) getActivity();
if (activity != null) {
if (AndroidUtilities.isTabletSized(activity)) {
activity.getFilterListFragment().showAddListPopover();
} else {
ActionBar ab = activity.getSupportActionBar();
View anchor = ab.getCustomView().findViewById(R.id.lists_nav);
HelpInfoPopover.showPopover(activity,
anchor, R.string.help_popover_switch_lists, null);
}
Preferences.setBoolean(
R.string.p_showed_lists_help,
true);
}
Preferences.setBoolean(
R.string.p_showed_lists_help,
true);
}
}

@ -157,6 +157,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
public static int APPLY_LISTENERS_NONE = 2;
protected final TaskListFragment fragment;
protected final Resources resources;
protected final HashMap<Long, Boolean> completedItems = new HashMap<Long, Boolean>(0);
protected OnCompletedTaskListener onCompletedTaskListener = null;
public boolean isFling = false;
@ -206,6 +207,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
this.query = query;
this.resource = resource;
this.fragment = fragment;
this.resources = fragment.getResources();
this.onCompletedTaskListener = onCompletedTaskListener;
fontSize = Preferences.getIntegerFromString(R.string.p_fontSize, 18);
@ -213,7 +215,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
displayMetrics = new DisplayMetrics();
fragment.getActivity().getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
this.minRowHeight = (int) (4 * displayMetrics.density);
this.minRowHeight = (int) (45 * displayMetrics.density);
startDetailThread();
startTaskActionsThread();
@ -352,7 +354,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
/** Helper method to set the contents and visibility of each field */
public synchronized void setFieldContentsAndVisibility(View view) {
Resources r = fragment.getResources();
ViewHolder viewHolder = (ViewHolder)view.getTag();
Task task = viewHolder.task;
@ -368,9 +369,9 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
long hiddenUntil = task.getValue(Task.HIDE_UNTIL);
if(task.getValue(Task.DELETION_DATE) > 0)
nameValue = r.getString(R.string.TAd_deletedFormat, nameValue);
nameValue = resources.getString(R.string.TAd_deletedFormat, nameValue);
if(hiddenUntil > DateUtilities.now())
nameValue = r.getString(R.string.TAd_hiddenFormat, nameValue);
nameValue = resources.getString(R.string.TAd_hiddenFormat, nameValue);
nameView.setText(nameValue);
}
@ -389,7 +390,7 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
setVisibility(dueDateView);
} else if(task.isCompleted()) {
String dateValue = formatDate(task.getValue(Task.COMPLETION_DATE));
dueDateView.setText(r.getString(R.string.TAd_completed, dateValue));
dueDateView.setText(resources.getString(R.string.TAd_completed, dateValue));
dueDateView.setTextAppearance(fragment.getActivity(), R.style.TextAppearance_TAd_ItemDueDate_Completed);
dueDateTextWidth = paint.measureText(dateValue);
setVisibility(dueDateView);
@ -802,8 +803,6 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
new HashMap<Integer, Drawable>(3);
@SuppressWarnings("nls")
public Drawable getDrawable(String source) {
Resources r = fragment.getResources();
if(source.equals("silk_clock"))
source = "details_alarm";
else if(source.equals("silk_tag_pink"))
@ -813,12 +812,12 @@ public class TaskAdapter extends CursorAdapter implements Filterable {
else if(source.equals("silk_note"))
source = "details_note";
int drawable = r.getIdentifier("drawable/" + source, null, Constants.PACKAGE);
int drawable = resources.getIdentifier("drawable/" + source, null, Constants.PACKAGE);
if(drawable == 0)
return null;
Drawable d;
if(!cache.containsKey(drawable)) {
d = r.getDrawable(drawable);
d = resources.getDrawable(drawable);
d.setBounds(0,0,d.getIntrinsicWidth(),d.getIntrinsicHeight());
cache.put(drawable, d);
} else

@ -37,7 +37,7 @@ public class Database extends AbstractDatabase {
* Database version number. This variable must be updated when database
* tables are updated, as it determines whether a database needs updating.
*/
public static final int VERSION = 20;
public static final int VERSION = 21;
/**
* Database name (must be unique)
@ -130,19 +130,26 @@ public class Database extends AbstractDatabase {
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS up_tid ON ").
sql.append("CREATE INDEX IF NOT EXISTS up_tkid ON ").
append(Update.TABLE).append('(').
append(Update.TASK_LOCAL.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE INDEX IF NOT EXISTS up_pid ON ").
sql.append("CREATE INDEX IF NOT EXISTS up_tgl ON ").
append(Update.TABLE).append('(').
append(Update.TAGS_LOCAL.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
sql.append("CREATE UNIQUE INDEX IF NOT EXISTS t_rid ON ").
append(Task.TABLE).append('(').
append(Task.REMOTE_ID.name).
append(')');
database.execSQL(sql.toString());
sql.setLength(0);
}
@Override
@ -263,6 +270,26 @@ public class Database extends AbstractDatabase {
database.execSQL("CREATE INDEX IF NOT EXISTS up_tid ON " +
Update.TABLE + "(" + Update.TAGS_LOCAL.name + ")");
} catch (SQLiteException e) {
Log.e("astrid", "db-upgrade-" + oldVersion + "-" + newVersion, e);
}
case 20: try {
String tasks = Task.TABLE.name;
String id = Task.ID.name;
String remoteId = Task.REMOTE_ID.name;
// Delete any items that have duplicate remote ids
String deleteDuplicates = String.format("DELETE FROM %s WHERE %s IN (SELECT %s.%s FROM %s, %s AS t2 WHERE %s.%s < t2.%s AND %s.%s = t2.%s AND %s.%s > 0 GROUP BY %s.%s)",
tasks, id, tasks, id, tasks, tasks, tasks, id, id, tasks, remoteId, remoteId, tasks, remoteId, tasks, id);
// Change all items with remote id = 0 to be remote id = NULL
String changeZeroes = String.format("UPDATE %s SET %s = NULL WHERE %s = 0", tasks, remoteId, remoteId);
database.execSQL(deleteDuplicates);
database.execSQL(changeZeroes);
onCreateTables();
} catch (SQLiteException e) {
Log.e("astrid", "db-upgrade-" + oldVersion + "-" + newVersion, e);
}

@ -6,13 +6,16 @@
package com.todoroo.astrid.dao;
import android.content.ContentValues;
import android.database.sqlite.SQLiteConstraintException;
import com.timsu.astrid.R;
import com.todoroo.andlib.data.DatabaseDao;
import com.todoroo.andlib.data.TodorooCursor;
import com.todoroo.andlib.service.Autowired;
import com.todoroo.andlib.service.DependencyInjectionService;
import com.todoroo.andlib.sql.Criterion;
import com.todoroo.andlib.sql.Functions;
import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.andlib.utility.Preferences;
import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria;
@ -166,7 +169,17 @@ public class TaskDao extends DatabaseDao<Task> {
public boolean save(Task task) {
boolean saveSuccessful;
if (task.getId() == Task.NO_ID) {
saveSuccessful = createNew(task);
try {
saveSuccessful = createNew(task);
} catch (SQLiteConstraintException e) { // Tried to create task with remote id that already exists
saveSuccessful = false;
TodorooCursor<Task> cursor = query(Query.select(Task.ID).where(Task.REMOTE_ID.eq(task.getValue(Task.REMOTE_ID))));
if (cursor.getCount() > 0) {
cursor.moveToFirst();
task.setId(cursor.get(Task.ID));
saveSuccessful = saveExisting(task);
}
}
} else {
saveSuccessful = saveExisting(task);
}

@ -144,4 +144,4 @@ abstract public class TaskAdapterAddOnManager<TYPE> {
return cache.get(taskId).values();
}
}
}

@ -79,4 +79,4 @@ public abstract class TaskEditControlSet {
* Called when views need to be inflated
*/
protected abstract void afterInflate();
}
}

@ -52,4 +52,4 @@ public class TaskListContextMenuExtensionLoader {
}
}
}

@ -7,4 +7,4 @@ abstract public class LegacyAlertModel {
public static final String TASK = "task";
public static final String DATE = "date";
}
}

@ -60,4 +60,4 @@ public class LegacyRepeatInfo {
return rrule;
}
}
}

@ -13,7 +13,6 @@ import com.todoroo.astrid.dao.TagDataDao;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.dao.UpdateDao;
import com.todoroo.astrid.data.TagData;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.data.Update;
/**
@ -141,10 +140,10 @@ public class TagDataService {
return updateDao.query(Query.select(Update.PROPERTIES).where(
criterion).
orderBy(Order.desc(Update.CREATION_DATE)));
if(tagData.getValue(Task.REMOTE_ID) < 1)
if(tagData.getValue(TagData.REMOTE_ID) == 0)
return updateDao.query(Query.select(Update.PROPERTIES).where(Update.TAGS_LOCAL.like("%," + tagData.getId() + ",%")));
return updateDao.query(Query.select(Update.PROPERTIES).where(Criterion.and(criterion,
Criterion.or(Update.TAGS.like("%," + tagData.getValue(Task.REMOTE_ID) + ",%"),
Criterion.or(Update.TAGS.like("%," + tagData.getValue(TagData.REMOTE_ID) + ",%"),
Update.TAGS_LOCAL.like("%," + tagData.getId() + ",%")))).
orderBy(Order.desc(Update.CREATION_DATE)));
}
@ -155,12 +154,12 @@ public class TagDataService {
* @return
*/
public Update getLatestUpdate(TagData tagData) {
if(tagData.getValue(Task.REMOTE_ID) < 1)
if(tagData.getValue(TagData.REMOTE_ID) == 0)
return null;
@SuppressWarnings("nls")
TodorooCursor<Update> updates = updateDao.query(Query.select(Update.PROPERTIES).where(
Update.TAGS.like("%," + tagData.getValue(Task.REMOTE_ID) + ",%")).
Update.TAGS.like("%," + tagData.getValue(TagData.REMOTE_ID) + ",%")).
orderBy(Order.desc(Update.CREATION_DATE)).limit(1));
try {
if(updates.getCount() == 0)

@ -62,4 +62,4 @@ public class CalendarDialog extends Dialog implements OnClickListener, OnSelecte
public void setCalendarDate(Date calendarDate) {
this.calendarDate = calendarDate;
}
}
}

@ -189,4 +189,4 @@ public class ContactListAdapter extends CursorAdapter {
}
}
}
}
}

@ -69,4 +69,4 @@ public class EditTextControlSet extends TaskEditControlSet {
return null;
}
}
}

@ -40,4 +40,4 @@ public class ErrorCatchingEditText extends EditText {
}
}
}
}

@ -258,4 +258,4 @@ public class HideUntilControlSet extends PopupControlSet implements OnItemSelect
return null;
}
}
}

@ -44,4 +44,4 @@ public class NestedListView extends ListView {
setMeasuredDimension(getMeasuredWidth(), newHeight);
}
}
}

@ -313,12 +313,14 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
}
private void validateCurrentView(CharSequence str, boolean notifyChange) {
int val = getSelectedPos(str.toString());
if ((val >= mStart) && (val <= mEnd)) {
mPrevious = mCurrent;
mCurrent = val;
if (notifyChange)
notifyChange(mCurrent);
if (!TextUtils.isEmpty(str)) {
int val = getSelectedPos(str.toString());
if ((val >= mStart) && (val <= mEnd)) {
mPrevious = mCurrent;
mCurrent = val;
if (notifyChange)
notifyChange(mCurrent);
}
}
updateView();
}
@ -489,4 +491,4 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
validateCurrentView(str, true);
return mCurrent;
}
}
}

@ -86,4 +86,4 @@ public class NumberPickerButton extends ImageButton {
mNumberPicker.cancelDecrement();
}
}
}
}

@ -185,7 +185,7 @@ public class PeopleContainer extends LinearLayout {
if(text.length() == 0)
continue;
if(text.indexOf('@') == -1)
if(text.indexOf('@') == -1 && textView.isEnabled())
throw new ParseSharedException(textView,
activity.getString(R.string.actfm_EPA_invalid_email, text));
if (peopleAsJSON) {

@ -95,4 +95,4 @@ public class RandomReminderControlSet extends TaskEditControlSet {
task.setValue(Task.REMINDER_PERIOD, 0L);
return null;
}
}
}

@ -127,4 +127,4 @@ public class ReminderControlSet extends PopupControlSet {
protected void refreshDisplayView() {
// Nothing to do here
}
}
}

@ -122,4 +122,4 @@ public class TimeDurationControlSet implements OnNNumberPickedListener,
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save