From 84c83a9fac07aa9fd939b6991ad0b1bf7cea0cee Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Thu, 12 Mar 2020 14:14:27 -0500 Subject: [PATCH] Match places with four decimal precision --- .../java/org/tasks/caldav/GeoUtilsTest.kt | 46 ++++++++++++++++++ .../java/org/tasks/data/LocationDaoTest.kt | 48 +++++++++++++++++++ .../org/tasks/injection/TestComponent.java | 3 ++ .../java/org/tasks/makers/PlaceMaker.kt | 23 +++++++++ .../main/java/org/tasks/caldav/GeoUtils.kt | 41 ++++++++++++++++ .../main/java/org/tasks/data/LocationDao.java | 4 +- app/src/main/java/org/tasks/data/Place.java | 11 +++-- .../location/LocationPickerActivity.java | 5 +- 8 files changed, 173 insertions(+), 8 deletions(-) create mode 100644 app/src/androidTest/java/org/tasks/caldav/GeoUtilsTest.kt create mode 100644 app/src/androidTest/java/org/tasks/data/LocationDaoTest.kt create mode 100644 app/src/androidTest/java/org/tasks/makers/PlaceMaker.kt create mode 100644 app/src/main/java/org/tasks/caldav/GeoUtils.kt diff --git a/app/src/androidTest/java/org/tasks/caldav/GeoUtilsTest.kt b/app/src/androidTest/java/org/tasks/caldav/GeoUtilsTest.kt new file mode 100644 index 000000000..85f8bfae2 --- /dev/null +++ b/app/src/androidTest/java/org/tasks/caldav/GeoUtilsTest.kt @@ -0,0 +1,46 @@ +package org.tasks.caldav + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import net.fortuna.ical4j.model.property.Geo +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith +import org.tasks.caldav.GeoUtils.equalish +import org.tasks.caldav.GeoUtils.latitudeLike +import org.tasks.caldav.GeoUtils.longitudeLike + +@RunWith(AndroidJUnit4::class) +class GeoUtilsTest { + @Test + fun getLatitudeLike() = + assertEquals("42.4347%", newGeo(42.434722, -83.985).latitudeLike()) + + @Test + fun getLatitudeLikeShort() = + assertEquals("-37.3", newGeo(-37.3, -12.68).latitudeLike()) + + @Test + fun getLongitudeLike() = + assertEquals("-122.3736%", newGeo(45.43, -122.373611).longitudeLike()) + + @Test + fun getLongitudeLikeShort() = + assertEquals("-12.68", newGeo(-37.3, -12.68).longitudeLike()) + + @Test + fun compareGeo() = + assertTrue(newGeo(-37.3, -12.68).equalish(newGeo(-37.3, -12.68))) + + @Test + fun compareGeoWithLatTruncation() = + assertTrue(newGeo(42.434722, -83.985).equalish(newGeo(42.4347, -83.985))) + + @Test + fun compareGeoWithLongTruncation() = + assertTrue(newGeo(45.43, -122.373611).equalish(newGeo(45.43, -122.3736))) + + @Test + fun compareGeoRightSideNull() = assertFalse(newGeo(63.4444, 10.9227).equalish(null)) + + private fun newGeo(latitude: Double, longitude: Double) = Geo("${latitude};${longitude}") +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/tasks/data/LocationDaoTest.kt b/app/src/androidTest/java/org/tasks/data/LocationDaoTest.kt new file mode 100644 index 000000000..d887a513e --- /dev/null +++ b/app/src/androidTest/java/org/tasks/data/LocationDaoTest.kt @@ -0,0 +1,48 @@ +package org.tasks.data + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.natpryce.makeiteasy.MakeItEasy.with +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.tasks.caldav.GeoUtils.toLikeString +import org.tasks.injection.InjectingTestCase +import org.tasks.injection.TestComponent +import org.tasks.makers.PlaceMaker.LATITUDE +import org.tasks.makers.PlaceMaker.LONGITUDE +import org.tasks.makers.PlaceMaker.newPlace +import javax.inject.Inject + +@RunWith(AndroidJUnit4::class) +class LocationDaoTest : InjectingTestCase() { + + @Inject lateinit var locationDao: LocationDao + + @Test + fun getExistingPlace() { + locationDao.insert(newPlace(with(LATITUDE, 48.067222), with(LONGITUDE, 12.863611))) + val place = locationDao.findPlace(48.067222.toLikeString(), 12.863611.toLikeString()) + assertEquals(48.067222, place?.latitude) + assertEquals(12.863611, place?.longitude) + } + + @Test + fun getPlaceWithLessPrecision() { + locationDao.insert(newPlace(with(LATITUDE, 50.7547), with(LONGITUDE, -2.2279))); + val place = locationDao.findPlace(50.754712.toLikeString(), (-2.227945).toLikeString()) + assertEquals(50.7547, place?.latitude) + assertEquals(-2.2279, place?.longitude) + } + + @Test + fun getPlaceWithMorePrecision() { + locationDao.insert(newPlace(with(LATITUDE, 36.246944), with(LONGITUDE, -116.816944))) + locationDao.places.forEach { println(it) } + val place = locationDao.findPlace(36.2469.toLikeString(), (-116.8169).toLikeString()) + assertEquals(36.246944, place?.latitude) + assertEquals(-116.816944, place?.longitude) + } + + override fun inject(component: TestComponent) = component.inject(this) +} + diff --git a/app/src/androidTest/java/org/tasks/injection/TestComponent.java b/app/src/androidTest/java/org/tasks/injection/TestComponent.java index f36327107..72469a11f 100644 --- a/app/src/androidTest/java/org/tasks/injection/TestComponent.java +++ b/app/src/androidTest/java/org/tasks/injection/TestComponent.java @@ -17,6 +17,7 @@ import dagger.Component; import org.tasks.data.CaldavDaoTests; import org.tasks.data.DeletionDaoTests; import org.tasks.data.GoogleTaskDaoTests; +import org.tasks.data.LocationDaoTest; import org.tasks.data.TagDataDaoTest; import org.tasks.jobs.BackupServiceTests; @@ -59,4 +60,6 @@ public interface TestComponent extends ApplicationComponent { void inject(CaldavDaoTests caldavDaoTests); void inject(TaskMoverTest taskMoverTest); + + void inject(LocationDaoTest locationDaoTest); } diff --git a/app/src/androidTest/java/org/tasks/makers/PlaceMaker.kt b/app/src/androidTest/java/org/tasks/makers/PlaceMaker.kt new file mode 100644 index 000000000..9d4b2be7c --- /dev/null +++ b/app/src/androidTest/java/org/tasks/makers/PlaceMaker.kt @@ -0,0 +1,23 @@ +package org.tasks.makers + +import com.natpryce.makeiteasy.Instantiator +import com.natpryce.makeiteasy.Property +import com.natpryce.makeiteasy.PropertyLookup +import com.natpryce.makeiteasy.PropertyValue +import org.tasks.data.Place + +object PlaceMaker { + val LATITUDE: Property = Property.newProperty() + val LONGITUDE: Property = Property.newProperty() + + private val instantiator = Instantiator { lookup: PropertyLookup -> + val place = Place() + place.latitude = lookup.valueOf(LATITUDE, 0.0) + place.longitude = lookup.valueOf(LONGITUDE, 0.0) + place + } + + fun newPlace(vararg properties: PropertyValue): Place { + return Maker.make(instantiator, *properties) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/caldav/GeoUtils.kt b/app/src/main/java/org/tasks/caldav/GeoUtils.kt new file mode 100644 index 000000000..731537735 --- /dev/null +++ b/app/src/main/java/org/tasks/caldav/GeoUtils.kt @@ -0,0 +1,41 @@ +package org.tasks.caldav + +import net.fortuna.ical4j.model.property.Geo +import org.tasks.data.Location +import java.math.BigDecimal +import kotlin.math.min + +object GeoUtils { + fun toGeo(location: Location?) = if (location == null) { + null + } else { + Geo("${location.latitude};${location.longitude}") + } + + fun Geo.latitudeLike() = latitude.toLikeString() + + fun Geo.longitudeLike() = longitude.toLikeString() + + fun Double.toLikeString(): String = BigDecimal(toString()).toLikeString() + + fun BigDecimal.toLikeString(): String { + val string = truncate() + return if (string.numDecimalPlaces() < 4) string else "${string}%" + } + + fun Geo.equalish(other: Geo?): Boolean { + return latitude.truncate() == other?.latitude?.truncate() + && longitude.truncate() == other?.longitude?.truncate() + } + + private fun String.numDecimalPlaces(): Int { + val index = indexOf(".") + return if (index < 0) 0 else length - index - 1 + } + + private fun BigDecimal.truncate(): String { + val string = stripTrailingZeros().toPlainString() + val index = string.indexOf(".") + return if (index < 0) string else string.substring(0.until(min(string.length, index + 5))) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/data/LocationDao.java b/app/src/main/java/org/tasks/data/LocationDao.java index 5eb363e52..1a09e59a2 100644 --- a/app/src/main/java/org/tasks/data/LocationDao.java +++ b/app/src/main/java/org/tasks/data/LocationDao.java @@ -60,6 +60,6 @@ public interface LocationDao { "SELECT places.*, IFNULL(COUNT(geofence_id),0) AS count FROM places LEFT OUTER JOIN geofences ON geofences.place = places.uid GROUP BY uid ORDER BY COUNT(geofence_id) DESC") LiveData> getPlaceUsage(); - @Query("SELECT * FROM places WHERE latitude = :latitude AND longitude = :longitude LIMIT 1") - Place findPlace(double latitude, double longitude); + @Query("SELECT * FROM places WHERE latitude LIKE :latitude AND longitude LIKE :longitude") + Place findPlace(String latitude, String longitude); } diff --git a/app/src/main/java/org/tasks/data/Place.java b/app/src/main/java/org/tasks/data/Place.java index d133ab11c..969f24cec 100644 --- a/app/src/main/java/org/tasks/data/Place.java +++ b/app/src/main/java/org/tasks/data/Place.java @@ -44,8 +44,6 @@ public class Place implements Serializable, Parcelable { } }; private static final Pattern pattern = Pattern.compile("(\\d+):(\\d+):(\\d+\\.\\d+)"); - private static final Pattern COORDS = - Pattern.compile("^\\d+°\\d+'\\d+\\.\\d+\"[NS] \\d+°\\d+'\\d+\\.\\d+\"[EW]$"); @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "place_id") @@ -213,10 +211,13 @@ public class Place implements Serializable, Parcelable { } public String getDisplayName() { - if (Strings.isNullOrEmpty(address) || !COORDS.matcher(name).matches()) { - return Strings.isNullOrEmpty(name) ? formatCoordinates(this) : name; + if (!Strings.isNullOrEmpty(address)) { + return address; } - return address; + if (!Strings.isNullOrEmpty(name)) { + return name; + } + return formatCoordinates(this); } public String getDisplayAddress() { diff --git a/app/src/main/java/org/tasks/location/LocationPickerActivity.java b/app/src/main/java/org/tasks/location/LocationPickerActivity.java index dfc5db7cf..94ff3ec7a 100644 --- a/app/src/main/java/org/tasks/location/LocationPickerActivity.java +++ b/app/src/main/java/org/tasks/location/LocationPickerActivity.java @@ -50,6 +50,7 @@ import javax.inject.Inject; import org.tasks.Event; import org.tasks.R; import org.tasks.billing.Inventory; +import org.tasks.caldav.GeoUtils; import org.tasks.data.LocationDao; import org.tasks.data.Place; import org.tasks.data.PlaceUsage; @@ -331,7 +332,9 @@ public class LocationPickerActivity extends InjectingAppCompatActivity } if (place.getId() <= 0) { org.tasks.data.Place existing = - locationDao.findPlace(place.getLatitude(), place.getLongitude()); + locationDao.findPlace( + GeoUtils.INSTANCE.toLikeString(place.getLatitude()), + GeoUtils.INSTANCE.toLikeString(place.getLongitude())); if (existing == null) { place.setId(locationDao.insert(place)); } else {