package org.tasks.location import android.content.Context import com.google.gson.JsonElement import com.google.gson.JsonParser import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.Request import org.tasks.BuildConfig import org.tasks.R import org.tasks.data.Place import org.tasks.data.Place.Companion.newPlace import org.tasks.http.HttpClientFactory import org.tasks.http.HttpException import javax.inject.Inject class GeocoderNominatim @Inject constructor( @ApplicationContext context: Context, private val httpClientFactory: HttpClientFactory, ) : Geocoder { private val url = context.getString(R.string.tasks_nominatim_url) override suspend fun reverseGeocode(mapPosition: MapPosition): Place? = withContext(Dispatchers.IO) { val client = httpClientFactory.newBuilder(foreground = true).build() val url = "$url/reverse?format=geocodejson&lat=${mapPosition.latitude}&lon=${mapPosition.longitude}" val response = client.newCall( Request.Builder().get().url(url).addHeader(USER_AGENT, UA_VALUE).build() ).execute() if (response.isSuccessful) { response.body?.string()?.let { jsonToPlace(it) } } else { throw HttpException(response.code, response.message) } } companion object { private const val USER_AGENT = "User-Agent" private const val UA_VALUE = "${BuildConfig.APPLICATION_ID}/${BuildConfig.VERSION_NAME} (${BuildConfig.FLAVOR}-${BuildConfig.BUILD_TYPE})" internal fun jsonToPlace(json: String): Place? = JsonParser .parseString(json).asJsonObject.getAsJsonArray("features") .takeIf { it.size() > 0 }?.get(0)?.asJsonObject ?.let { feature -> val geocoding = feature .get("properties").asJsonObject .get("geocoding").asJsonObject val geometry = feature.get("geometry").asJsonObject newPlace().apply { val type = geocoding.get("type").asString name = if (type.equals("house")) { "${geocoding.get("housenumber").asString} ${geocoding.get("street").asString}" } else { geocoding.get("name").asString } address = geocoding.get("label").asString geometry.get("coordinates").asCoordinates.let { longitude = it.first latitude = it.second } } } private val JsonElement.asCoordinates: Pair get() = asJsonArray.let { Pair(it[0].asDouble, it[1].asDouble) } } }