diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f32e1c3ab..153895e6e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -95,6 +95,7 @@ android { resValue("string", "mapbox_key", tasks_mapbox_key_debug ?: "") resValue("string", "google_key", tasks_google_key_debug ?: "") resValue("string", "tasks_caldav_url", tasks_caldav_url ?: "https://caldav.tasks.org") + resValue("string", "tasks_nominatim_url", tasks_caldav_url ?: "https://nominatim.tasks.org") isTestCoverageEnabled = project.hasProperty("coverage") } getByName("release") { diff --git a/app/src/main/java/org/tasks/http/HttpClientFactory.kt b/app/src/main/java/org/tasks/http/HttpClientFactory.kt new file mode 100644 index 000000000..c2c81a3d6 --- /dev/null +++ b/app/src/main/java/org/tasks/http/HttpClientFactory.kt @@ -0,0 +1,42 @@ +package org.tasks.http + +import android.content.Context +import at.bitfire.cert4android.CustomCertManager +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import okhttp3.internal.tls.OkHostnameVerifier +import org.tasks.DebugNetworkInterceptor +import org.tasks.preferences.Preferences +import javax.inject.Inject +import javax.net.ssl.SSLContext + +class HttpClientFactory @Inject constructor( + @ApplicationContext private val context: Context, + private val preferences: Preferences, + private val interceptor: DebugNetworkInterceptor, +) { + + suspend fun newCertManager() = withContext(Dispatchers.Default) { + CustomCertManager(context) + } + + suspend fun newBuilder(foreground: Boolean = false): OkHttpClient.Builder = + newBuilder(newCertManager(), foreground) + + fun newBuilder(customCertManager: CustomCertManager, foreground: Boolean = false): OkHttpClient.Builder { + customCertManager.appInForeground = foreground + val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier) + val sslContext = SSLContext.getInstance("TLS") + sslContext.init(null, arrayOf(customCertManager), null) + val builder = OkHttpClient() + .newBuilder() + .sslSocketFactory(sslContext.socketFactory, customCertManager) + .hostnameVerifier(hostnameVerifier) + if (preferences.isFlipperEnabled) { + interceptor.apply(builder) + } + return builder + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/http/HttpException.kt b/app/src/main/java/org/tasks/http/HttpException.kt new file mode 100644 index 000000000..42ba65c48 --- /dev/null +++ b/app/src/main/java/org/tasks/http/HttpException.kt @@ -0,0 +1,5 @@ +package org.tasks.http + +import java.io.IOException + +class HttpException(code: Int, message: String) : IOException("$code $message") \ No newline at end of file diff --git a/app/src/main/java/org/tasks/location/GeocoderNominatim.kt b/app/src/main/java/org/tasks/location/GeocoderNominatim.kt index f1708e21e..7b9f3f328 100644 --- a/app/src/main/java/org/tasks/location/GeocoderNominatim.kt +++ b/app/src/main/java/org/tasks/location/GeocoderNominatim.kt @@ -1,38 +1,37 @@ 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.OkHttpClient import okhttp3.Request import org.tasks.BuildConfig -import org.tasks.DebugNetworkInterceptor +import org.tasks.R import org.tasks.data.Place import org.tasks.data.Place.Companion.newPlace -import org.tasks.preferences.Preferences -import java.io.IOException +import org.tasks.http.HttpClientFactory +import org.tasks.http.HttpException import javax.inject.Inject class GeocoderNominatim @Inject constructor( - private val preferences: Preferences, - private val interceptor: DebugNetworkInterceptor, + @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 builder = OkHttpClient().newBuilder() - if (preferences.isFlipperEnabled) { - interceptor.apply(builder) - } - val client = builder.build() - val url = "https://nominatim.openstreetmap.org/reverse?format=geocodejson&lat=${mapPosition.latitude}&lon=${mapPosition.longitude}" + val client = httpClientFactory.newBuilder(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 IOException("${response.code} ${response.message}") + throw HttpException(response.code, response.message) } } diff --git a/app/src/main/java/org/tasks/location/LocationPickerActivity.kt b/app/src/main/java/org/tasks/location/LocationPickerActivity.kt index 3cf72c1f2..307b4e43a 100644 --- a/app/src/main/java/org/tasks/location/LocationPickerActivity.kt +++ b/app/src/main/java/org/tasks/location/LocationPickerActivity.kt @@ -34,6 +34,7 @@ import org.tasks.PermissionUtil.verifyPermissions import org.tasks.R import org.tasks.Strings.isNullOrEmpty import org.tasks.activities.PlaceSettingsActivity +import org.tasks.analytics.Firebase import org.tasks.billing.Inventory import org.tasks.caldav.GeoUtils.toLikeString import org.tasks.data.LocationDao @@ -93,6 +94,7 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC @Inject lateinit var inventory: Inventory @Inject lateinit var colorProvider: ColorProvider @Inject lateinit var locationProvider: LocationProvider + @Inject lateinit var firebase: Firebase private var disposables: CompositeDisposable? = null private var mapPosition: MapPosition? = null @@ -232,10 +234,14 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC fun selectLocation() { val mapPosition = map.mapPosition ?: return loadingIndicator.visibility = View.VISIBLE - lifecycleScope.launch { - val place = geocoder.reverseGeocode(mapPosition) ?: newPlace(mapPosition) + try { + lifecycleScope.launch { + returnPlace(geocoder.reverseGeocode(mapPosition) ?: newPlace(mapPosition)) + } + } catch (e: Exception) { loadingIndicator.visibility = View.GONE - returnPlace(place) + firebase.reportException(e) + toaster.longToast(e.message) } } diff --git a/app/src/release/res/values/keys.xml b/app/src/release/res/values/keys.xml index dfdae5954..fa04b6d97 100644 --- a/app/src/release/res/values/keys.xml +++ b/app/src/release/res/values/keys.xml @@ -2,4 +2,5 @@ Tasks https://caldav.tasks.org + https://nominatim.tasks.org \ No newline at end of file