Split locations table into geofences and places

pull/795/head
Alex Baker 7 years ago
parent de232e57b5
commit e302f3402e

@ -0,0 +1,984 @@
{
"formatVersion": 1,
"database": {
"version": 61,
"identityHash": "c6fc1759f838676d2d7f02f04154a679",
"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, `location` INTEGER)",
"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
},
{
"fieldPath": "location",
"columnName": "location",
"affinity": "INTEGER",
"notNull": false
}
],
"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)",
"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
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "userActivity",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `remoteId` TEXT, `message` TEXT, `picture` TEXT, `target_id` TEXT, `created_at` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "remoteId",
"columnName": "remoteId",
"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
}
],
"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)",
"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": "uri",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "contentType",
"columnName": "content_type",
"affinity": "TEXT",
"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": "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": "priority",
"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": "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": "places",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`place_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `name` TEXT, `address` TEXT, `phone` TEXT, `url` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "place_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uid",
"columnName": "uid",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "address",
"columnName": "address",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "phone",
"columnName": "phone",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "latitude",
"columnName": "latitude",
"affinity": "REAL",
"notNull": true
},
{
"fieldPath": "longitude",
"columnName": "longitude",
"affinity": "REAL",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"place_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "geofences",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`geofence_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `place` TEXT, `radius` INTEGER NOT NULL, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "geofence_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "task",
"columnName": "task",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "place",
"columnName": "place",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "radius",
"columnName": "radius",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "arrival",
"columnName": "arrival",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "departure",
"columnName": "departure",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"geofence_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "tags",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `name` TEXT, `tag_uid` TEXT, `task_uid` TEXT)",
"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": "tagUid",
"columnName": "tag_uid",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "taskUid",
"columnName": "task_uid",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "google_tasks",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `remote_id` TEXT, `list_id` TEXT, `parent` INTEGER NOT NULL, `indent` INTEGER NOT NULL, `order` INTEGER NOT NULL, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "task",
"columnName": "task",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "remoteId",
"columnName": "remote_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "listId",
"columnName": "list_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "parent",
"columnName": "parent",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "indent",
"columnName": "indent",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "remoteOrder",
"columnName": "remote_order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastSync",
"columnName": "last_sync",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "filters",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `sql` TEXT, `values` TEXT, `criterion` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "sql",
"columnName": "sql",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "values",
"columnName": "values",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "criterion",
"columnName": "criterion",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "google_task_lists",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `remote_id` TEXT, `title` TEXT, `remote_order` INTEGER NOT NULL, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `color` INTEGER)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "account",
"columnName": "account",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "remoteId",
"columnName": "remote_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "remoteOrder",
"columnName": "remote_order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastSync",
"columnName": "last_sync",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "color",
"columnName": "color",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "caldav_calendar",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `uuid` TEXT, `name` TEXT, `color` INTEGER NOT NULL, `ctag` TEXT, `url` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "account",
"columnName": "account",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "color",
"columnName": "color",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ctag",
"columnName": "ctag",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "caldav_tasks",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `calendar` TEXT, `object` TEXT, `remote_id` TEXT, `etag` TEXT, `last_sync` INTEGER NOT NULL, `deleted` INTEGER NOT NULL, `vtodo` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "task",
"columnName": "task",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "calendar",
"columnName": "calendar",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "object",
"columnName": "object",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "remoteId",
"columnName": "remote_id",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "etag",
"columnName": "etag",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "lastSync",
"columnName": "last_sync",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "vtodo",
"columnName": "vtodo",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "caldav_account",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uuid` TEXT, `name` TEXT, `url` TEXT, `username` TEXT, `password` TEXT, `error` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "url",
"columnName": "url",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "google_task_accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `account` TEXT, `error` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "account",
"columnName": "account",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"_id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"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, \"c6fc1759f838676d2d7f02f04154a679\")"
]
}
}

@ -9,7 +9,9 @@ import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException; import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.location.places.Place; import com.google.android.gms.location.places.Place;
import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLng;
import com.todoroo.astrid.helper.UUIDHelper;
import org.tasks.R; import org.tasks.R;
import org.tasks.data.Geofence;
import org.tasks.data.Location; import org.tasks.data.Location;
import org.tasks.preferences.Preferences; import org.tasks.preferences.Preferences;
import timber.log.Timber; import timber.log.Timber;
@ -36,23 +38,32 @@ public class PlacePicker {
public static Location 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); Place place = com.google.android.gms.location.places.ui.PlacePicker.getPlace(context, data);
LatLng latLng = place.getLatLng(); LatLng latLng = place.getLatLng();
Location location = new Location(); Geofence g = new Geofence();
location.setName(place.getName().toString()); g.setRadius(preferences.getInt(R.string.p_default_location_radius, 250));
int defaultReminders =
preferences.getIntegerFromString(R.string.p_default_location_reminder_key, 1);
g.setArrival(defaultReminders == 1 || defaultReminders == 3);
g.setDeparture(defaultReminders == 2 || defaultReminders == 3);
org.tasks.data.Place p = new org.tasks.data.Place();
p.setUid(UUIDHelper.newUUID());
p.setName(place.getName().toString());
CharSequence address = place.getAddress(); CharSequence address = place.getAddress();
if (address != null) { if (address != null) {
location.setAddress(place.getAddress().toString()); p.setAddress(place.getAddress().toString());
} }
CharSequence phoneNumber = place.getPhoneNumber(); CharSequence phoneNumber = place.getPhoneNumber();
if (phoneNumber != null) { if (phoneNumber != null) {
location.setPhone(phoneNumber.toString()); p.setPhone(phoneNumber.toString());
} }
Uri uri = place.getWebsiteUri(); Uri uri = place.getWebsiteUri();
if (uri != null) { if (uri != null) {
location.setUrl(uri.toString()); p.setUrl(uri.toString());
} }
location.setLatitude(latLng.latitude); p.setLatitude(latLng.latitude);
location.setLongitude(latLng.longitude); p.setLongitude(latLng.longitude);
location.setRadius(preferences.getInt(R.string.p_default_location_radius, 250));
Location location = new Location(g, p);
Timber.i("Picked %s", location); Timber.i("Picked %s", location);
return location; return location;
} }

@ -15,6 +15,7 @@ import android.text.TextUtils;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.UUIDHelper;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -24,10 +25,11 @@ import org.tasks.R;
import org.tasks.backup.XmlReader; import org.tasks.backup.XmlReader;
import org.tasks.data.Alarm; import org.tasks.data.Alarm;
import org.tasks.data.AlarmDao; import org.tasks.data.AlarmDao;
import org.tasks.data.Geofence;
import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskDao; import org.tasks.data.GoogleTaskDao;
import org.tasks.data.Location;
import org.tasks.data.LocationDao; import org.tasks.data.LocationDao;
import org.tasks.data.Place;
import org.tasks.data.Tag; import org.tasks.data.Tag;
import org.tasks.data.TagDao; import org.tasks.data.TagDao;
import org.tasks.data.TagData; import org.tasks.data.TagData;
@ -245,13 +247,18 @@ public class TasksXmlImporter {
alarm.setTime(xml.readLong("value")); alarm.setTime(xml.readLong("value"));
alarmDao.insert(alarm); alarmDao.insert(alarm);
} else if ("geofence".equals(key)) { } else if ("geofence".equals(key)) {
Location location = new Location(); Place place = new Place();
location.setTask(currentTask.getId()); place.setUid(UUIDHelper.newUUID());
location.setName(xml.readString("value")); place.setName(xml.readString("value"));
location.setLatitude(xml.readDouble("value2")); place.setLatitude(xml.readDouble("value2"));
location.setLongitude(xml.readDouble("value3")); place.setLongitude(xml.readDouble("value3"));
location.setRadius(xml.readInteger("value4")); locationDao.insert(place);
locationDao.insert(location); Geofence geofence = new Geofence();
geofence.setTask(currentTask.getId());
geofence.setPlace(place.getUid());
geofence.setRadius(xml.readInteger("value4"));
geofence.setArrival(true);
locationDao.insert(geofence);
} else if ("tags-tag".equals(key)) { } else if ("tags-tag".equals(key)) {
String name = xml.readString("value"); String name = xml.readString("value");
String tagUid = xml.readString("value2"); String tagUid = xml.readString("value2");

@ -17,13 +17,14 @@ import org.tasks.data.CaldavTask;
import org.tasks.data.DeletionDao; import org.tasks.data.DeletionDao;
import org.tasks.data.Filter; import org.tasks.data.Filter;
import org.tasks.data.FilterDao; import org.tasks.data.FilterDao;
import org.tasks.data.Geofence;
import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskAccount; import org.tasks.data.GoogleTaskAccount;
import org.tasks.data.GoogleTaskDao; import org.tasks.data.GoogleTaskDao;
import org.tasks.data.GoogleTaskList; import org.tasks.data.GoogleTaskList;
import org.tasks.data.GoogleTaskListDao; import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.Location;
import org.tasks.data.LocationDao; import org.tasks.data.LocationDao;
import org.tasks.data.Place;
import org.tasks.data.Tag; import org.tasks.data.Tag;
import org.tasks.data.TagDao; import org.tasks.data.TagDao;
import org.tasks.data.TagData; import org.tasks.data.TagData;
@ -46,7 +47,8 @@ import org.tasks.notifications.NotificationDao;
TaskListMetadata.class, TaskListMetadata.class,
Task.class, Task.class,
Alarm.class, Alarm.class,
Location.class, Place.class,
Geofence.class,
Tag.class, Tag.class,
GoogleTask.class, GoogleTask.class,
Filter.class, Filter.class,
@ -56,7 +58,7 @@ import org.tasks.notifications.NotificationDao;
CaldavAccount.class, CaldavAccount.class,
GoogleTaskAccount.class GoogleTaskAccount.class
}, },
version = 60) version = 61)
public abstract class Database extends RoomDatabase { public abstract class Database extends RoomDatabase {
public static final String NAME = "database"; public static final String NAME = "database";

@ -4,15 +4,17 @@ import static java.util.Collections.emptyList;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import java.util.List; import java.util.List;
import org.tasks.backup.TasksJsonImporter.LegacyLocation;
import org.tasks.data.Alarm; import org.tasks.data.Alarm;
import org.tasks.data.CaldavAccount; import org.tasks.data.CaldavAccount;
import org.tasks.data.CaldavCalendar; import org.tasks.data.CaldavCalendar;
import org.tasks.data.CaldavTask; import org.tasks.data.CaldavTask;
import org.tasks.data.Filter; import org.tasks.data.Filter;
import org.tasks.data.Geofence;
import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskAccount; import org.tasks.data.GoogleTaskAccount;
import org.tasks.data.GoogleTaskList; import org.tasks.data.GoogleTaskList;
import org.tasks.data.Location; import org.tasks.data.Place;
import org.tasks.data.Tag; import org.tasks.data.Tag;
import org.tasks.data.TagData; import org.tasks.data.TagData;
import org.tasks.data.TaskAttachment; import org.tasks.data.TaskAttachment;
@ -21,6 +23,7 @@ import org.tasks.data.UserActivity;
class BackupContainer { class BackupContainer {
private final List<TaskBackup> tasks; private final List<TaskBackup> tasks;
private final List<Place> places;
private final List<TagData> tags; private final List<TagData> tags;
private final List<Filter> filters; private final List<Filter> filters;
private final List<GoogleTaskList> googleTaskLists; private final List<GoogleTaskList> googleTaskLists;
@ -30,6 +33,7 @@ class BackupContainer {
BackupContainer( BackupContainer(
List<TaskBackup> tasks, List<TaskBackup> tasks,
List<Place> places,
List<TagData> tags, List<TagData> tags,
List<Filter> filters, List<Filter> filters,
List<GoogleTaskAccount> googleTaskAccounts, List<GoogleTaskAccount> googleTaskAccounts,
@ -37,6 +41,7 @@ class BackupContainer {
List<CaldavAccount> caldavAccounts, List<CaldavAccount> caldavAccounts,
List<CaldavCalendar> caldavCalendars) { List<CaldavCalendar> caldavCalendars) {
this.tasks = tasks; this.tasks = tasks;
this.places = places;
this.tags = tags; this.tags = tags;
this.filters = filters; this.filters = filters;
this.googleTaskAccounts = googleTaskAccounts; this.googleTaskAccounts = googleTaskAccounts;
@ -73,21 +78,26 @@ class BackupContainer {
return googleTaskAccounts == null ? emptyList() : googleTaskAccounts; return googleTaskAccounts == null ? emptyList() : googleTaskAccounts;
} }
public List<Place> getPlaces() {
return places == null ? emptyList() : places;
}
static class TaskBackup { static class TaskBackup {
final Task task; final Task task;
final List<Alarm> alarms; final List<Alarm> alarms;
final List<Location> locations; final List<LegacyLocation> locations;
final List<Tag> tags; final List<Tag> tags;
final List<GoogleTask> google; final List<GoogleTask> google;
final List<UserActivity> comments; final List<UserActivity> comments;
private final List<Geofence> geofences;
private final List<TaskAttachment> attachments; private final List<TaskAttachment> attachments;
private final List<CaldavTask> caldavTasks; private final List<CaldavTask> caldavTasks;
TaskBackup( TaskBackup(
Task task, Task task,
List<Alarm> alarms, List<Alarm> alarms,
List<Location> locations, List<Geofence> geofences,
List<Tag> tags, List<Tag> tags,
List<GoogleTask> google, List<GoogleTask> google,
List<UserActivity> comments, List<UserActivity> comments,
@ -95,12 +105,13 @@ class BackupContainer {
List<CaldavTask> caldavTasks) { List<CaldavTask> caldavTasks) {
this.task = task; this.task = task;
this.alarms = alarms; this.alarms = alarms;
this.locations = locations; this.geofences = geofences;
this.tags = tags; this.tags = tags;
this.google = google; this.google = google;
this.comments = comments; this.comments = comments;
this.attachments = attachments; this.attachments = attachments;
this.caldavTasks = caldavTasks; this.caldavTasks = caldavTasks;
locations = emptyList();
} }
List<TaskAttachment> getAttachments() { List<TaskAttachment> getAttachments() {
@ -110,5 +121,9 @@ class BackupContainer {
List<CaldavTask> getCaldavTasks() { List<CaldavTask> getCaldavTasks() {
return caldavTasks == null ? emptyList() : caldavTasks; return caldavTasks == null ? emptyList() : caldavTasks;
} }
List<Geofence> getGeofences() {
return geofences == null ? emptyList() : geofences;
}
} }
} }

@ -181,7 +181,7 @@ public class TasksJsonExporter {
new BackupContainer.TaskBackup( new BackupContainer.TaskBackup(
task, task,
alarmDao.getAlarms(taskId), alarmDao.getAlarms(taskId),
locationDao.getGeofences(taskId), locationDao.getGeofencesForTask(taskId),
tagDao.getTagsForTask(taskId), tagDao.getTagsForTask(taskId),
googleTaskDao.getAllByTaskId(taskId), googleTaskDao.getAllByTaskId(taskId),
userActivityDao.getCommentsForTask(task.getUuid()), userActivityDao.getCommentsForTask(task.getUuid()),
@ -196,6 +196,7 @@ public class TasksJsonExporter {
"data", "data",
new BackupContainer( new BackupContainer(
taskBackups, taskBackups,
locationDao.getPlaces(),
tagDataDao.getAll(), tagDataDao.getAll(),
filterDao.getAll(), filterDao.getAll(),
googleTaskListDao.getAccounts(), googleTaskListDao.getAccounts(),

@ -13,6 +13,7 @@ import com.google.gson.JsonObject;
import com.todoroo.andlib.utility.DialogUtilities; import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.dao.TaskDao; import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task; import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.UUIDHelper;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -28,13 +29,14 @@ import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask; import org.tasks.data.CaldavTask;
import org.tasks.data.Filter; import org.tasks.data.Filter;
import org.tasks.data.FilterDao; import org.tasks.data.FilterDao;
import org.tasks.data.Geofence;
import org.tasks.data.GoogleTask; import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskAccount; import org.tasks.data.GoogleTaskAccount;
import org.tasks.data.GoogleTaskDao; import org.tasks.data.GoogleTaskDao;
import org.tasks.data.GoogleTaskList; import org.tasks.data.GoogleTaskList;
import org.tasks.data.GoogleTaskListDao; import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.Location;
import org.tasks.data.LocationDao; import org.tasks.data.LocationDao;
import org.tasks.data.Place;
import org.tasks.data.Tag; import org.tasks.data.Tag;
import org.tasks.data.TagDao; import org.tasks.data.TagDao;
import org.tasks.data.TagData; import org.tasks.data.TagData;
@ -139,6 +141,11 @@ public class TasksJsonImporter {
googleTaskListDao.insert(googleTaskAccount); googleTaskListDao.insert(googleTaskAccount);
} }
} }
for (Place place : backupContainer.getPlaces()) {
if (locationDao.getByUid(place.getUid()) == null) {
locationDao.insert(place);
}
}
for (GoogleTaskList googleTaskList : backupContainer.getGoogleTaskLists()) { for (GoogleTaskList googleTaskList : backupContainer.getGoogleTaskLists()) {
if (googleTaskListDao.getByRemoteId(googleTaskList.getRemoteId()) == null) { if (googleTaskListDao.getByRemoteId(googleTaskList.getRemoteId()) == null) {
googleTaskListDao.insert(googleTaskList); googleTaskListDao.insert(googleTaskList);
@ -185,14 +192,32 @@ public class TasksJsonImporter {
googleTask.setTask(taskId); googleTask.setTask(taskId);
googleTaskDao.insert(googleTask); googleTaskDao.insert(googleTask);
} }
for (LegacyLocation location : backup.locations) {
Place place = new Place();
place.setUid(UUIDHelper.newUUID());
place.setLongitude(location.longitude);
place.setLatitude(location.latitude);
place.setName(location.name);
place.setAddress(location.address);
place.setUrl(location.url);
place.setPhone(location.phone);
locationDao.insert(place);
Geofence geofence = new Geofence();
geofence.setTask(taskId);
geofence.setPlace(place.getUid());
geofence.setRadius(location.radius);
geofence.setArrival(location.arrival);
geofence.setDeparture(location.departure);
locationDao.insert(geofence);
}
for (Tag tag : backup.tags) { for (Tag tag : backup.tags) {
tag.setTask(taskId); tag.setTask(taskId);
tag.setTaskUid(taskUuid); tag.setTaskUid(taskUuid);
tagDao.insert(tag); tagDao.insert(tag);
} }
for (Location location : backup.locations) { for (Geofence geofence : backup.getGeofences()) {
location.setTask(taskId); geofence.setTask(taskId);
locationDao.insert(location); locationDao.insert(geofence);
} }
for (TaskAttachment attachment : backup.getAttachments()) { for (TaskAttachment attachment : backup.getAttachments()) {
attachment.setTaskId(taskUuid); attachment.setTaskId(taskUuid);
@ -239,4 +264,16 @@ public class TasksJsonImporter {
.setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.dismiss()) .setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.dismiss())
.show(); .show();
} }
static class LegacyLocation {
String name;
String address;
String phone;
String url;
double latitude;
double longitude;
int radius;
boolean arrival;
boolean departure;
}
} }

@ -25,7 +25,7 @@ public abstract class DeletionDao {
@Query("DELETE FROM tags WHERE task IN(:ids)") @Query("DELETE FROM tags WHERE task IN(:ids)")
abstract void deleteTags(List<Long> ids); abstract void deleteTags(List<Long> ids);
@Query("DELETE FROM locations WHERE task IN(:ids)") @Query("DELETE FROM geofences WHERE task IN(:ids)")
abstract void deleteGeofences(List<Long> ids); abstract void deleteGeofences(List<Long> ids);
@Query("DELETE FROM alarms WHERE task IN(:ids)") @Query("DELETE FROM alarms WHERE task IN(:ids)")

@ -0,0 +1,189 @@
package org.tasks.data;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import java.io.Serializable;
@Entity(tableName = "geofences")
public class Geofence implements Serializable, Parcelable {
public static final Parcelable.Creator<Geofence> CREATOR =
new Parcelable.Creator<Geofence>() {
@Override
public Geofence createFromParcel(Parcel source) {
return new Geofence(source);
}
@Override
public Geofence[] newArray(int size) {
return new Geofence[size];
}
};
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "geofence_id")
private transient long id;
@ColumnInfo(name = "task")
private transient long task;
@ColumnInfo(name = "place")
private String place;
@ColumnInfo(name = "radius")
private int radius;
@ColumnInfo(name = "arrival")
private boolean arrival;
@ColumnInfo(name = "departure")
private boolean departure;
public Geofence() {}
@Ignore
public Geofence(Geofence o) {
id = o.id;
task = o.task;
place = o.place;
radius = o.radius;
arrival = o.arrival;
departure = o.departure;
}
@Ignore
public Geofence(Parcel parcel) {
id = parcel.readLong();
task = parcel.readLong();
place = parcel.readString();
radius = parcel.readInt();
arrival = parcel.readInt() == 1;
departure = parcel.readInt() == 1;
}
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 getPlace() {
return place;
}
public void setPlace(String place) {
this.place = place;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public boolean isArrival() {
return arrival;
}
public void setArrival(boolean arrival) {
this.arrival = arrival;
}
public boolean isDeparture() {
return departure;
}
public void setDeparture(boolean departure) {
this.departure = departure;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Geofence location = (Geofence) o;
if (id != location.id) {
return false;
}
if (task != location.task) {
return false;
}
if (radius != location.radius) {
return false;
}
if (arrival != location.arrival) {
return false;
}
if (departure != location.departure) {
return false;
}
return place != null ? place.equals(location.place) : location.place == null;
}
@Override
public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (int) (task ^ (task >>> 32));
result = 31 * result + (place != null ? place.hashCode() : 0);
result = 31 * result + radius;
result = 31 * result + (arrival ? 1 : 0);
result = 31 * result + (departure ? 1 : 0);
return result;
}
@Override
public String toString() {
return "Location{"
+ "id="
+ id
+ ", task="
+ task
+ ", place='"
+ place
+ '\''
+ ", radius="
+ radius
+ ", arrival="
+ arrival
+ ", departure="
+ departure
+ '}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeLong(id);
out.writeLong(task);
out.writeString(place);
out.writeInt(radius);
out.writeInt(arrival ? 1 : 0);
out.writeInt(departure ? 1 : 0);
}
}

@ -1,25 +1,17 @@
package org.tasks.data; package org.tasks.data;
import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.room.ColumnInfo; import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import com.google.common.base.Strings;
import java.io.Serializable; import java.io.Serializable;
import java.util.regex.Pattern;
import org.tasks.backup.XmlReader;
@Entity(tableName = "locations")
public class Location implements Serializable, Parcelable { public class Location implements Serializable, Parcelable {
public static final Parcelable.Creator<Location> CREATOR = public static final Creator<Location> CREATOR =
new Parcelable.Creator<Location>() { new Creator<Location>() {
@Override @Override
public Location createFromParcel(Parcel source) { public Location createFromParcel(Parcel in) {
return new Location(source); return new Location(in);
} }
@Override @Override
@ -27,187 +19,117 @@ public class Location implements Serializable, Parcelable {
return new Location[size]; return new Location[size];
} }
}; };
private static final Pattern COORDS =
Pattern.compile("^\\d+°\\d+'\\d+\\.\\d+\"[NS] \\d+°\\d+'\\d+\\.\\d+\"[EW]$");
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "_id")
private transient long id;
@ColumnInfo(name = "task") @Embedded public Geofence geofence;
private transient long task; @Embedded public Place place;
@ColumnInfo(name = "name")
private String name;
@ColumnInfo(name = "address")
private String address;
@ColumnInfo(name = "phone")
private String phone;
@ColumnInfo(name = "url")
private String url;
@ColumnInfo(name = "latitude")
private double latitude;
@ColumnInfo(name = "longitude")
private double longitude;
@ColumnInfo(name = "radius")
private int radius;
@ColumnInfo(name = "arrival")
private boolean arrival;
@ColumnInfo(name = "departure")
private boolean departure;
public Location() {} public Location() {}
@Ignore public Location(Geofence geofence, Place place) {
public Location(Location o) { this.geofence = geofence;
id = o.id; this.place = place;
task = o.task;
name = o.name;
address = o.address;
phone = o.phone;
url = o.url;
latitude = o.latitude;
longitude = o.longitude;
radius = o.radius;
arrival = o.arrival;
departure = o.departure;
}
@Ignore
public Location(Parcel parcel) {
id = parcel.readLong();
task = parcel.readLong();
name = parcel.readString();
address = parcel.readString();
phone = parcel.readString();
url = parcel.readString();
latitude = parcel.readDouble();
longitude = parcel.readDouble();
radius = parcel.readInt();
arrival = parcel.readInt() == 1;
departure = parcel.readInt() == 1;
} }
@Ignore protected Location(Parcel in) {
public Location(XmlReader xml) { geofence = in.readParcelable(Geofence.class.getClassLoader());
xml.readString("name", this::setName); place = in.readParcelable(Place.class.getClassLoader());
xml.readDouble("latitude", this::setLatitude);
xml.readDouble("longitude", this::setLongitude);
xml.readInteger("radius", this::setRadius);
} }
public long getId() { public long getId() {
return id; return geofence.getId();
} }
public void setId(long id) { public void setId(long id) {
this.id = id; geofence.setId(id);
} }
public long getTask() { public long getTask() {
return task; return geofence.getTask();
} }
public void setTask(long task) { public void setTask(long task) {
this.task = task; geofence.setTask(task);
} }
public String getName() { public String getName() {
return name; return place.getName();
} }
public void setName(String name) { public void setName(String name) {
this.name = name; place.setName(name);
} }
public double getLatitude() { public double getLatitude() {
return latitude; return place.getLatitude();
} }
public void setLatitude(double latitude) { public void setLatitude(double latitude) {
this.latitude = latitude; place.setLatitude(latitude);
} }
public double getLongitude() { public double getLongitude() {
return longitude; return place.getLongitude();
} }
public void setLongitude(double longitude) { public void setLongitude(double longitude) {
this.longitude = longitude; place.setLongitude(longitude);
} }
public int getRadius() { public int getRadius() {
return radius; return geofence.getRadius();
} }
public void setRadius(int radius) { public void setRadius(int radius) {
this.radius = radius; geofence.setRadius(radius);
} }
public String getAddress() { public String getAddress() {
return address; return place.getAddress();
}
public void setAddress(String address) {
this.address = address;
} }
public String getPhone() { public String getPhone() {
return phone; return place.getPhone();
} }
public void setPhone(String phone) { public void setPhone(String phone) {
this.phone = phone; place.setPhone(phone);
} }
public String getUrl() { public String getUrl() {
return url; return place.getUrl();
} }
public void setUrl(String url) { public void setUrl(String url) {
this.url = url; place.setUrl(url);
} }
public boolean isArrival() { public boolean isArrival() {
return arrival; return geofence.isArrival();
} }
public void setArrival(boolean arrival) { public void setArrival(boolean arrival) {
this.arrival = arrival; geofence.setArrival(arrival);
} }
public boolean isDeparture() { public boolean isDeparture() {
return departure; return geofence.isDeparture();
} }
public void setDeparture(boolean departure) { public void setDeparture(boolean departure) {
this.departure = departure; geofence.setDeparture(departure);
} }
public String getDisplayName() { public String getDisplayName() {
if (Strings.isNullOrEmpty(address)) { return place.getDisplayName();
return name;
}
if (COORDS.matcher(name).matches()) {
return address;
}
if (address.startsWith(name)) {
return address;
}
return name;
} }
public String getGeoUri() { public String getGeoUri() {
return String.format( return place.getGeoUri();
"geo:%s,%s?q=%s", }
latitude, longitude, Uri.encode(Strings.isNullOrEmpty(address) ? name : address));
@Override
public String toString() {
return "Location{" + "geofence=" + geofence + ", place=" + place + '}';
} }
@Override @Override
@ -215,114 +137,33 @@ public class Location implements Serializable, Parcelable {
if (this == o) { if (this == o) {
return true; return true;
} }
if (!(o instanceof Location)) { if (o == null || getClass() != o.getClass()) {
return false; return false;
} }
Location location = (Location) o; Location location = (Location) o;
if (id != location.id) { if (geofence != null ? !geofence.equals(location.geofence) : location.geofence != null) {
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;
}
if (arrival != location.arrival) {
return false;
}
if (departure != location.departure) {
return false; return false;
} }
if (name != null ? !name.equals(location.name) : location.name != null) { return place != null ? place.equals(location.place) : location.place == null;
return false;
}
if (address != null ? !address.equals(location.address) : location.address != null) {
return false;
}
if (phone != null ? !phone.equals(location.phone) : location.phone != null) {
return false;
}
return url != null ? url.equals(location.url) : location.url == null;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result; int result = geofence != null ? geofence.hashCode() : 0;
long temp; result = 31 * result + (place != null ? place.hashCode() : 0);
result = (int) (id ^ (id >>> 32));
result = 31 * result + (int) (task ^ (task >>> 32));
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (address != null ? address.hashCode() : 0);
result = 31 * result + (phone != null ? phone.hashCode() : 0);
result = 31 * result + (url != null ? url.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 + (arrival ? 1 : 0);
result = 31 * result + (departure ? 1 : 0);
return result; return result;
} }
@Override
public String toString() {
return "Location{"
+ "id="
+ id
+ ", task="
+ task
+ ", name='"
+ name
+ '\''
+ ", address='"
+ address
+ '\''
+ ", phone='"
+ phone
+ '\''
+ ", url='"
+ url
+ '\''
+ ", latitude="
+ latitude
+ ", longitude="
+ longitude
+ ", radius="
+ radius
+ ", arrival="
+ arrival
+ ", departure="
+ departure
+ '}';
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;
} }
@Override @Override
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel dest, int flags) {
out.writeLong(id); dest.writeParcelable(geofence, flags);
out.writeLong(task); dest.writeParcelable(place, flags);
out.writeString(name);
out.writeString(address);
out.writeString(phone);
out.writeString(url);
out.writeDouble(latitude);
out.writeDouble(longitude);
out.writeInt(radius);
out.writeInt(arrival ? 1 : 0);
out.writeInt(departure ? 1 : 0);
} }
} }

@ -3,6 +3,7 @@ package org.tasks.data;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Delete; import androidx.room.Delete;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query; import androidx.room.Query;
import io.reactivex.Single; import io.reactivex.Single;
import java.util.List; import java.util.List;
@ -10,26 +11,39 @@ import java.util.List;
@Dao @Dao
public interface LocationDao { public interface LocationDao {
@Query("SELECT * FROM locations WHERE _id = :id LIMIT 1") @Query(
"SELECT * FROM geofences INNER JOIN places ON geofences.place = places.uid WHERE geofence_id = :id LIMIT 1")
Location getGeofence(Long id); Location getGeofence(Long id);
@Query("SELECT * FROM locations WHERE task = :taskId ORDER BY name ASC") @Query("SELECT * FROM geofences INNER JOIN places ON geofences.place = places.uid WHERE task = :taskId ORDER BY name ASC")
List<Location> getGeofences(long taskId); List<Location> getGeofences(long taskId);
@Query( @Query(
"SELECT locations.* FROM locations INNER JOIN tasks ON tasks._id = locations.task WHERE tasks._id = :taskId AND tasks.deleted = 0 AND tasks.completed = 0") "SELECT geofences.*, places.* FROM geofences INNER JOIN places ON geofences.place = places.uid INNER JOIN tasks ON tasks._id = geofences.task WHERE tasks._id = :taskId AND tasks.deleted = 0 AND tasks.completed = 0")
List<Location> getActiveGeofences(long taskId); List<Location> getActiveGeofences(long taskId);
@Query( @Query(
"SELECT locations.* FROM locations INNER JOIN tasks ON tasks._id = locations.task WHERE tasks.deleted = 0 AND tasks.completed = 0") "SELECT geofences.*, places.* FROM geofences INNER JOIN places ON geofences.place = places.uid INNER JOIN tasks ON tasks._id = geofences.task WHERE tasks.deleted = 0 AND tasks.completed = 0")
List<Location> getActiveGeofences(); List<Location> getActiveGeofences();
@Query("SELECT COUNT(*) FROM locations") @Query("SELECT COUNT(*) FROM geofences")
Single<Integer> geofenceCount(); Single<Integer> geofenceCount();
@Delete @Delete
void delete(Location location); void delete(Geofence location);
@Insert @Insert
void insert(Location location); void insert(Geofence location);
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insert(Place place);
@Query("SELECT * FROM places WHERE uid = :uid LIMIT 1")
Place getByUid(String uid);
@Query("SELECT * FROM geofences WHERE task = :taskId")
List<Geofence> getGeofencesForTask(long taskId);
@Query("SELECT * FROM places")
List<Place> getPlaces();
} }

@ -0,0 +1,262 @@
package org.tasks.data;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import com.google.common.base.Strings;
import java.io.Serializable;
import java.util.regex.Pattern;
@Entity(tableName = "places")
public class Place implements Serializable, Parcelable {
public static final Creator<Place> CREATOR =
new Creator<Place>() {
@Override
public Place createFromParcel(Parcel source) {
return new Place(source);
}
@Override
public Place[] newArray(int size) {
return new Place[size];
}
};
private static final Pattern COORDS =
Pattern.compile("^\\d+°\\d+'\\d+\\.\\d+\"[NS] \\d+°\\d+'\\d+\\.\\d+\"[EW]$");
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "place_id")
private transient long id;
@ColumnInfo(name = "uid")
private String uid;
@ColumnInfo(name = "name")
private String name;
@ColumnInfo(name = "address")
private String address;
@ColumnInfo(name = "phone")
private String phone;
@ColumnInfo(name = "url")
private String url;
@ColumnInfo(name = "latitude")
private double latitude;
@ColumnInfo(name = "longitude")
private double longitude;
public Place() {}
@Ignore
public Place(Place o) {
id = o.id;
uid = o.uid;
name = o.name;
address = o.address;
phone = o.phone;
url = o.url;
latitude = o.latitude;
longitude = o.longitude;
}
@Ignore
public Place(Parcel parcel) {
id = parcel.readLong();
uid = parcel.readString();
name = parcel.readString();
address = parcel.readString();
phone = parcel.readString();
url = parcel.readString();
latitude = parcel.readDouble();
longitude = parcel.readDouble();
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
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 String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDisplayName() {
if (Strings.isNullOrEmpty(address)) {
return name;
}
if (COORDS.matcher(name).matches()) {
return address;
}
if (address.startsWith(name)) {
return address;
}
return name;
}
public String getGeoUri() {
return String.format(
"geo:%s,%s?q=%s",
latitude, longitude, Uri.encode(Strings.isNullOrEmpty(address) ? name : address));
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Place place = (Place) o;
if (id != place.id) {
return false;
}
if (Double.compare(place.latitude, latitude) != 0) {
return false;
}
if (Double.compare(place.longitude, longitude) != 0) {
return false;
}
if (uid != null ? !uid.equals(place.uid) : place.uid != null) {
return false;
}
if (name != null ? !name.equals(place.name) : place.name != null) {
return false;
}
if (address != null ? !address.equals(place.address) : place.address != null) {
return false;
}
if (phone != null ? !phone.equals(place.phone) : place.phone != null) {
return false;
}
return url != null ? url.equals(place.url) : place.url == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = (int) (id ^ (id >>> 32));
result = 31 * result + (uid != null ? uid.hashCode() : 0);
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (address != null ? address.hashCode() : 0);
result = 31 * result + (phone != null ? phone.hashCode() : 0);
result = 31 * result + (url != null ? url.hashCode() : 0);
temp = Double.doubleToLongBits(latitude);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(longitude);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "Place{"
+ "id="
+ id
+ ", uid='"
+ uid
+ '\''
+ ", name='"
+ name
+ '\''
+ ", address='"
+ address
+ '\''
+ ", phone='"
+ phone
+ '\''
+ ", url='"
+ url
+ '\''
+ ", latitude="
+ latitude
+ ", longitude="
+ longitude
+ '}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeLong(id);
out.writeString(uid);
out.writeString(name);
out.writeString(address);
out.writeString(phone);
out.writeString(url);
out.writeDouble(latitude);
out.writeDouble(longitude);
}
}

@ -218,6 +218,18 @@ public class Migrations {
} }
}; };
private static final Migration MIGRATION_60_61 =
new Migration(60, 61) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
database.execSQL("CREATE TABLE IF NOT EXISTS `places` (`place_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `uid` TEXT, `name` TEXT, `address` TEXT, `phone` TEXT, `url` TEXT, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL)");
database.execSQL("CREATE TABLE IF NOT EXISTS `geofences` (`geofence_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `task` INTEGER NOT NULL, `place` TEXT, `radius` INTEGER NOT NULL, `arrival` INTEGER NOT NULL, `departure` INTEGER NOT NULL)");
database.execSQL("INSERT INTO `places` (`place_id`, `uid`, `name`, `address`, `phone`, `url`, `latitude`, `longitude`) SELECT `_id`, hex(randomblob(16)), `name`, `address`, `phone`, `url`, `latitude`, `longitude` FROM `locations`");
database.execSQL("INSERT INTO `geofences` (`geofence_id`, `task`, `place`, `radius`, `arrival`, `departure`) SELECT `_id`, `task`, `uid`, `radius`, `arrival`, `departure` FROM `locations` INNER JOIN `places` ON `_id` = `place_id`");
database.execSQL("DROP TABLE `locations`");
}
};
public static final Migration[] MIGRATIONS = public static final Migration[] MIGRATIONS =
new Migration[] { new Migration[] {
MIGRATION_35_36, MIGRATION_35_36,
@ -235,7 +247,8 @@ public class Migrations {
MIGRATION_53_54, MIGRATION_53_54,
MIGRATION_54_58, MIGRATION_54_58,
MIGRATION_58_59, MIGRATION_58_59,
MIGRATION_59_60 MIGRATION_59_60,
MIGRATION_60_61
}; };
private static Migration NOOP(int from, int to) { private static Migration NOOP(int from, int to) {

@ -112,7 +112,7 @@ public class LocationDialog extends InjectingDialogFragment {
} }
private Location getOriginal() { private Location getOriginal() {
return new Location(getArguments().<Location>getParcelable(EXTRA_ORIGINAL)); return getArguments().getParcelable(EXTRA_ORIGINAL);
} }
private void sendResult(DialogInterface d, int... i) { private void sendResult(DialogInterface d, int... i) {

@ -7,8 +7,10 @@ import static java.util.Collections.emptyList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import org.tasks.data.Geofence;
import org.tasks.data.Location; import org.tasks.data.Location;
import org.tasks.data.LocationDao; import org.tasks.data.LocationDao;
import org.tasks.data.Place;
public class GeofenceService { public class GeofenceService {
@ -71,14 +73,18 @@ public class GeofenceService {
if (callback != null) { if (callback != null) {
callback.beforeDelete(item); callback.beforeDelete(item);
} }
locationDao.delete(item); locationDao.delete(item.geofence);
dirty = true; dirty = true;
} }
} }
// everything that remains shall be written // everything that remains shall be written
for (Location location : locations) { for (Location location : locations) {
locationDao.insert(location); Place place = location.place;
locationDao.insert(place);
Geofence geofence = location.geofence;
geofence.setPlace(place.getUid());
locationDao.insert(geofence);
dirty = true; dirty = true;
} }

@ -150,10 +150,6 @@ public class LocationControlSet extends TaskEditControlFragment {
if (requestCode == REQUEST_LOCATION_REMINDER) { if (requestCode == REQUEST_LOCATION_REMINDER) {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
Location place = PlacePicker.getPlace(context, data, preferences); Location place = PlacePicker.getPlace(context, data, preferences);
int defaultReminders =
preferences.getIntegerFromString(R.string.p_default_location_reminder_key, 1);
place.setArrival(defaultReminders == 1 || defaultReminders == 3);
place.setDeparture(defaultReminders == 2 || defaultReminders == 3);
locations.add(place); locations.add(place);
setup(locations); setup(locations);
} }

Loading…
Cancel
Save