diff --git a/app/schemas/com.todoroo.astrid.dao.Database/48.json b/app/schemas/com.todoroo.astrid.dao.Database/48.json new file mode 100644 index 000000000..b2774718b --- /dev/null +++ b/app/schemas/com.todoroo.astrid.dao.Database/48.json @@ -0,0 +1,679 @@ +{ + "formatVersion": 1, + "database": { + "version": 48, + "identityHash": "a102567219ce1411936177294f59a581", + "entities": [ + { + "tableName": "notification", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `type` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "taskId", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "uid" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_notification_task", + "unique": true, + "columnNames": [ + "task" + ], + "createSql": "CREATE UNIQUE INDEX `index_notification_task` ON `${TABLE_NAME}` (`task`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "tagdata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `name` TEXT, `color` INTEGER, `tagOrdering` TEXT, `deleted` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "color", + "columnName": "color", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "tagOrdering", + "columnName": "tagOrdering", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "userActivity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `action` TEXT, `message` TEXT, `picture` TEXT, `target_id` TEXT, `created_at` INTEGER, `deleted_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "action", + "columnName": "action", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "picture", + "columnName": "picture", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetId", + "columnName": "target_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created_at", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "deleted", + "columnName": "deleted_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "task_attachments", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `task_id` TEXT, `name` TEXT, `path` TEXT, `content_type` TEXT, `deleted_at` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "taskId", + "columnName": "task_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "contentType", + "columnName": "content_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deleted", + "columnName": "deleted_at", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "task_list_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `tag_uuid` TEXT, `filter` TEXT, `task_ids` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tagUuid", + "columnName": "tag_uuid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filter", + "columnName": "filter", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "taskIds", + "columnName": "task_ids", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "store", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `type` TEXT, `item` TEXT, `value` TEXT, `value2` TEXT, `value3` TEXT, `value4` TEXT, `deleted` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "item", + "columnName": "item", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value2", + "columnName": "value2", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value3", + "columnName": "value3", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value4", + "columnName": "value4", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "so_id", + "unique": false, + "columnNames": [ + "type", + "item" + ], + "createSql": "CREATE INDEX `so_id` ON `${TABLE_NAME}` (`type`, `item`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "tasks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `importance` INTEGER, `dueDate` INTEGER, `hideUntil` INTEGER, `created` INTEGER, `modified` INTEGER, `completed` INTEGER, `deleted` INTEGER, `notes` TEXT, `estimatedSeconds` INTEGER, `elapsedSeconds` INTEGER, `timerStart` INTEGER, `notificationFlags` INTEGER, `notifications` INTEGER, `lastNotified` INTEGER, `snoozeTime` INTEGER, `recurrence` TEXT, `repeatUntil` INTEGER, `calendarUri` TEXT, `remoteId` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "importance", + "columnName": "importance", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "dueDate", + "columnName": "dueDate", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hideUntil", + "columnName": "hideUntil", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "modified", + "columnName": "modified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "completed", + "columnName": "completed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notes", + "columnName": "notes", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "estimatedSeconds", + "columnName": "estimatedSeconds", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "elapsedSeconds", + "columnName": "elapsedSeconds", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "timerStart", + "columnName": "timerStart", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notificationFlags", + "columnName": "notificationFlags", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notifications", + "columnName": "notifications", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "lastNotified", + "columnName": "lastNotified", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "snoozeTime", + "columnName": "snoozeTime", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "recurrence", + "columnName": "recurrence", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "repeatUntil", + "columnName": "repeatUntil", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "calendarUri", + "columnName": "calendarUri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "t_rid", + "unique": true, + "columnNames": [ + "remoteId" + ], + "createSql": "CREATE UNIQUE INDEX `t_rid` ON `${TABLE_NAME}` (`remoteId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `task` INTEGER, `key` TEXT, `value` TEXT, `value2` TEXT, `value3` TEXT, `value4` TEXT, `value5` TEXT, `value6` TEXT, `value7` TEXT, `created` INTEGER, `deleted` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value1", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value2", + "columnName": "value2", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value3", + "columnName": "value3", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value4", + "columnName": "value4", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value5", + "columnName": "value5", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value6", + "columnName": "value6", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "value7", + "columnName": "value7", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "created", + "columnName": "created", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "deleted", + "columnName": "deleted", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "md_tid", + "unique": false, + "columnNames": [ + "task" + ], + "createSql": "CREATE INDEX `md_tid` ON `${TABLE_NAME}` (`task`)" + }, + { + "name": "md_tkid", + "unique": false, + "columnNames": [ + "task", + "key" + ], + "createSql": "CREATE INDEX `md_tkid` ON `${TABLE_NAME}` (`task`, `key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "alarms", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `time` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "locations", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `radius` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "task", + "columnName": "task", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "latitude", + "columnName": "latitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "longitude", + "columnName": "longitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "radius", + "columnName": "radius", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "_id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"a102567219ce1411936177294f59a581\")" + ] + } +} \ No newline at end of file diff --git a/app/src/googleplay/java/org/tasks/location/GeofenceApi.java b/app/src/googleplay/java/org/tasks/location/GeofenceApi.java index 2b5483260..1d40b7046 100644 --- a/app/src/googleplay/java/org/tasks/location/GeofenceApi.java +++ b/app/src/googleplay/java/org/tasks/location/GeofenceApi.java @@ -9,8 +9,10 @@ import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.Status; import com.google.android.gms.location.GeofencingRequest; import com.google.android.gms.location.LocationServices; +import com.google.common.collect.Lists; import org.tasks.R; +import org.tasks.data.Location; import org.tasks.injection.ForApplication; import org.tasks.preferences.PermissionChecker; import org.tasks.preferences.Preferences; @@ -40,8 +42,8 @@ public class GeofenceApi { this.permissionChecker = permissionChecker; } - public void register(final List geofences) { - if (geofences.isEmpty() || !permissionChecker.canAccessLocation()) { + public void register(final List locations) { + if (locations.isEmpty() || !permissionChecker.canAccessLocation()) { return; } @@ -50,13 +52,13 @@ public class GeofenceApi { @SuppressLint("MissingPermission") PendingResult result = LocationServices.GeofencingApi.addGeofences( client, - getRequests(geofences), + getRequests(locations), PendingIntent.getBroadcast(context, 0, new Intent(context, GeofenceTransitionsIntentService.Broadcast.class), PendingIntent.FLAG_UPDATE_CURRENT)); result.setResultCallback(status -> { if (status.isSuccess()) { - Timber.i("Registered %s", geofences); + Timber.i("Registered %s", locations); } else { - Timber.e("Failed to register %s", geofences); + Timber.e("Failed to register %s", locations); } client.disconnect(); @@ -64,23 +66,23 @@ public class GeofenceApi { }); } - public void cancel(final Geofence geofence) { - cancel(singletonList(geofence)); + public void cancel(final Location location) { + cancel(singletonList(location)); } - public void cancel(final List geofences) { - if (geofences.isEmpty() || !permissionChecker.canAccessLocation()) { + public void cancel(final List locations) { + if (locations.isEmpty() || !permissionChecker.canAccessLocation()) { return; } - final List ids = newArrayList(transform(geofences, geofence -> Long.toString(geofence.getMetadataId()))); + List ids = Lists.transform(locations, geofence -> Long.toString(geofence.getId())); newClient(client -> LocationServices.GeofencingApi.removeGeofences(client, ids) .setResultCallback(status -> { if (status.isSuccess()) { - Timber.i("Removed %s", geofences); + Timber.i("Removed %s", locations); } else { - Timber.e("Failed to remove %s", geofences); + Timber.e("Failed to remove %s", locations); } client.disconnect(); @@ -91,17 +93,17 @@ public class GeofenceApi { new GoogleApi(context).connect(handler); } - private List getRequests(List geofences) { - return newArrayList(transform(geofences, this::toGoogleGeofence)); + private List getRequests(List locations) { + return newArrayList(transform(locations, this::toGoogleGeofence)); } - private com.google.android.gms.location.Geofence toGoogleGeofence(Geofence geofence) { + private com.google.android.gms.location.Geofence toGoogleGeofence(Location location) { int radius = preferences.getIntegerFromString(R.string.p_geofence_radius, 250); int responsiveness = (int) TimeUnit.SECONDS.toMillis(preferences.getIntegerFromString(R.string.p_geofence_responsiveness, 60)); return new com.google.android.gms.location.Geofence.Builder() - .setCircularRegion(geofence.getLatitude(), geofence.getLongitude(), radius) + .setCircularRegion(location.getLatitude(), location.getLongitude(), radius) .setNotificationResponsiveness(responsiveness) - .setRequestId(Long.toString(geofence.getMetadataId())) + .setRequestId(Long.toString(location.getId())) .setTransitionTypes(GeofencingRequest.INITIAL_TRIGGER_ENTER) .setExpirationDuration(NEVER_EXPIRE) .build(); diff --git a/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java b/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java index 7542ed2df..152461b4c 100644 --- a/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java +++ b/app/src/googleplay/java/org/tasks/location/GeofenceTransitionsIntentService.java @@ -6,11 +6,11 @@ import android.content.Intent; import android.support.v4.app.JobIntentService; import com.google.android.gms.location.GeofencingEvent; -import com.todoroo.astrid.dao.MetadataDao; -import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.reminders.ReminderService; import org.tasks.Notifier; +import org.tasks.data.Location; +import org.tasks.data.LocationDao; import org.tasks.injection.InjectingJobIntentService; import org.tasks.injection.IntentServiceComponent; import org.tasks.jobs.JobManager; @@ -30,7 +30,7 @@ public class GeofenceTransitionsIntentService extends InjectingJobIntentService } } - @Inject MetadataDao metadataDao; + @Inject LocationDao locationDao; @Inject Notifier notifier; @Override @@ -64,9 +64,8 @@ public class GeofenceTransitionsIntentService extends InjectingJobIntentService private void triggerNotification(com.google.android.gms.location.Geofence triggeringGeofence) { String requestId = triggeringGeofence.getRequestId(); try { - Metadata fetch = metadataDao.fetch(Long.parseLong(requestId)); - Geofence geofence = new Geofence(fetch); - notifier.triggerTaskNotification(geofence.getTaskId(), ReminderService.TYPE_ALARM); + Location location = locationDao.getGeofence(Long.parseLong(requestId)); + notifier.triggerTaskNotification(location.getTask(), ReminderService.TYPE_ALARM); } catch(Exception e) { Timber.e(e, "Error triggering geofence %s: %s", requestId, e.getMessage()); } diff --git a/app/src/googleplay/java/org/tasks/location/PlacePicker.java b/app/src/googleplay/java/org/tasks/location/PlacePicker.java index e7ff3f44a..d45fabe57 100644 --- a/app/src/googleplay/java/org/tasks/location/PlacePicker.java +++ b/app/src/googleplay/java/org/tasks/location/PlacePicker.java @@ -11,6 +11,7 @@ import com.google.android.gms.location.places.Place; import com.google.android.gms.maps.model.LatLng; import org.tasks.R; +import org.tasks.data.Location; import org.tasks.preferences.Preferences; import timber.log.Timber; @@ -31,11 +32,15 @@ public class PlacePicker { return null; } - public static Geofence getPlace(Context context, Intent data, Preferences preferences) { + public static Location getPlace(Context context, Intent data, Preferences preferences) { Place place = com.google.android.gms.location.places.ui.PlacePicker.getPlace(context, data); LatLng latLng = place.getLatLng(); - Geofence geofence = new Geofence(place.getName().toString(), latLng.latitude, latLng.longitude, preferences.getIntegerFromString(R.string.p_geofence_radius, 250)); - Timber.i("Picked %s", geofence); - return geofence; + Location location = new Location(); + location.setName(place.getName().toString()); + location.setLatitude(latLng.latitude); + location.setLongitude(latLng.longitude); + location.setRadius(preferences.getIntegerFromString(R.string.p_geofence_radius, 250)); + Timber.i("Picked %s", location); + return location; } } diff --git a/app/src/main/java/com/todoroo/astrid/backup/BackupConstants.java b/app/src/main/java/com/todoroo/astrid/backup/BackupConstants.java index f933bce8c..62a35d602 100755 --- a/app/src/main/java/com/todoroo/astrid/backup/BackupConstants.java +++ b/app/src/main/java/com/todoroo/astrid/backup/BackupConstants.java @@ -39,6 +39,8 @@ class BackupConstants { public static final String ALARM_TAG = "alarm"; + public static final String LOCATION_TAG = "location"; + /** Tag containing a tagdata item */ public static final String TAGDATA_TAG = "tagdata"; diff --git a/app/src/main/java/com/todoroo/astrid/backup/TasksXmlExporter.java b/app/src/main/java/com/todoroo/astrid/backup/TasksXmlExporter.java index 873cc5925..3d856d847 100755 --- a/app/src/main/java/com/todoroo/astrid/backup/TasksXmlExporter.java +++ b/app/src/main/java/com/todoroo/astrid/backup/TasksXmlExporter.java @@ -33,6 +33,8 @@ import org.tasks.R; import org.tasks.backup.XmlWriter; import org.tasks.data.Alarm; import org.tasks.data.AlarmDao; +import org.tasks.data.Location; +import org.tasks.data.LocationDao; import org.tasks.preferences.Preferences; import org.xmlpull.v1.XmlSerializer; @@ -64,6 +66,7 @@ public class TasksXmlExporter { private final TagDataDao tagDataDao; private final MetadataDao metadataDao; private final AlarmDao alarmDao; + private final LocationDao locationDao; private final TaskDao taskDao; private UserActivityDao userActivityDao; private final Preferences preferences; @@ -94,13 +97,14 @@ public class TasksXmlExporter { @Inject public TasksXmlExporter(TagDataDao tagDataDao, MetadataDao metadataDao, TaskDao taskDao, UserActivityDao userActivityDao, - Preferences preferences, AlarmDao alarmDao) { + Preferences preferences, AlarmDao alarmDao, LocationDao locationDao) { this.tagDataDao = tagDataDao; this.metadataDao = metadataDao; this.taskDao = taskDao; this.userActivityDao = userActivityDao; this.preferences = preferences; this.alarmDao = alarmDao; + this.locationDao = locationDao; } public void exportTasks(final Context context, final ExportType exportType, @Nullable final ProgressDialog progressDialog) { @@ -188,7 +192,6 @@ public class TasksXmlExporter { xml.startTag(null, BackupConstants.TASK_TAG); serializeModel(task, Task.PROPERTIES, Task.ID); serializeMetadata(task); - serializeAlarms(task); serializeComments(task); xml.endTag(null, BackupConstants.TASK_TAG); this.exportCount++; @@ -221,9 +224,6 @@ public class TasksXmlExporter { throw new RuntimeException(e); } } - } - - private synchronized void serializeAlarms(Task task) { for (Alarm alarm : alarmDao.getAlarms(task.getId())) { try { xml.startTag(null, BackupConstants.ALARM_TAG); @@ -233,6 +233,15 @@ public class TasksXmlExporter { throw new RuntimeException(e); } } + for (Location location : locationDao.getGeofences(task.getId())) { + try { + xml.startTag(null, BackupConstants.LOCATION_TAG); + location.writeToXml(new XmlWriter(xml)); + xml.endTag(null, BackupConstants.LOCATION_TAG); + } catch (IOException e) { + throw new RuntimeException(e); + } + } } /** diff --git a/app/src/main/java/com/todoroo/astrid/backup/TasksXmlImporter.java b/app/src/main/java/com/todoroo/astrid/backup/TasksXmlImporter.java index f3acf0367..8dea28ceb 100755 --- a/app/src/main/java/com/todoroo/astrid/backup/TasksXmlImporter.java +++ b/app/src/main/java/com/todoroo/astrid/backup/TasksXmlImporter.java @@ -34,6 +34,8 @@ import org.tasks.R; import org.tasks.backup.XmlReader; import org.tasks.data.Alarm; import org.tasks.data.AlarmDao; +import org.tasks.data.LocationDao; +import org.tasks.data.Location; import org.tasks.dialogs.DialogBuilder; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -56,6 +58,7 @@ public class TasksXmlImporter { private final TaskDao taskDao; private LocalBroadcastManager localBroadcastManager; private final AlarmDao alarmDao; + private final LocationDao locationDao; private Activity activity; private Handler handler; @@ -72,13 +75,14 @@ public class TasksXmlImporter { @Inject public TasksXmlImporter(TagDataDao tagDataDao, MetadataDao metadataDao, UserActivityDao userActivityDao, - DialogBuilder dialogBuilder, TaskDao taskDao, + DialogBuilder dialogBuilder, TaskDao taskDao, LocationDao locationDao, LocalBroadcastManager localBroadcastManager, AlarmDao alarmDao) { this.tagDataDao = tagDataDao; this.metadataDao = metadataDao; this.userActivityDao = userActivityDao; this.dialogBuilder = dialogBuilder; this.taskDao = taskDao; + this.locationDao = locationDao; this.localBroadcastManager = localBroadcastManager; this.alarmDao = alarmDao; } @@ -245,6 +249,16 @@ public class TasksXmlImporter { alarmDao.insert(alarm); } + void parseLocation() { + if (!currentTask.isSaved()) { + return; + } + + Location location = new Location(new XmlReader(xpp)); + + locationDao.insert(location); + } + void parseMetadata(int format) { if(!currentTask.isSaved()) { return; @@ -381,6 +395,9 @@ public class TasksXmlImporter { case BackupConstants.ALARM_TAG: parseAlarm(); break; + case BackupConstants.LOCATION_TAG: + parseLocation(); + break; } } catch (Exception e) { errorCount++; diff --git a/app/src/main/java/com/todoroo/astrid/dao/Database.java b/app/src/main/java/com/todoroo/astrid/dao/Database.java index 70668594d..4389a60d3 100644 --- a/app/src/main/java/com/todoroo/astrid/dao/Database.java +++ b/app/src/main/java/com/todoroo/astrid/dao/Database.java @@ -25,6 +25,8 @@ import com.todoroo.astrid.data.UserActivity; import org.tasks.data.Alarm; import org.tasks.data.AlarmDao; +import org.tasks.data.Location; +import org.tasks.data.LocationDao; import org.tasks.notifications.Notification; import org.tasks.notifications.NotificationDao; @@ -48,9 +50,10 @@ import timber.log.Timber; StoreObject.class, Task.class, Metadata.class, - Alarm.class + Alarm.class, + Location.class }, - version = 47) + version = 48) public abstract class Database extends RoomDatabase { public abstract NotificationDao notificationDao(); @@ -60,6 +63,7 @@ public abstract class Database extends RoomDatabase { public abstract TaskListMetadataDao getTaskListMetadataDao(); public abstract StoreObjectDao getStoreObjectDao(); public abstract AlarmDao getAlarmDao(); + public abstract LocationDao getGeofenceDao(); public static final String NAME = "database"; diff --git a/app/src/main/java/com/todoroo/astrid/service/SynchronizeMetadataCallback.java b/app/src/main/java/com/todoroo/astrid/service/SynchronizeMetadataCallback.java deleted file mode 100644 index 74a10387b..000000000 --- a/app/src/main/java/com/todoroo/astrid/service/SynchronizeMetadataCallback.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.todoroo.astrid.service; - -import com.todoroo.astrid.data.Metadata; - -public interface SynchronizeMetadataCallback { - void beforeDeleteMetadata(Metadata m); -} diff --git a/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.java b/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.java index 7b2edcf67..ab657f1da 100644 --- a/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.java +++ b/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.java @@ -24,16 +24,15 @@ import android.widget.TextView; import com.google.common.primitives.Longs; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.astrid.alarms.AlarmService; -import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Task; import org.tasks.R; import org.tasks.activities.DateAndTimePickerActivity; import org.tasks.activities.TimePickerActivity; import org.tasks.data.Alarm; +import org.tasks.data.Location; import org.tasks.injection.ForActivity; import org.tasks.injection.FragmentComponent; -import org.tasks.location.Geofence; import org.tasks.location.GeofenceService; import org.tasks.location.PlacePicker; import org.tasks.preferences.Device; @@ -102,7 +101,7 @@ public class ReminderControlSet extends TaskEditControlFragment { private final List spinnerOptions = new ArrayList<>(); private ArrayAdapter remindAdapter; private final Set alarms = new LinkedHashSet<>(); - private final Set geofences = new LinkedHashSet<>(); + private final Set locations = new LinkedHashSet<>(); @OnItemSelected(R.id.reminder_alarm) void ringModeSelected(int position) { @@ -124,12 +123,12 @@ public class ReminderControlSet extends TaskEditControlFragment { taskId = savedInstanceState.getLong(EXTRA_TASK_ID); flags = savedInstanceState.getInt(EXTRA_FLAGS); randomReminder = savedInstanceState.getLong(EXTRA_RANDOM_REMINDER); - List geofences = new ArrayList<>(); + List locations = new ArrayList<>(); List geofenceArray = savedInstanceState.getParcelableArrayList(EXTRA_GEOFENCES); for (Parcelable geofence : geofenceArray) { - geofences.add((Geofence) geofence); + locations.add((Location) geofence); } - setup(Longs.asList(savedInstanceState.getLongArray(EXTRA_ALARMS)), geofences); + setup(Longs.asList(savedInstanceState.getLongArray(EXTRA_ALARMS)), locations); } else { setup(currentAlarms(), geofenceService.getGeofences(taskId)); } @@ -199,7 +198,7 @@ public class ReminderControlSet extends TaskEditControlFragment { randomReminder = task.getReminderPeriod(); } - private void setup(List alarms, List geofences) { + private void setup(List alarms, List locations) { setValue(flags); alertContainer.removeAllViews(); @@ -215,8 +214,8 @@ public class ReminderControlSet extends TaskEditControlFragment { for (long timestamp : alarms) { addAlarmRow(timestamp); } - for (Geofence geofence : geofences) { - addGeolocationReminder(geofence); + for (Location location : locations) { + addGeolocationReminder(location); } updateSpinner(); } @@ -226,7 +225,7 @@ public class ReminderControlSet extends TaskEditControlFragment { return getFlags() != original.getReminderFlags() || getRandomReminderPeriod() != original.getReminderPeriod() || !newHashSet(currentAlarms()).equals(alarms) || - !newHashSet(geofenceService.getGeofences(taskId)).equals(geofences); + !newHashSet(geofenceService.getGeofences(taskId)).equals(locations); } @Override @@ -238,7 +237,7 @@ public class ReminderControlSet extends TaskEditControlFragment { if(alarmService.synchronizeAlarms(task.getId(), alarms)) { task.setModificationDate(DateUtilities.now()); } - if (geofenceService.synchronizeGeofences(task.getId(), geofences)) { + if (geofenceService.synchronizeGeofences(task.getId(), locations)) { task.setModificationDate(DateUtilities.now()); } } @@ -251,7 +250,7 @@ public class ReminderControlSet extends TaskEditControlFragment { outState.putInt(EXTRA_FLAGS, getFlags()); outState.putLong(EXTRA_RANDOM_REMINDER, getRandomReminderPeriod()); outState.putLongArray(EXTRA_ALARMS, Longs.toArray(alarms)); - outState.putParcelableArrayList(EXTRA_GEOFENCES, newArrayList(geofences)); + outState.putParcelableArrayList(EXTRA_GEOFENCES, newArrayList(locations)); } @Override @@ -292,9 +291,9 @@ public class ReminderControlSet extends TaskEditControlFragment { } } - private void addGeolocationReminder(final Geofence geofence) { - addAlarmRow(geofence.getName(), v -> geofences.remove(geofence)); - geofences.add(geofence); + private void addGeolocationReminder(final Location location) { + addAlarmRow(location.getName(), v -> locations.remove(location)); + locations.add(location); } private int getFlags() { diff --git a/app/src/main/java/org/tasks/backup/XmlReader.java b/app/src/main/java/org/tasks/backup/XmlReader.java index 9ad12f167..bdfd04dc1 100644 --- a/app/src/main/java/org/tasks/backup/XmlReader.java +++ b/app/src/main/java/org/tasks/backup/XmlReader.java @@ -38,4 +38,12 @@ public class XmlReader { writer.write(value); } } + + public void readDouble(String name, ValueWriter writer) { + String value = xpp.getAttributeValue(null, name); + if (value != null) { + writer.write(TasksXmlExporter.XML_NULL.equals(value) ? + null : Double.parseDouble(value)); + } + } } diff --git a/app/src/main/java/org/tasks/backup/XmlWriter.java b/app/src/main/java/org/tasks/backup/XmlWriter.java index 2f7e52d8c..00356c20a 100644 --- a/app/src/main/java/org/tasks/backup/XmlWriter.java +++ b/app/src/main/java/org/tasks/backup/XmlWriter.java @@ -51,4 +51,16 @@ public class XmlWriter { throw new RuntimeException(e); } } + + public void writeDouble(String name, Double value) { + try { + String valueString = (value == null) ? XML_NULL : value.toString(); + xml.attribute(null, name, valueString); + } catch (UnsupportedOperationException e) { + // didn't read this value, do nothing + Timber.e(e, e.getMessage()); + } catch (IllegalArgumentException | IllegalStateException | IOException e) { + throw new RuntimeException(e); + } + } } diff --git a/app/src/main/java/org/tasks/data/Location.java b/app/src/main/java/org/tasks/data/Location.java new file mode 100644 index 000000000..51c0b3582 --- /dev/null +++ b/app/src/main/java/org/tasks/data/Location.java @@ -0,0 +1,181 @@ +package org.tasks.data; + +import android.arch.persistence.room.ColumnInfo; +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.Ignore; +import android.arch.persistence.room.PrimaryKey; +import android.os.Parcel; +import android.os.Parcelable; + +import org.tasks.backup.XmlReader; +import org.tasks.backup.XmlWriter; + +import java.io.Serializable; + +@Entity(tableName = "locations") +public class Location implements Serializable, Parcelable { + @PrimaryKey(autoGenerate = true) + @ColumnInfo(name = "_id") + private long id; + + @ColumnInfo(name = "task") + private long task; + + @ColumnInfo(name = "name") + private String name; + + @ColumnInfo(name = "latitude") + private double latitude; + + @ColumnInfo(name = "longitude") + private double longitude; + + @ColumnInfo(name = "radius") + private int radius; + + public Location() { + + } + + @Ignore + public Location(Parcel parcel) { + id = parcel.readLong(); + task = parcel.readLong(); + name = parcel.readString(); + latitude = parcel.readDouble(); + longitude = parcel.readDouble(); + radius = parcel.readInt(); + } + + @Ignore + public Location(XmlReader xml) { + xml.readLong("task", this::setTask); + xml.readString("name", this::setName); + xml.readDouble("latitude", this::setLatitude); + xml.readDouble("longitude", this::setLongitude); + xml.readInteger("radius", this::setRadius); + } + + public void writeToXml(XmlWriter xmlWriter) { + xmlWriter.writeLong("task", task); + xmlWriter.writeString("name", name); + xmlWriter.writeDouble("latitude", latitude); + xmlWriter.writeDouble("longitude", longitude); + xmlWriter.writeInteger("radius", radius); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public long getTask() { + return task; + } + + public void setTask(long task) { + this.task = task; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude(double latitude) { + this.latitude = latitude; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude(double longitude) { + this.longitude = longitude; + } + + public int getRadius() { + return radius; + } + + public void setRadius(int radius) { + this.radius = radius; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Location location = (Location) o; + + if (id != location.id) return false; + if (task != location.task) return false; + if (Double.compare(location.latitude, latitude) != 0) return false; + if (Double.compare(location.longitude, longitude) != 0) return false; + if (radius != location.radius) return false; + return name != null ? name.equals(location.name) : location.name == null; + } + + @Override + public int hashCode() { + int result; + long temp; + result = (int) (id ^ (id >>> 32)); + result = 31 * result + (int) (task ^ (task >>> 32)); + result = 31 * result + (name != null ? name.hashCode() : 0); + temp = Double.doubleToLongBits(latitude); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(longitude); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + radius; + return result; + } + + @Override + public String toString() { + return "Location{" + + "id=" + id + + ", task=" + task + + ", name='" + name + '\'' + + ", latitude=" + latitude + + ", longitude=" + longitude + + ", radius=" + radius + + '}'; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeLong(id); + out.writeLong(task); + out.writeString(name); + out.writeDouble(latitude); + out.writeDouble(longitude); + out.writeInt(radius); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public Location createFromParcel(Parcel source) { + return new Location(source); + } + + @Override + public Location[] newArray(int size) { + return new Location[size]; + } + }; +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/LocationDao.java b/app/src/main/java/org/tasks/data/LocationDao.java new file mode 100644 index 000000000..0ed741b97 --- /dev/null +++ b/app/src/main/java/org/tasks/data/LocationDao.java @@ -0,0 +1,30 @@ +package org.tasks.data; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Delete; +import android.arch.persistence.room.Insert; +import android.arch.persistence.room.Query; + +import java.util.List; + +@Dao +public interface LocationDao { + + @Query("SELECT * FROM locations WHERE _id = :id LIMIT 1") + Location getGeofence(Long id); + + @Query("SELECT * FROM locations WHERE task = :taskId ORDER BY name ASC") + List getGeofences(long taskId); + + @Query("SELECT locations.* FROM locations LEFT JOIN tasks ON tasks._id = locations.task WHERE locations.task = :taskId AND tasks.deleted = 0 AND tasks.completed = 0") + List getActiveGeofences(long taskId); + + @Query("SELECT locations.* FROM locations LEFT JOIN tasks ON tasks._id = locations.task WHERE tasks.deleted = 0 AND tasks.completed = 0") + List getActiveGeofences(); + + @Delete + void delete(Location location); + + @Insert + void insert(Location location); +} diff --git a/app/src/main/java/org/tasks/db/Migrations.java b/app/src/main/java/org/tasks/db/Migrations.java index 99dcd0ed6..27e5dc7a7 100644 --- a/app/src/main/java/org/tasks/db/Migrations.java +++ b/app/src/main/java/org/tasks/db/Migrations.java @@ -43,6 +43,16 @@ public class Migrations { } }; + private static final Migration MIGRATION_47_48 = new Migration(47, 48) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE IF NOT EXISTS `locations` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `radius` INTEGER NOT NULL)"); + database.execSQL("INSERT INTO `locations` (`task`, `name`, `latitude`, `longitude`, `radius`) " + + "SELECT `task`, `value`, `value2`, `value3`, `value4` FROM `metadata` WHERE `key` = 'geofence'"); + database.execSQL("DELETE FROM `metadata` WHERE `key` = 'geofence'"); + } + }; + private static Migration NOOP(int from, int to) { return new Migration(from, to) { @Override @@ -64,6 +74,7 @@ public class Migrations { NOOP(43, 44), NOOP(44, 45), NOOP(45, 46), - MIGRATION_46_47 + MIGRATION_46_47, + MIGRATION_47_48 }; } diff --git a/app/src/main/java/org/tasks/injection/ApplicationModule.java b/app/src/main/java/org/tasks/injection/ApplicationModule.java index 196b5823c..66b5977ad 100644 --- a/app/src/main/java/org/tasks/injection/ApplicationModule.java +++ b/app/src/main/java/org/tasks/injection/ApplicationModule.java @@ -14,6 +14,7 @@ import com.todoroo.astrid.provider.Astrid2TaskProvider; import org.tasks.ErrorReportingSingleThreadExecutor; import org.tasks.analytics.Tracker; import org.tasks.data.AlarmDao; +import org.tasks.data.LocationDao; import org.tasks.db.Migrations; import org.tasks.locale.Locale; import org.tasks.notifications.NotificationDao; @@ -96,4 +97,9 @@ public class ApplicationModule { public AlarmDao getAlarmDao(Database database) { return database.getAlarmDao(); } + + @Provides + public LocationDao getGeofenceDao(Database database) { + return database.getGeofenceDao(); + } } diff --git a/app/src/main/java/org/tasks/location/Geofence.java b/app/src/main/java/org/tasks/location/Geofence.java deleted file mode 100644 index 24862be7c..000000000 --- a/app/src/main/java/org/tasks/location/Geofence.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.tasks.location; - -import android.os.Parcel; -import android.os.Parcelable; - -import com.todoroo.astrid.data.Metadata; - -import java.io.Serializable; - -public class Geofence implements Serializable, Parcelable { - private final String name; - private final double latitude; - private final double longitude; - private final int radius; - private long taskId; - private long metadataId; - - public Geofence(Metadata metadata) { - this(metadata.getValue(GeofenceFields.PLACE), - metadata.getValue(GeofenceFields.LATITUDE), - metadata.getValue(GeofenceFields.LONGITUDE), - metadata.getValue(GeofenceFields.RADIUS)); - metadataId = metadata.getId(); - taskId = metadata.getTask(); - } - - public Geofence(String name, double latitude, double longitude, int radius) { - this.name = name; - this.latitude = latitude; - this.longitude = longitude; - this.radius = radius; - } - - public Metadata toMetadata() { - Metadata metadata = new Metadata(); - metadata.setKey(GeofenceFields.METADATA_KEY); - metadata.setValue(GeofenceFields.PLACE, name); - metadata.setValue(GeofenceFields.LATITUDE, latitude); - metadata.setValue(GeofenceFields.LONGITUDE, longitude); - metadata.setValue(GeofenceFields.RADIUS, radius); - return metadata; - } - - public String getName() { - return name; - } - - public double getLatitude() { - return latitude; - } - - public double getLongitude() { - return longitude; - } - - public long getMetadataId() { - return metadataId; - } - - public long getTaskId() { - return taskId; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Geofence geofence = (Geofence) o; - - if (Double.compare(geofence.latitude, latitude) != 0) return false; - if (Double.compare(geofence.longitude, longitude) != 0) return false; - if (radius != geofence.radius) return false; - if (taskId != geofence.taskId) return false; - if (metadataId != geofence.metadataId) return false; - return !(name != null ? !name.equals(geofence.name) : geofence.name != null); - - } - - @Override - public int hashCode() { - int result; - long temp; - result = name != null ? name.hashCode() : 0; - temp = Double.doubleToLongBits(latitude); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(longitude); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + radius; - result = 31 * result + (int) (taskId ^ (taskId >>> 32)); - result = 31 * result + (int) (metadataId ^ (metadataId >>> 32)); - return result; - } - - @Override - public String toString() { - return "Geofence{" + - "name='" + name + '\'' + - ", latitude=" + latitude + - ", longitude=" + longitude + - ", radius=" + radius + - ", taskId=" + taskId + - ", metadataId=" + metadataId + - '}'; - } - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel out, int flags) { - out.writeString(name); - out.writeDouble(latitude); - out.writeDouble(longitude); - out.writeInt(radius); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public Geofence createFromParcel(Parcel source) { - String name = source.readString(); - double latitude = source.readDouble(); - double longitude = source.readDouble(); - int radius = source.readInt(); - return new Geofence(name, latitude, longitude, radius); - } - - @Override - public Geofence[] newArray(int size) { - return new Geofence[size]; - } - }; -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/location/GeofenceFields.java b/app/src/main/java/org/tasks/location/GeofenceFields.java deleted file mode 100644 index 97e6ba87a..000000000 --- a/app/src/main/java/org/tasks/location/GeofenceFields.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.tasks.location; - -import com.todoroo.andlib.data.Property.IntegerProperty; -import com.todoroo.astrid.data.Metadata; - -import static com.todoroo.andlib.data.Property.DoubleProperty; -import static com.todoroo.andlib.data.Property.StringProperty; - -class GeofenceFields { - - public static final String METADATA_KEY = "geofence"; - - public static final StringProperty PLACE = new StringProperty(Metadata.TABLE, - Metadata.VALUE1.name); - - public static final DoubleProperty LATITUDE = new DoubleProperty(Metadata.TABLE, - Metadata.VALUE2.name); - - public static final DoubleProperty LONGITUDE = new DoubleProperty(Metadata.TABLE, - Metadata.VALUE3.name); - - public static final IntegerProperty RADIUS = new IntegerProperty(Metadata.TABLE, - Metadata.VALUE4.name); -} diff --git a/app/src/main/java/org/tasks/location/GeofenceService.java b/app/src/main/java/org/tasks/location/GeofenceService.java index 7b618793a..be2f80ec4 100644 --- a/app/src/main/java/org/tasks/location/GeofenceService.java +++ b/app/src/main/java/org/tasks/location/GeofenceService.java @@ -1,44 +1,28 @@ package org.tasks.location; -import android.content.ContentValues; - -import com.todoroo.andlib.sql.Criterion; -import com.todoroo.andlib.sql.Join; -import com.todoroo.andlib.sql.Order; -import com.todoroo.andlib.sql.Query; -import com.todoroo.andlib.utility.DateUtilities; -import com.todoroo.astrid.dao.MetadataDao; -import com.todoroo.astrid.dao.MetadataDao.MetadataCriteria; -import com.todoroo.astrid.dao.TaskDao.TaskCriteria; -import com.todoroo.astrid.data.Metadata; -import com.todoroo.astrid.data.Task; -import com.todoroo.astrid.service.SynchronizeMetadataCallback; - -import java.util.HashSet; +import org.tasks.data.Location; +import org.tasks.data.LocationDao; + import java.util.List; -import java.util.Map; import java.util.Set; import javax.inject.Inject; -import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Lists.newArrayList; public class GeofenceService { - private final MetadataDao metadataDao; private final GeofenceApi geofenceApi; + private final LocationDao locationDao; @Inject - public GeofenceService(MetadataDao metadataDao, GeofenceApi geofenceApi) { - this.metadataDao = metadataDao; + public GeofenceService(GeofenceApi geofenceApi, LocationDao locationDao) { this.geofenceApi = geofenceApi; + this.locationDao = locationDao; } - public List getGeofences(long taskId) { - return toGeofences(metadataDao.toList(Query.select( - Metadata.PROPERTIES).where(MetadataCriteria.byTaskAndwithKey( - taskId, GeofenceFields.METADATA_KEY)).orderBy(Order.asc(GeofenceFields.PLACE)))); + public List getGeofences(long taskId) { + return locationDao.getGeofences(taskId); } public void setupGeofences() { @@ -54,87 +38,65 @@ public class GeofenceService { } public void cancelGeofences(long taskId) { - for (Geofence geofence : getGeofences(taskId)) { - geofenceApi.cancel(geofence); + for (Location location : getGeofences(taskId)) { + geofenceApi.cancel(location); } } - public boolean synchronizeGeofences(final long taskId, Set geofences) { - List metadatas = newArrayList(transform(geofences, Geofence::toMetadata)); - - boolean changed = synchronizeMetadata(taskId, metadatas, m -> geofenceApi.cancel(new Geofence(m))); + public boolean synchronizeGeofences(final long taskId, Set locations) { + boolean changed = synchronizeMetadata(taskId, newArrayList(locations), geofenceApi::cancel); if(changed) { setupGeofences(taskId); } + return changed; } - private List toGeofences(List geofences) { - return newArrayList(transform(geofences, Geofence::new)); + private List getActiveGeofences() { + return locationDao.getActiveGeofences(); } - private List getActiveGeofences() { - return toGeofences(metadataDao.toList(Query.select(Metadata.ID, Metadata.TASK, GeofenceFields.PLACE, GeofenceFields.LATITUDE, GeofenceFields.LONGITUDE, GeofenceFields.RADIUS). - join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))). - where(Criterion.and(TaskCriteria.isActive(), MetadataCriteria.withKey(GeofenceFields.METADATA_KEY))))); + private List getGeofencesForTask(long taskId) { + return locationDao.getActiveGeofences(taskId); } - private List getGeofencesForTask(long taskId) { - return toGeofences(metadataDao.toList(Query.select(Metadata.ID, Metadata.TASK, GeofenceFields.PLACE, GeofenceFields.LATITUDE, GeofenceFields.LONGITUDE, GeofenceFields.RADIUS). - join(Join.inner(Task.TABLE, Metadata.TASK.eq(Task.ID))). - where(Criterion.and(TaskCriteria.isActive(), - MetadataCriteria.byTaskAndwithKey(taskId, GeofenceFields.METADATA_KEY))))); + public interface SynchronizeGeofenceCallback { + void beforeDelete(Location location); } - private boolean synchronizeMetadata(long taskId, List metadata, final SynchronizeMetadataCallback callback) { - final boolean[] dirty = new boolean[1]; - final Set newMetadataValues = new HashSet<>(); - for(Metadata metadatum : metadata) { + private boolean synchronizeMetadata(long taskId, List locations, final SynchronizeGeofenceCallback callback) { + boolean dirty = false; + for(Location metadatum : locations) { metadatum.setTask(taskId); - metadatum.clearValue(Metadata.CREATION_DATE); - metadatum.clearValue(Metadata.ID); - - ContentValues values = metadatum.getMergedValues(); - for(Map.Entry entry : values.valueSet()) { - if(entry.getKey().startsWith("value")) //$NON-NLS-1$ - { - values.put(entry.getKey(), entry.getValue().toString()); - } - } - newMetadataValues.add(values); + metadatum.setId(0L); } - for (Metadata item : metadataDao.byTaskAndKey(taskId, GeofenceFields.METADATA_KEY)) { + for (Location item : locationDao.getGeofences(taskId)) { long id = item.getId(); // clear item id when matching with incoming values - item.clearValue(Metadata.ID); - item.clearValue(Metadata.CREATION_DATE); - ContentValues itemMergedValues = item.getMergedValues(); + item.setId(0L); - if(newMetadataValues.contains(itemMergedValues)) { - newMetadataValues.remove(itemMergedValues); + if(locations.contains(item)) { + locations.remove(item); } else { // not matched. cut it item.setId(id); if (callback != null) { - callback.beforeDeleteMetadata(item); + callback.beforeDelete(item); } - metadataDao.delete(id); - dirty[0] = true; + locationDao.delete(item); + dirty = true; } } // everything that remains shall be written - for(ContentValues values : newMetadataValues) { - Metadata item = new Metadata(); - item.setCreationDate(DateUtilities.now()); - item.mergeWith(values); - metadataDao.persist(item); - dirty[0] = true; + for(Location location : locations) { + locationDao.insert(location); + dirty = true; } - return dirty[0]; + return dirty; } }