Split locations table into geofences and places

pull/795/head
Alex Baker 6 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.location.places.Place;
import com.google.android.gms.maps.model.LatLng;
import com.todoroo.astrid.helper.UUIDHelper;
import org.tasks.R;
import org.tasks.data.Geofence;
import org.tasks.data.Location;
import org.tasks.preferences.Preferences;
import timber.log.Timber;
@ -36,23 +38,32 @@ public class PlacePicker {
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();
Location location = new Location();
location.setName(place.getName().toString());
Geofence g = new Geofence();
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();
if (address != null) {
location.setAddress(place.getAddress().toString());
p.setAddress(place.getAddress().toString());
}
CharSequence phoneNumber = place.getPhoneNumber();
if (phoneNumber != null) {
location.setPhone(phoneNumber.toString());
p.setPhone(phoneNumber.toString());
}
Uri uri = place.getWebsiteUri();
if (uri != null) {
location.setUrl(uri.toString());
p.setUrl(uri.toString());
}
location.setLatitude(latLng.latitude);
location.setLongitude(latLng.longitude);
location.setRadius(preferences.getInt(R.string.p_default_location_radius, 250));
p.setLatitude(latLng.latitude);
p.setLongitude(latLng.longitude);
Location location = new Location(g, p);
Timber.i("Picked %s", location);
return location;
}

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

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

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

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

