From f52d7f29d969b8d8df2e7c7c0bf3ed0773bdc31e Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 11 Jun 2021 00:24:21 +0200 Subject: [PATCH] replace nominatimClient with copied/pacthes version replacing legacy httpClient with okhttp3 Signed-off-by: Andy Scherzinger --- app/build.gradle | 6 +- .../talk/controllers/GeocodingController.kt | 22 +- .../controllers/LocationPickerController.kt | 22 +- .../client/TalkJsonNominatimClient.java | 338 ++++++++++++++++++ 4 files changed, 356 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/fr/dudie/nominatim/client/TalkJsonNominatimClient.java diff --git a/app/build.gradle b/app/build.gradle index 11c813925..35c829263 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -294,8 +294,10 @@ dependencies { implementation 'com.elyeproj.libraries:loaderviewlibrary:2.0.0' implementation 'org.osmdroid:osmdroid-android:6.1.10' - //noinspection DuplicatePlatformClasses - implementation 'fr.dudie:nominatim-api:3.4' + implementation ('fr.dudie:nominatim-api:3.4', { + //noinspection DuplicatePlatformClasses + exclude group: 'org.apache.httpcomponents', module: 'httpclient' + }) testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.11.0' diff --git a/app/src/main/java/com/nextcloud/talk/controllers/GeocodingController.kt b/app/src/main/java/com/nextcloud/talk/controllers/GeocodingController.kt index 0373c212f..332c69051 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/GeocodingController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/GeocodingController.kt @@ -46,20 +46,14 @@ import com.nextcloud.talk.controllers.util.viewBinding import com.nextcloud.talk.databinding.ControllerGeocodingBinding import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.database.user.UserUtils -import fr.dudie.nominatim.client.JsonNominatimClient +import fr.dudie.nominatim.client.TalkJsonNominatimClient import fr.dudie.nominatim.model.Address import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.apache.http.client.HttpClient -import org.apache.http.conn.ClientConnectionManager -import org.apache.http.conn.scheme.Scheme -import org.apache.http.conn.scheme.SchemeRegistry -import org.apache.http.conn.ssl.SSLSocketFactory -import org.apache.http.impl.client.DefaultHttpClient -import org.apache.http.impl.conn.SingleClientConnManager +import okhttp3.OkHttpClient import org.osmdroid.config.Configuration import javax.inject.Inject @@ -78,8 +72,11 @@ class GeocodingController(args: Bundle) : @Inject lateinit var userUtils: UserUtils + @Inject + lateinit var okHttpClient: OkHttpClient + var roomToken: String? - var nominatimClient: JsonNominatimClient? = null + var nominatimClient: TalkJsonNominatimClient? = null var searchItem: MenuItem? = null var searchView: SearchView? = null @@ -178,13 +175,9 @@ class GeocodingController(args: Bundle) : } private fun initGeocoder() { - val registry = SchemeRegistry() - registry.register(Scheme("https", SSLSocketFactory.getSocketFactory(), HTTPS_PORT)) - val connexionManager: ClientConnectionManager = SingleClientConnManager(null, registry) - val httpClient: HttpClient = DefaultHttpClient(connexionManager, null) val baseUrl = context!!.getString(R.string.osm_geocoder_url) val email = context!!.getString(R.string.osm_geocoder_contact) - nominatimClient = JsonNominatimClient(baseUrl, httpClient, email) + nominatimClient = TalkJsonNominatimClient(baseUrl, okHttpClient, email) } private fun searchLocation(): Boolean { @@ -223,6 +216,5 @@ class GeocodingController(args: Bundle) : companion object { private const val TAG = "GeocodingController" - private const val HTTPS_PORT: Int = 443 } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/LocationPickerController.kt b/app/src/main/java/com/nextcloud/talk/controllers/LocationPickerController.kt index daa90425b..fd68dd88e 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/LocationPickerController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/LocationPickerController.kt @@ -57,7 +57,7 @@ import com.nextcloud.talk.utils.DisplayUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.database.user.UserUtils -import fr.dudie.nominatim.client.JsonNominatimClient +import fr.dudie.nominatim.client.TalkJsonNominatimClient import fr.dudie.nominatim.model.Address import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers @@ -67,13 +67,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.apache.http.client.HttpClient -import org.apache.http.conn.ClientConnectionManager -import org.apache.http.conn.scheme.Scheme -import org.apache.http.conn.scheme.SchemeRegistry -import org.apache.http.conn.ssl.SSLSocketFactory -import org.apache.http.impl.client.DefaultHttpClient -import org.apache.http.impl.conn.SingleClientConnManager +import okhttp3.OkHttpClient import org.osmdroid.config.Configuration.getInstance import org.osmdroid.events.DelayedMapListener import org.osmdroid.events.MapListener @@ -103,7 +97,10 @@ class LocationPickerController(args: Bundle) : @Inject lateinit var userUtils: UserUtils - var nominatimClient: JsonNominatimClient? = null + @Inject + lateinit var okHttpClient: OkHttpClient + + var nominatimClient: TalkJsonNominatimClient? = null var roomToken: String? @@ -425,13 +422,9 @@ class LocationPickerController(args: Bundle) : } private fun initGeocoder() { - val registry = SchemeRegistry() - registry.register(Scheme("https", SSLSocketFactory.getSocketFactory(), HTTPS_PORT)) - val connexionManager: ClientConnectionManager = SingleClientConnManager(null, registry) - val httpClient: HttpClient = DefaultHttpClient(connexionManager, null) val baseUrl = context!!.getString(R.string.osm_geocoder_url) val email = context!!.getString(R.string.osm_geocoder_contact) - nominatimClient = JsonNominatimClient(baseUrl, httpClient, email) + nominatimClient = TalkJsonNominatimClient(baseUrl, okHttpClient, email) } private fun searchPlaceNameForCoordinates(lat: Double, lon: Double): Boolean { @@ -483,6 +476,5 @@ class LocationPickerController(args: Bundle) : private const val ZOOM_LEVEL_RECEIVED_RESULT: Double = 14.0 private const val ZOOM_LEVEL_DEFAULT: Double = 14.0 private const val GEOCODE_ZERO: Double = 0.0 - private const val HTTPS_PORT: Int = 443 } } diff --git a/app/src/main/java/fr/dudie/nominatim/client/TalkJsonNominatimClient.java b/app/src/main/java/fr/dudie/nominatim/client/TalkJsonNominatimClient.java new file mode 100644 index 000000000..f898d7112 --- /dev/null +++ b/app/src/main/java/fr/dudie/nominatim/client/TalkJsonNominatimClient.java @@ -0,0 +1,338 @@ +package fr.dudie.nominatim.client; + +/* + * [license] + * Nominatim Java API client + * ~~~~ + * Copyright (C) 2010 - 2014 Dudie + * ~~~~ + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * [/license] + */ + +import android.util.Log; + +import com.github.filosganga.geogson.gson.GeometryAdapterFactory; +import com.github.filosganga.geogson.jts.JtsAdapterFactory; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +import fr.dudie.nominatim.client.request.NominatimLookupRequest; +import fr.dudie.nominatim.client.request.NominatimReverseRequest; +import fr.dudie.nominatim.client.request.NominatimSearchRequest; +import fr.dudie.nominatim.client.request.paramhelper.OsmType; +import fr.dudie.nominatim.gson.ArrayOfAddressElementsDeserializer; +import fr.dudie.nominatim.gson.ArrayOfPolygonPointsDeserializer; +import fr.dudie.nominatim.gson.BoundingBoxDeserializer; +import fr.dudie.nominatim.gson.PolygonPointDeserializer; +import fr.dudie.nominatim.model.Address; +import fr.dudie.nominatim.model.BoundingBox; +import fr.dudie.nominatim.model.Element; +import fr.dudie.nominatim.model.PolygonPoint; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** + * An implementation of the Nominatim Api Service. + * + * @author Jérémie Huchet + * @author Sunil D S + * @author Andy Scherzinger + */ +public final class TalkJsonNominatimClient implements NominatimClient { + private static final String TAG = "TalkNominationClient"; + + /** + * The default nominatim base URL. + */ + private static final String DEFAULT_BASE_URL = "https://nominatim.openstreetmap.org/"; + + /** + * UTF-8 encoding. + */ + public static final String ENCODING_UTF_8 = "UTF-8"; + + private final OkHttpClient httpClient; + + /** + * Gson instance for Nominatim API calls. + */ + private final Gson gson; + + /** + * The url to make search queries. + */ + private final String searchUrl; + + /** + * The url for reverse geocoding. + */ + private final String reverseUrl; + + /** + * The url for address lookup. + */ + private final String lookupUrl; + + /** + * The default search options. + */ + private final NominatimOptions defaults; + + /** + * Creates the json nominatim client with the default base URL ({@value #DEFAULT_BASE_URL}. + * + * @param httpClient an HTTP client + * @param email an email to add in the HTTP requests parameters to "sign" them + */ + public TalkJsonNominatimClient(final OkHttpClient httpClient, final String email) { + this(DEFAULT_BASE_URL, httpClient, email, new NominatimOptions()); + } + + /** + * Creates the json nominatim client with the default base URL ({@value #DEFAULT_BASE_URL}. + * + * @param httpClient an HTTP client + * @param email an email to add in the HTTP requests parameters to "sign" them + * @param defaults defaults options, they override null valued requests options + */ + public TalkJsonNominatimClient(final OkHttpClient httpClient, final String email, final NominatimOptions defaults) { + this(DEFAULT_BASE_URL, httpClient, email, defaults); + } + + /** + * Creates the json nominatim client. + * + * @param baseUrl the nominatim server url + * @param httpClient an HTTP client + * @param email an email to add in the HTTP requests parameters to "sign" them (see + * https://wiki.openstreetmap.org/wiki/Nominatim_usage_policy) + */ + public TalkJsonNominatimClient(final String baseUrl, final OkHttpClient httpClient, final String email) { + this(baseUrl, httpClient, email, new NominatimOptions()); + } + + /** + * Creates the json nominatim client. + * + * @param baseUrl the nominatim server url + * @param httpClient an HTTP client + * @param email an email to add in the HTTP requests parameters to "sign" them (see + * https://wiki.openstreetmap.org/wiki/Nominatim_usage_policy) + * @param defaults defaults options, they override null valued requests options + */ + public TalkJsonNominatimClient(final String baseUrl, final OkHttpClient httpClient, final String email, final NominatimOptions defaults) { + String emailEncoded; + try { + emailEncoded = URLEncoder.encode(email, ENCODING_UTF_8); + } catch (UnsupportedEncodingException e) { + emailEncoded = email; + } + this.searchUrl = String.format("%s/search?format=jsonv2&email=%s", baseUrl.replaceAll("/$", ""), emailEncoded); + this.reverseUrl = String.format("%s/reverse?format=jsonv2&email=%s", baseUrl.replaceAll("/$", ""), emailEncoded); + this.lookupUrl = String.format("%s/lookup?format=json&email=%s", baseUrl.replaceAll("/$", ""), emailEncoded); + + Log.d(TAG, "API search URL: " + searchUrl); + Log.d(TAG, "API reverse URL: " + reverseUrl); + + this.defaults = defaults; + + // prepare gson instance + final GsonBuilder gsonBuilder = new GsonBuilder(); + + gsonBuilder.registerTypeAdapter(Element[].class, new ArrayOfAddressElementsDeserializer()); + gsonBuilder.registerTypeAdapter(PolygonPoint.class, new PolygonPointDeserializer()); + gsonBuilder.registerTypeAdapter(PolygonPoint[].class, new ArrayOfPolygonPointsDeserializer()); + gsonBuilder.registerTypeAdapter(BoundingBox.class, new BoundingBoxDeserializer()); + + gsonBuilder.registerTypeAdapterFactory(new JtsAdapterFactory()); + gsonBuilder.registerTypeAdapterFactory(new GeometryAdapterFactory()); + + gson = gsonBuilder.create(); + + // prepare httpclient + this.httpClient = httpClient; + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#search(fr.dudie.nominatim.client.request.NominatimSearchRequest) + */ + @Override + public List
search(final NominatimSearchRequest search) throws IOException { + + defaults.mergeTo(search); + final String apiCall = String.format("%s&%s", searchUrl, search.getQueryString()); + Log.d(TAG, "search url: " + apiCall); + + Request requesthttp = new Request.Builder() + .addHeader("accept", "application/json") + .url(apiCall) + .build(); + + Response response = httpClient.newCall(requesthttp).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + return gson.fromJson(responseBody.string(), new TypeToken>() { + }.getType()); + } + } + + return new ArrayList<>(); + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#getAddress(fr.dudie.nominatim.client.request.NominatimReverseRequest) + */ + @Override + public Address getAddress(final NominatimReverseRequest reverse) throws IOException { + + final String apiCall = String.format("%s&%s", reverseUrl, reverse.getQueryString()); + Log.d(TAG, "reverse geocoding url: " + apiCall); + + Request requesthttp = new Request.Builder() + .addHeader("accept", "application/json") + .url(apiCall) + .build(); + + Response response = httpClient.newCall(requesthttp).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + return gson.fromJson(responseBody.string(), Address.class); + } + } + + return null; + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#lookupAddress(fr.dudie.nominatim.client.request.NominatimLookupRequest) + */ + @Override + public List
lookupAddress(final NominatimLookupRequest lookup) throws IOException { + + final String apiCall = String.format("%s&%s", lookupUrl, lookup.getQueryString()); + Log.d(TAG, "lookup url: " + apiCall); + Request requesthttp = new Request.Builder() + .addHeader("accept", "application/json") + .url(apiCall) + .build(); + + Response response = httpClient.newCall(requesthttp).execute(); + if (response.isSuccessful()) { + ResponseBody responseBody = response.body(); + if (responseBody != null) { + return gson.fromJson(responseBody.string(), new TypeToken>() { + }.getType()); + } + } + + return new ArrayList<>(); + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#search(java.lang.String) + */ + @Override + public List
search(final String query) throws IOException { + + final NominatimSearchRequest q = new NominatimSearchRequest(); + q.setQuery(query); + return this.search(q); + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#getAddress(double, double) + */ + @Override + public Address getAddress(final double longitude, final double latitude) throws IOException { + + final NominatimReverseRequest q = new NominatimReverseRequest(); + q.setQuery(longitude, latitude); + return this.getAddress(q); + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#getAddress(double, double, int) + */ + @Override + public Address getAddress(final double longitude, final double latitude, final int zoom) + throws IOException { + + final NominatimReverseRequest q = new NominatimReverseRequest(); + q.setQuery(longitude, latitude); + q.setZoom(zoom); + return this.getAddress(q); + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#getAddress(int, int) + */ + @Override + public Address getAddress(final int longitudeE6, final int latitudeE6) throws IOException { + + return this.getAddress((double) (longitudeE6 / 1E6), (double) (latitudeE6 / 1E6)); + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#getAddress(String, long) + */ + @Override + public Address getAddress(final String type, final long id) throws IOException { + + final NominatimReverseRequest q = new NominatimReverseRequest(); + q.setQuery(OsmType.from(type), id); + return this.getAddress(q); + } + + /** + * {@inheritDoc} + * + * @see fr.dudie.nominatim.client.NominatimClient#lookupAddress(java.util.List) + */ + @Override + public List
lookupAddress(final List typeId) throws IOException { + + final NominatimLookupRequest q = new NominatimLookupRequest(); + q.setQuery(typeId); + return this.lookupAddress(q); + } +}