@ -13,6 +13,7 @@ import com.google.gson.JsonObject;
import com.todoroo.andlib.utility.DialogUtilities;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.helper.UUIDHelper;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@ -28,13 +29,14 @@ import org.tasks.data.CaldavDao;
import org.tasks.data.CaldavTask;
import org.tasks.data.Filter;
import org.tasks.data.FilterDao;
import org.tasks.data.Geofence;
import org.tasks.data.GoogleTask;
import org.tasks.data.GoogleTaskAccount;
import org.tasks.data.GoogleTaskDao;
import org.tasks.data.GoogleTaskList;
import org.tasks.data.GoogleTaskListDao;
import org.tasks.data.Location;
import org.tasks.data.LocationDao;
import org.tasks.data.Place;
import org.tasks.data.Tag;
import org.tasks.data.TagDao;
import org.tasks.data.TagData;
@ -139,6 +141,11 @@ public class TasksJsonImporter {
googleTaskListDao.insert(googleTaskAccount);
}
}
for (Place place : backupContainer.getPlaces()) {
if (locationDao.getByUid(place.getUid()) == null) {
locationDao.insert(place);
}
}
for (GoogleTaskList googleTaskList : backupContainer.getGoogleTaskLists()) {
if (googleTaskListDao.getByRemoteId(googleTaskList.getRemoteId()) == null) {
googleTaskListDao.insert(googleTaskList);
@ -185,14 +192,32 @@ public class TasksJsonImporter {
googleTask.setTask(taskId);
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) {
tag.setTask(taskId);
tag.setTaskUid(taskUuid);
tagDao.insert(tag);
}
for (Location location : backup.locations) {
location.setTask(taskId);
locationDao.insert(location);
for (Geofence geofence : backup.getGeofences()) {
geofence.setTask(taskId);
locationDao.insert(geofence);
}
for (TaskAttachment attachment : backup.getAttachments()) {
attachment.setTaskId(taskUuid);
@ -239,4 +264,16 @@ public class TasksJsonImporter {
.setPositiveButton(android.R.string.ok, (dialog, id) -> dialog.dismiss())
.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)")
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);
@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;
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 androidx.room.Embedded;
import java.io.Serializable;
import java.util.regex.Pattern;
import org.tasks.backup.XmlReader;
@Entity(tableName = "locations")
public class Location implements Serializable, Parcelable {
public static final Parcelable.Creator<Location> CREATOR =
new Parcelable.Creator<Location>() {
public static final Creator<Location> CREATOR =
new Creator<Location>() {
@Override
public Location createFromParcel(Parcel source) {
return new Location(source);
public Location createFromParcel(Parcel in) {
return new Location(in);
}
@Override
@ -27,187 +19,117 @@ public class Location implements Serializable, Parcelable {
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")
private transient long task;
@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;
@Embedded public Geofence geofence;
@Embedded public Place place;
public Location() {}
@Ignore
public Location(Location o) {
id = o.id;
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;
public Location(Geofence geofence, Place place) {
this.geofence = geofence;
this.place = place;
}
@Ignore
public Location(XmlReader xml) {
xml.readString("name", this::setName);
xml.readDouble("latitude", this::setLatitude);
xml.readDouble("longitude", this::setLongitude);
xml.readInteger("radius", this::setRadius);
protected Location(Parcel in) {
geofence = in.readParcelable(Geofence.class.getClassLoader());
place = in.readParcelable(Place.class.getClassLoader());
}
public long getId() {
return id;
return geofence.getId();
}
public void setId(long id) {
this.id = id;
geofence.setId(id);
}
public long getTask() {
return task;
return geofence.getTask();
}
public void setTask(long task) {
this.task = task;
geofence.setTask(task);
}
public String getName() {
return name;
return place.getName();
}
public void setName(String name) {
this.name = name;
place.setName(name);
}
public double getLatitude() {
return latitude;
return place.getLatitude();
}
public void setLatitude(double latitude) {
this.latitude = latitude;
place.setLatitude(latitude);
}
public double getLongitude() {
return longitude;
return place.getLongitude();
}
public void setLongitude(double longitude) {
this.longitude = longitude;
place.setLongitude(longitude);
}
public int getRadius() {
return radius;
return geofence.getRadius();
}
public void setRadius(int radius) {
this.radius = radius;
geofence.setRadius(radius);
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
return place.getAddress();
}
public String getPhone() {
return phone;
return place.getPhone();
}
public void setPhone(String phone) {
this.phone = phone;
place.setPhone(phone);
}
public String getUrl() {
return url;
return place.getUrl();
}
public void setUrl(String url) {
this.url = url;
place.setUrl(url);
}
public boolean isArrival() {
return arrival;
return geofence.isArrival();
}
public void setArrival(boolean arrival) {
this.arrival = arrival;
geofence.setArrival(arrival);
}
public boolean isDeparture() {
return departure;
return geofence.isDeparture();
}
public void setDeparture(boolean departure) {
this.departure = departure;
geofence.setDeparture(departure);
}
public String getDisplayName() {
if (Strings.isNullOrEmpty(address)) {
return name;
}
if (COORDS.matcher(name).matches()) {
return address;
}
if (address.startsWith(name)) {
return address;
}
return name;
return place.getDisplayName();
}
public String getGeoUri() {
return String.format(
"geo:%s,%s?q=%s",
latitude, longitude, Uri.encode(Strings.isNullOrEmpty(address) ? name : address));
return place.getGeoUri();
}
@Override
public String toString() {
return "Location{" + "geofence=" + geofence + ", place=" + place + '}';
}
@Override
@ -215,114 +137,33 @@ public class Location implements Serializable, Parcelable {
if (this == o) {
return true;
}
if (!(o instanceof Location)) {
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;
}
if (arrival != location.arrival) {
return false;
}
if (departure != location.departure) {
if (geofence != null ? !geofence.equals(location.geofence) : location.geofence != null) {
return false;
}
if (name != null ? !name.equals(location.name) : location.name != 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;
return place != null ? place.equals(location.place) : location.place == 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);
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);
int result = geofence != null ? geofence.hashCode() : 0;
result = 31 * result + (place != null ? place.hashCode() : 0);
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
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeLong(id);
out.writeLong(task);
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);
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(geofence, flags);
dest.writeParcelable(place, flags);
}
}

@ -3,6 +3,7 @@ package org.tasks.data;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import io.reactivex.Single;
import java.util.List;
@ -10,26 +11,39 @@ import java.util.List;
@Dao
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);
@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);
@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);
@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();
@Query("SELECT COUNT(*) FROM locations")
@Query("SELECT COUNT(*) FROM geofences")
Single<Integer> geofenceCount();
@Delete
void delete(Location location);
void delete(Geofence location);
@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 =
new Migration[] {
MIGRATION_35_36,
@ -235,7 +247,8 @@ public class Migrations {
MIGRATION_53_54,
MIGRATION_54_58,
MIGRATION_58_59,
MIGRATION_59_60
MIGRATION_59_60,
MIGRATION_60_61
};
private static Migration NOOP(int from, int to) {

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

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

@ -150,10 +150,6 @@ public class LocationControlSet extends TaskEditControlFragment {
if (requestCode == REQUEST_LOCATION_REMINDER) {
if (resultCode == Activity.RESULT_OK) {
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);
setup(locations);
}

Loading…
Cancel
Save