From 0e728152c952108bf9be6d443ff38aa1763af86b Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Tue, 16 Feb 2021 17:32:25 -0600 Subject: [PATCH] Replace Places SDK with HTTP API --- app/build.gradle.kts | 2 +- .../org/tasks/injection/LocationModule.kt | 14 +- .../org/tasks/injection/LocationModule.kt | 24 +- .../org/tasks/location/PlaceSearchGoogle.kt | 128 ---------- .../java/org/tasks/billing/PurchaseDialog.kt | 2 +- .../java/org/tasks/http/HttpClientFactory.kt | 27 ++- .../org/tasks/injection/ViewModelModule.kt | 24 ++ .../org/tasks/location/GeocoderNominatim.kt | 2 +- .../tasks/location/LocationPickerActivity.kt | 2 +- .../org/tasks/location/PlaceSearchGoogle.kt | 146 ++++++++++++ .../java/org/tasks/preferences/Preferences.kt | 2 - .../fragments/LocationPreferences.kt | 23 +- .../powered_by_google_on_non_white.png | Bin 0 -> 2282 bytes .../powered_by_google_on_white.png | Bin 0 -> 3331 bytes .../powered_by_google_on_non_white.png | Bin 0 -> 1127 bytes .../powered_by_google_on_white.png | Bin 0 -> 1619 bytes .../powered_by_google_on_non_white.png | Bin 0 -> 1404 bytes .../powered_by_google_on_white.png | Bin 0 -> 2133 bytes .../powered_by_google_on_non_white.png | Bin 0 -> 3126 bytes .../powered_by_google_on_white.png | Bin 0 -> 4550 bytes .../powered_by_google_on_non_white.png | Bin 0 -> 4800 bytes .../powered_by_google_on_white.png | Bin 0 -> 6898 bytes .../powered_by_google_on_non_white.png | Bin 0 -> 6115 bytes .../powered_by_google_on_white.png | Bin 0 -> 9314 bytes app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-bg-rBG/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-da/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-eu/strings.xml | 1 - app/src/main/res/values-fi/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-id/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-iw/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-lt/strings.xml | 1 - app/src/main/res/values-nb/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sk/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-ta/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/arrays.xml | 10 - app/src/main/res/values/keys.xml | 2 - app/src/main/res/values/strings.xml | 1 - app/src/main/res/xml/preferences_location.xml | 8 - app/src/release/res/values/keys.xml | 1 + .../tasks/location/PlaceSearchGoogleTest.kt | 49 ++++ .../test/resources/google_places/fetch.json | 17 ++ .../test/resources/google_places/search.json | 225 ++++++++++++++++++ deps_googleplay.txt | 81 +++---- 62 files changed, 529 insertions(+), 290 deletions(-) delete mode 100644 app/src/googleplay/java/org/tasks/location/PlaceSearchGoogle.kt create mode 100644 app/src/main/java/org/tasks/injection/ViewModelModule.kt create mode 100644 app/src/main/java/org/tasks/location/PlaceSearchGoogle.kt create mode 100644 app/src/main/res/drawable-hdpi/powered_by_google_on_non_white.png create mode 100644 app/src/main/res/drawable-hdpi/powered_by_google_on_white.png create mode 100644 app/src/main/res/drawable-ldpi/powered_by_google_on_non_white.png create mode 100644 app/src/main/res/drawable-ldpi/powered_by_google_on_white.png create mode 100644 app/src/main/res/drawable-mdpi/powered_by_google_on_non_white.png create mode 100644 app/src/main/res/drawable-mdpi/powered_by_google_on_white.png create mode 100644 app/src/main/res/drawable-xhdpi/powered_by_google_on_non_white.png create mode 100644 app/src/main/res/drawable-xhdpi/powered_by_google_on_white.png create mode 100644 app/src/main/res/drawable-xxhdpi/powered_by_google_on_non_white.png create mode 100644 app/src/main/res/drawable-xxhdpi/powered_by_google_on_white.png create mode 100644 app/src/main/res/drawable-xxxhdpi/powered_by_google_on_non_white.png create mode 100644 app/src/main/res/drawable-xxxhdpi/powered_by_google_on_white.png create mode 100644 app/src/test/java/org/tasks/location/PlaceSearchGoogleTest.kt create mode 100644 app/src/test/resources/google_places/fetch.json create mode 100644 app/src/test/resources/google_places/search.json diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 067ca175c..031320e6a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -96,6 +96,7 @@ android { 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") + resValue("string", "tasks_places_url", tasks_caldav_url ?: "https://places.tasks.org") isTestCoverageEnabled = project.hasProperty("coverage") } getByName("release") { @@ -215,7 +216,6 @@ dependencies { googleplayImplementation("com.google.firebase:firebase-config-ktx:${Versions.remote_config}") googleplayImplementation("com.google.android.gms:play-services-location:17.1.0") googleplayImplementation("com.google.android.gms:play-services-maps:17.0.0") - googleplayImplementation("com.google.android.libraries.places:places:2.4.0") googleplayImplementation("com.android.billingclient:billing:1.2.2") androidTestImplementation("com.google.dagger:hilt-android-testing:${Versions.hilt}") diff --git a/app/src/generic/java/org/tasks/injection/LocationModule.kt b/app/src/generic/java/org/tasks/injection/LocationModule.kt index fab0662b1..86766fb87 100644 --- a/app/src/generic/java/org/tasks/injection/LocationModule.kt +++ b/app/src/generic/java/org/tasks/injection/LocationModule.kt @@ -5,20 +5,16 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityComponent -import dagger.hilt.android.components.ViewModelComponent import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityScoped -import dagger.hilt.android.scopes.ViewModelScoped -import org.tasks.location.* +import org.tasks.location.AndroidLocationProvider +import org.tasks.location.LocationProvider +import org.tasks.location.MapFragment +import org.tasks.location.OsmMapFragment @Module -@InstallIn(ActivityComponent::class, ViewModelComponent::class) +@InstallIn(ActivityComponent::class) class LocationModule { - @Provides - @ViewModelScoped - fun getPlaceSearchProvider(mapboxSearchProvider: PlaceSearchMapbox): PlaceSearch = - mapboxSearchProvider - @Provides @ActivityScoped fun getLocationProvider(provider: AndroidLocationProvider): LocationProvider = provider diff --git a/app/src/googleplay/java/org/tasks/injection/LocationModule.kt b/app/src/googleplay/java/org/tasks/injection/LocationModule.kt index 9ae25cd1a..c3efa3d02 100644 --- a/app/src/googleplay/java/org/tasks/injection/LocationModule.kt +++ b/app/src/googleplay/java/org/tasks/injection/LocationModule.kt @@ -5,37 +5,15 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityComponent -import dagger.hilt.android.components.ViewModelComponent import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ActivityScoped -import dagger.hilt.android.scopes.ViewModelScoped import org.tasks.R -import org.tasks.billing.Inventory -import org.tasks.gtasks.PlayServices import org.tasks.location.* import org.tasks.preferences.Preferences @Module -@InstallIn(ActivityComponent::class, ViewModelComponent::class) +@InstallIn(ActivityComponent::class) internal class LocationModule { - @Provides - @ViewModelScoped - fun getPlaceSearchProvider( - @ApplicationContext context: Context, - preferences: Preferences, - playServices: PlayServices, - inventory: Inventory, - mapboxSearchProvider: MapboxSearchProvider - ): PlaceSearchProvider { - return if (preferences.useGooglePlaces() - && playServices.isPlayServicesAvailable - && inventory.hasPro) { - PlaceSearchGoogle(context) - } else { - mapboxSearchProvider - } - } - @Provides @ActivityScoped fun getLocationProvider(@ApplicationContext context: Context): LocationProvider = diff --git a/app/src/googleplay/java/org/tasks/location/PlaceSearchGoogle.kt b/app/src/googleplay/java/org/tasks/location/PlaceSearchGoogle.kt deleted file mode 100644 index 17f65cdac..000000000 --- a/app/src/googleplay/java/org/tasks/location/PlaceSearchGoogle.kt +++ /dev/null @@ -1,128 +0,0 @@ -package org.tasks.location - -import android.content.Context -import android.os.Bundle -import com.google.android.gms.maps.model.LatLng -import com.google.android.gms.maps.model.LatLngBounds -import com.google.android.libraries.places.api.Places -import com.google.android.libraries.places.api.model.AutocompletePrediction -import com.google.android.libraries.places.api.model.AutocompleteSessionToken -import com.google.android.libraries.places.api.model.Place.Field -import com.google.android.libraries.places.api.model.RectangularBounds -import com.google.android.libraries.places.api.net.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import org.tasks.R -import org.tasks.data.Place -import org.tasks.data.Place.Companion.newPlace -import kotlin.coroutines.suspendCoroutine - -class PlaceSearchGoogle(private val context: Context) : PlaceSearch { - private var token: AutocompleteSessionToken? = null - private var placesClient: PlacesClient? = null - - override fun restoreState(savedInstanceState: Bundle?) { - token = savedInstanceState?.getParcelable(EXTRA_SESSION_TOKEN) - } - - override fun saveState(outState: Bundle) { - outState.putParcelable(EXTRA_SESSION_TOKEN, token) - } - - override fun getAttributionRes(dark: Boolean): Int { - return if (dark) R.drawable.places_powered_by_google_dark else R.drawable.places_powered_by_google_light - } - - override suspend fun search(query: String, bias: MapPosition?): List = - withContext(Dispatchers.IO) { - suspendCoroutine { cont -> - if (!Places.isInitialized()) { - Places.initialize(context, context.getString(R.string.google_key)) - } - if (placesClient == null) { - placesClient = Places.createClient(context) - } - if (token == null) { - token = AutocompleteSessionToken.newInstance() - } - val request = FindAutocompletePredictionsRequest.builder().setSessionToken(token).setQuery(query) - if (bias != null) { - request.locationBias = - RectangularBounds.newInstance( - LatLngBounds.builder() - .include(LatLng(bias.latitude, bias.longitude)) - .build()) - } - placesClient!! - .findAutocompletePredictions(request.build()) - .addOnSuccessListener { response: FindAutocompletePredictionsResponse -> - val places = toSearchResults(response.autocompletePredictions) - cont.resumeWith(Result.success(places)) - } - .addOnFailureListener { e: Exception -> - cont.resumeWith(Result.failure(e)) - } - } - } - - override suspend fun fetch(placeSearchResult: PlaceSearchResult): Place = - withContext(Dispatchers.IO) { - suspendCoroutine { cont -> - placesClient!! - .fetchPlace( - FetchPlaceRequest.builder( - placeSearchResult.id, - listOf( - Field.ID, - Field.LAT_LNG, - Field.ADDRESS, - Field.WEBSITE_URI, - Field.NAME, - Field.PHONE_NUMBER)) - .setSessionToken(token) - .build()) - .addOnSuccessListener { result: FetchPlaceResponse -> - cont.resumeWith(Result.success(toPlace(result))) - } - .addOnFailureListener { e: Exception -> - cont.resumeWith(Result.failure(e)) - } - } - } - - - private fun toSearchResults(predictions: List): List { - return predictions.map { - PlaceSearchResult( - it.placeId, - it.getPrimaryText(null).toString(), - it.getSecondaryText(null).toString()) - } - } - - private fun toPlace(fetchPlaceResponse: FetchPlaceResponse): Place { - val place = fetchPlaceResponse.place - val result = newPlace() - result.name = place.name - val address: CharSequence? = place.address - if (address != null) { - result.address = place.address - } - val phoneNumber: CharSequence? = place.phoneNumber - if (phoneNumber != null) { - result.phone = phoneNumber.toString() - } - val uri = place.websiteUri - if (uri != null) { - result.url = uri.toString() - } - val latLng = place.latLng - result.latitude = latLng!!.latitude - result.longitude = latLng.longitude - return result - } - - companion object { - private const val EXTRA_SESSION_TOKEN = "extra_session_token" - } -} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/billing/PurchaseDialog.kt b/app/src/main/java/org/tasks/billing/PurchaseDialog.kt index 4c0345252..1fe924b00 100644 --- a/app/src/main/java/org/tasks/billing/PurchaseDialog.kt +++ b/app/src/main/java/org/tasks/billing/PurchaseDialog.kt @@ -140,6 +140,7 @@ _${getString(R.string.account_not_included)}_ #### ${getString(R.string.tasks_org_account)} * ${getString(R.string.tasks_org_description)} * [${getString(R.string.upgrade_third_party_apps)}](${getString(R.string.url_app_passwords)}) +* ${getString(R.string.upgrade_google_places)} * [${getString(R.string.upgrade_coming_soon)}](${getString(R.string.help_url_sync)}) """ } @@ -155,7 +156,6 @@ _${getString(R.string.account_not_included)}_ --- #### ${getString(R.string.upgrade_additional_features)} * ${getString(R.string.upgrade_themes)} -* ${getString(R.string.upgrade_google_places)} * [${getString(R.string.upgrade_tasker)}](${getString(R.string.url_tasker)}) --- * ${getString(R.string.upgrade_free_trial)} diff --git a/app/src/main/java/org/tasks/http/HttpClientFactory.kt b/app/src/main/java/org/tasks/http/HttpClientFactory.kt index c2c81a3d6..823e610b8 100644 --- a/app/src/main/java/org/tasks/http/HttpClientFactory.kt +++ b/app/src/main/java/org/tasks/http/HttpClientFactory.kt @@ -2,6 +2,7 @@ package org.tasks.http import android.content.Context import at.bitfire.cert4android.CustomCertManager +import at.bitfire.dav4jvm.BasicDigestAuthHandler import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -9,6 +10,7 @@ import okhttp3.OkHttpClient import okhttp3.internal.tls.OkHostnameVerifier import org.tasks.DebugNetworkInterceptor import org.tasks.preferences.Preferences +import org.tasks.security.KeyStoreEncryption import javax.inject.Inject import javax.net.ssl.SSLContext @@ -16,16 +18,30 @@ class HttpClientFactory @Inject constructor( @ApplicationContext private val context: Context, private val preferences: Preferences, private val interceptor: DebugNetworkInterceptor, + private val encryption: KeyStoreEncryption, ) { suspend fun newCertManager() = withContext(Dispatchers.Default) { CustomCertManager(context) } - suspend fun newBuilder(foreground: Boolean = false): OkHttpClient.Builder = - newBuilder(newCertManager(), foreground) + suspend fun newBuilder( + foreground: Boolean = false, + username: String? = null, + encryptedPassword: String? = null + ): OkHttpClient.Builder = newBuilder( + newCertManager(), + foreground = foreground, + username = username, + password = encryptedPassword?.let { encryption.decrypt(it) } + ) - fun newBuilder(customCertManager: CustomCertManager, foreground: Boolean = false): OkHttpClient.Builder { + fun newBuilder( + customCertManager: CustomCertManager, + foreground: Boolean = false, + username: String? = null, + password: String? = null + ): OkHttpClient.Builder { customCertManager.appInForeground = foreground val hostnameVerifier = customCertManager.hostnameVerifier(OkHostnameVerifier) val sslContext = SSLContext.getInstance("TLS") @@ -37,6 +53,11 @@ class HttpClientFactory @Inject constructor( if (preferences.isFlipperEnabled) { interceptor.apply(builder) } + if (!username.isNullOrBlank() && !password.isNullOrBlank()) { + val auth = BasicDigestAuthHandler(null, username, password) + builder.addNetworkInterceptor(auth) + builder.authenticator(auth) + } return builder } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/injection/ViewModelModule.kt b/app/src/main/java/org/tasks/injection/ViewModelModule.kt new file mode 100644 index 000000000..3b4bfa68f --- /dev/null +++ b/app/src/main/java/org/tasks/injection/ViewModelModule.kt @@ -0,0 +1,24 @@ +package org.tasks.injection + +import dagger.Lazy +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ViewModelComponent +import dagger.hilt.android.scopes.ViewModelScoped +import org.tasks.billing.Inventory +import org.tasks.location.PlaceSearch +import org.tasks.location.PlaceSearchGoogle +import org.tasks.location.PlaceSearchMapbox + +@Module +@InstallIn(ViewModelComponent::class) +class ViewModelModule { + @Provides + @ViewModelScoped + fun getPlaceSearchProvider( + inventory: Inventory, + google: Lazy, + mapbox: Lazy + ): PlaceSearch = if (inventory.hasTasksSubscription) google.get() else mapbox.get() +} \ 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 7b9f3f328..b30d5bd12 100644 --- a/app/src/main/java/org/tasks/location/GeocoderNominatim.kt +++ b/app/src/main/java/org/tasks/location/GeocoderNominatim.kt @@ -23,7 +23,7 @@ class GeocoderNominatim @Inject constructor( override suspend fun reverseGeocode(mapPosition: MapPosition): Place? = withContext(Dispatchers.IO) { - val client = httpClientFactory.newBuilder(true).build() + 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() diff --git a/app/src/main/java/org/tasks/location/LocationPickerActivity.kt b/app/src/main/java/org/tasks/location/LocationPickerActivity.kt index 526f2b0c7..f112d75e4 100644 --- a/app/src/main/java/org/tasks/location/LocationPickerActivity.kt +++ b/app/src/main/java/org/tasks/location/LocationPickerActivity.kt @@ -303,7 +303,7 @@ class LocationPickerActivity : InjectingAppCompatActivity(), Toolbar.OnMenuItemC searchSubject .debounce(SEARCH_DEBOUNCE_TIMEOUT.toLong(), TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) - .subscribe { query: String? -> viewModel.query(query, mapPosition) }) + .subscribe { query: String? -> viewModel.query(query, map.mapPosition) }) } override fun onDestroy() { diff --git a/app/src/main/java/org/tasks/location/PlaceSearchGoogle.kt b/app/src/main/java/org/tasks/location/PlaceSearchGoogle.kt new file mode 100644 index 000000000..83d76ea20 --- /dev/null +++ b/app/src/main/java/org/tasks/location/PlaceSearchGoogle.kt @@ -0,0 +1,146 @@ +package org.tasks.location + +import android.content.Context +import android.os.Bundle +import com.google.gson.JsonObject +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.R +import org.tasks.data.CaldavAccount.Companion.TYPE_TASKS +import org.tasks.data.CaldavDao +import org.tasks.data.Place +import org.tasks.data.Place.Companion.newPlace +import org.tasks.http.HttpClientFactory +import org.tasks.http.HttpException +import timber.log.Timber +import java.util.* +import javax.inject.Inject + +class PlaceSearchGoogle @Inject constructor( + @ApplicationContext private val context: Context, + private val httpClientFactory: HttpClientFactory, + private val caldavDao: CaldavDao +) : PlaceSearch { + private val url = context.getString(R.string.tasks_places_url) + private var token: String? = null + + override fun restoreState(savedInstanceState: Bundle?) { + token = savedInstanceState?.getParcelable(EXTRA_SESSION_TOKEN) + } + + override fun saveState(outState: Bundle) { + outState.putString(EXTRA_SESSION_TOKEN, token) + } + + override fun getAttributionRes(dark: Boolean) = if (dark) { + R.drawable.powered_by_google_on_non_white + } else { + R.drawable.powered_by_google_on_white + } + + override suspend fun search(query: String, bias: MapPosition?): List { + if (token == null) { + token = UUID.randomUUID().toString() + } + val proximity = bias?.let { + "&location=${bias.latitude},${bias.longitude}&radius=25000" + } + val jsonObject = execute( + "${this.url}/maps/api/place/queryautocomplete/json?input=$query&sessiontoken=$token$proximity" + ) + return toSearchResults(jsonObject) + } + + override suspend fun fetch(placeSearchResult: PlaceSearchResult): Place { + val jsonObject = execute( + "${this.url}/maps/api/place/details/json?place_id=${placeSearchResult.id}&fields=$FIELDS&sessiontoken=$token" + ) + return toPlace(jsonObject) + } + + private suspend fun execute(url: String): JsonObject = withContext(Dispatchers.IO) { + Timber.d(url) + val account = caldavDao.getAccounts(TYPE_TASKS).firstOrNull() + ?: throw IllegalStateException( + context.getString(R.string.tasks_org_account_required) + ) + val client = httpClientFactory + .newBuilder( + foreground = true, + username = account.username, + encryptedPassword = account.password + ) + .build() + val response = client.newCall(Request.Builder().get().url(url).build()).execute() + if (response.isSuccessful) { + response.body?.string()?.toJson()?.apply { checkResult(this) } + ?: throw IllegalStateException("Request failed") + } else { + throw HttpException(response.code, response.message) + } + } + + companion object { + private const val EXTRA_SESSION_TOKEN = "extra_session_token" + private val FIELDS = + listOf( + "place_id", + "geometry/location", + "formatted_address", + "website", + "name", + "international_phone_number" + ).joinToString(",") + + internal fun String.toJson(): JsonObject = JsonParser.parseString(this).asJsonObject + + private fun checkResult(json: JsonObject) { + val status = json.get("status").asString + when { + status == "OK" -> return + json.has("error_message") -> + throw IllegalStateException(json.get("error_message").asString) + else -> + throw IllegalStateException(status) + } + } + + internal fun toSearchResults(json: JsonObject): List = + json.get("predictions") + .asJsonArray + .map { it.asJsonObject } + .filter { it.has("place_id") } + .map { toSearchEntry(it) } + + private fun toSearchEntry(json: JsonObject): PlaceSearchResult { + val place = json.get("structured_formatting").asJsonObject + return PlaceSearchResult( + json.get("place_id").asString, + place.get("main_text").asString, + place.get("secondary_text").asString + ) + } + + internal fun toPlace(json: JsonObject): Place { + val result = json.get("result").asJsonObject + val location = result.get("geometry").asJsonObject.get("location").asJsonObject + return newPlace().apply { + name = result.get("name").asString + address = result.getString("formatted_address") + phone = result.getString("international_phone_number") + url = result.getString("website") + latitude = location.get("lat").asDouble + longitude = location.get("lng").asDouble + } + } + + private fun JsonObject.getString(field: String): String? = if (has(field)) { + get(field).asString + } else { + null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/tasks/preferences/Preferences.kt b/app/src/main/java/org/tasks/preferences/Preferences.kt index 3340cae52..e93796452 100644 --- a/app/src/main/java/org/tasks/preferences/Preferences.kt +++ b/app/src/main/java/org/tasks/preferences/Preferences.kt @@ -423,8 +423,6 @@ class Preferences @JvmOverloads constructor( syncFlags.forEach { setBoolean(it, value) } } - fun useGooglePlaces(): Boolean = getIntegerFromString(R.string.p_place_provider, 0) == 1 - fun getPrefs(c: Class): Map { val result: MutableMap = HashMap() val entries: Iterable> = prefs.all.entries.filter { e: Map.Entry -> c.isInstance(e.value) } diff --git a/app/src/main/java/org/tasks/preferences/fragments/LocationPreferences.kt b/app/src/main/java/org/tasks/preferences/fragments/LocationPreferences.kt index 539d6d028..1570ac9f2 100644 --- a/app/src/main/java/org/tasks/preferences/fragments/LocationPreferences.kt +++ b/app/src/main/java/org/tasks/preferences/fragments/LocationPreferences.kt @@ -35,16 +35,10 @@ class LocationPreferences : InjectingPreferenceFragment() { override suspend fun setupPreferences(savedInstanceState: Bundle?) { if (IS_GOOGLE_PLAY) { - findPreference(R.string.p_place_provider) - .setOnPreferenceChangeListener(this::onPlaceSearchChanged) findPreference(R.string.p_geofence_service) .setOnPreferenceChangeListener(this::onGeofenceServiceChanged) } else { - disable( - R.string.p_map_tiles, - R.string.p_place_provider, - R.string.p_geofence_service - ) + disable(R.string.p_map_tiles, R.string.p_geofence_service) } } @@ -71,21 +65,6 @@ class LocationPreferences : InjectingPreferenceFragment() { findPreference(R.string.p_geofence_service).isEnabled = hasPermissions && IS_GOOGLE_PLAY } - private fun onPlaceSearchChanged(preference: Preference, newValue: Any): Boolean = - if (newValue.toString().toIntOrNull() ?: 0 == 1) { - if (!playServices.refreshAndCheck()) { - playServices.resolve(activity) - false - } else if (!inventory.hasPro) { - toaster.longToast(R.string.requires_pro_subscription) - false - } else { - true - } - } else { - true - } - private fun onGeofenceServiceChanged(preference: Preference, newValue: Any): Boolean = if (newValue.toString().toIntOrNull() ?: 0 == 1) { if (!playServices.refreshAndCheck()) { diff --git a/app/src/main/res/drawable-hdpi/powered_by_google_on_non_white.png b/app/src/main/res/drawable-hdpi/powered_by_google_on_non_white.png new file mode 100644 index 0000000000000000000000000000000000000000..4ea9402516621c40b6584ba0a8e79d914ef47707 GIT binary patch literal 2282 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 N002ovPDHLkV1k8@Xt@9Y literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-ldpi/powered_by_google_on_non_white.png b/app/src/main/res/drawable-ldpi/powered_by_google_on_non_white.png new file mode 100644 index 0000000000000000000000000000000000000000..55888f9146d6f343710255aa1964c12360ffaf46 GIT binary patch literal 1127 zcmV-t1ep7YP)6N3bEIfO}Y# z7)%-Z{AT_jj_b!@oz7KG3;*rFmYSeae!GN)Eu7rRLUAf&smXwYtmMuwo2lfVKMcPC zY;9DS>1Hg~>roKOv&)RBdc@7|n+&xS*S^CXUE|uTTzj#dPZy4Jlny%A#d1)?x^YC$ z>Qee}g2S}5vm(=`LhN?B2Lls;htQo9wnv3sMmE|<#<&a}SkZy`FPmmD#P!9Rj-DWp#I>x!mZ_ zOn0gWL+vKJgYmR-sshJVLQh(l;q=kHVL2I)t7nc0{$V14COH+^ek*k+eR zfhz~*!>AZ3p)aD_n%NOVMk&9}W}gZ(&Z$0DL>D6}DT}hufI|+m0u%iF-e{&$KHh>I zE_afO1fteV)MJKzl&NOj<>sYrKWFyeILBE0jUdeLTpTpG1hP4p0n3hD3V!5Sy zJ?A7a)0nFBl#N9k(R&^^?FOZ>U$5hOEYLlu+4szQ}nW7q4GLCM@79pi;a;N*3}$ikaBv1zRdF@hxAf( zO?8WlrvYEgiRfhMR=dH)`_jZaoyO_};8i-^S8hUo-;QNEljE;UCb|l51S(7&vxs6g z(=Fs9bC{Au_h-GGc@ z!pdT%(jKTJ+mv6~SVf(az^+%FP0FpDtfzusyTdfF?-|M7cwkp=w5!*E!+ONKy=$gV z6E3GKnQz7@DpQ*!X4LS}&-@w9&M-53KZ$;fW6V@(rX-x<_skfoY;*@b>VZ|5atx!M zT@jRfh7Jr-X`L77H?4?oFQbNTj;?{eqz|r`g`WDAz603LSbY?We#v4^ut=Y#qn~6b%W2I-Mn!>x zqq7iLt{;OHR-O$07&%7y{}!Jcl-W}J7GGs;(n-pAU_HiuJO6KS`o@f|mWlpmdM^F6 t*K}uwuJ0APcT*u> zBb04swWoD@wd)V-5}vu~G1u>A5}rmV8(VRc6-oU?9xRT5Z_07NgZ4(`6g*{l`zB1^ z-^NYf?4)IUXTtPf2_+;0h~P3Dj+KbvI7mWx5a-|slwlZ$;ZQt=TZ8#by;a1!TiGg8Ugu`Irg@p>n3t{0fT!>*9I0|u`1j+G; z=N6p$7$rXer9twaI?m>AKuMXDo0$w5AD=Ap^^9A_XXO0}%Uca4WloQIGV2rWk5h2Z zx-rjZwHQ3A<>xC(Y0xJ@gu7b|f9UVtF_A!$Pphu|@kV)24O(TLzUoPZai z66IOkC_yvqoQW5rJP!x%!&T7JP^e%uK+hGpACl7$#Ss|687eF0=(9J<1O=n1TcNT^ z0kWOSq(QN;$%&Bf6;L*&>a`Q5_X#MbHc7#+fU;5SYnkLHK*6a$^ZPym1t6MwTfTyM zkf!*3|KN3sV*f{Oy1cKUG(hLD>~#=+V5EvEQcEoEA)VSnhGS#3^*H0<*f1m%5a>>VJs-gH@pJ8JEaeX**2vkit za{yEQ`vL`jmx8CKEaQgxpT_|3u?wJtUGNC1VW125;UMke6hj&O7{aY^Wo?fy)Gk+x zwJp|82#;bVmf$|zP@rJRb$A%du?!EQM4P%8%3+`vBPdkx2>M{495I{*JD1>vxB_{z zBAPy#5`T#dzC;P5oWn2Y?cX9}M$#{V5^T|RaxQYg`rL$N4Dohi{(Z||4!K~}$(hR} ze{TJldx+PGg)_JuDiD2kF_*gaJWxO4feACc-z{_o%g>abjf8B`y#BCUwjQ^-?Amoa{m6B_iLeSX5^WrOz{`xEN07o`}__PNq-qRw`h5wCPLm{ z$?uYzJMVWw!7i97*}v0=mDnQ^h_5-ziapyWM*}t-1DwldH2Dswt?+!ouvP RLD~QS002ovPDHLkV1hnD3Z4J} literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/powered_by_google_on_non_white.png b/app/src/main/res/drawable-mdpi/powered_by_google_on_non_white.png new file mode 100644 index 0000000000000000000000000000000000000000..abe091388933c83693d8309dd9c0cff2ecd507ad GIT binary patch literal 1404 zcmV-?1%vvDP))&-Gw2b zgoDcK=4y&g@MEUQayN$Az5~qZF7F#`m5jFwhQ$Na3KL}6V!mV|%VC|fJ@R21ItJsq zNG#yN)jjaT3??=bgCqcZBniz-$oVxNdf3`Q39*v0hXCnoL`m3-GZN}JG^bk&;y1=M z6=(IleS`wF_&&2?Kb9(`VJSUsBA=HezHfs8GbsWyv4el|eKe8yhn?#TrZJn|GQPRk z#y^a&xx}B%nAZ@E3H-ufngxG6Kz{V$NBjk=v7dMG3BWIVtogr;6qo*- zJJ%rh!4<=N2hHdWXd8UaptyioA=sXW;qKOa#Jdp1{Gm8LBjF4mivP{Vbmnubq%@w$ z83||`J=!l}fYM3WfhZ88`I908uh@{#qp*Z;O8hi%z~LlnnKaoD#aAUh#}_SpIKtdU zEeXHg((BF-U-iOfS;X+opzjrvdks2=?mh7URSUM4T9D<#67*pS9OF%4kv$~L_uiiQ z3xGpb6lR1fL;pzVm4G%y;ybQZJ`B)@5@Jx#K2UriKqvWt)#AN4iY7{hBk184d|F9g z+qc(P%))9*!#d0ac&}04v3Jfk7K#Jt1BrhHeJ`7F?QoEHy06jcxPY+M*;Bm^vXC8> z1u@u;2I1AB+pt{1E*9oNR#WHuzOZo3^RH^ew&1r0i!V&4Ol^97Hj5O%;((tZj9NQ&T6{d zblqLQ49!0CKDrQ@l|sMsS*MM*5VkrR7}!0000< KMNUMnLSTX=_M(&k literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/powered_by_google_on_white.png b/app/src/main/res/drawable-mdpi/powered_by_google_on_white.png new file mode 100644 index 0000000000000000000000000000000000000000..43a153527476d0bef18d448092891bc0ada777ed GIT binary patch literal 2133 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 sR~}1lqJ$73I?*%6=nTeDI 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/powered_by_google_on_white.png b/app/src/main/res/drawable-xxxhdpi/powered_by_google_on_white.png new file mode 100644 index 0000000000000000000000000000000000000000..74ba4b4efc2ebb3b0581012e036a6f6440949e3e GIT binary patch literal 9314 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 D;>d^h literal 0 HcmV?d00001 diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index c2ccb23e1..c4165c5e0 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -188,7 +188,6 @@ توليد الإخطارات ادمج عدة إشعارات في شعار واحد انقر هنا إذا كنت تواجه مشاكل في الإشعارات - مزود الخرائط الموقع إظهار التعليقات في شاشة تحرير المهمة الضغط على زر الرجوع يحفظ المهمة diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index e0bc3c3fa..2ef7d2d5e 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -438,7 +438,6 @@ Изберете местоположение Избери това местоположение Или избери местоположение - Намери доставчик Липсващи разрешения Необходими са разрешения за местоположение, за да откриете текущото ви местоположение Отворете картата diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 885bdec12..454212143 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -464,7 +464,6 @@ Dle systému každý %1$s %2$s Nebo vyberte místo - Poskytovatel vyhledávání Chybí oprávnění Oprávnění k přístupu k poloze je nutné pro zjištění Vaší aktuální polohy Otevřít mapu diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index aee0dadd0..bdb156194 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -211,7 +211,6 @@ Åbn kort Placeringstilladelse kræves for at finde din nuværende placering Manglende tilladelser - Søgeudbyder Eller vælg et sted Vælg dette sted Vælg sted diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 9c4f9dc07..d39b8ec81 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -435,7 +435,6 @@ Ort auswählen Diesen Ort auswählen Oder Ort auswählen - Suchanbieter Fehlende Berechtigungen Standortberechtigung wird zur Ermittlung des aktuellen Standortes benötigt Karte öffnen diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f83567758..ad063fd40 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -438,7 +438,6 @@ Escoger ubicación Seleccionar esta ubicación O elija una ubicación - Proveedor de búsquedas Permisos ausentes Son necesarios permisos de ubicación para encontrar tu ubicación actual Abrir mapa diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 0f23eba17..a534f8b54 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -444,7 +444,6 @@ Hautatu kokaleku bat Hautatu kokaleku hau Edo hautatu kokaleku bat - Bilatu hornitzailea Baimenak falta dira Kokapen baimena behar da zure uneko kokalekua aurkitzeko Ireki mapa diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index a9a9c7f2d..973adeba8 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -441,7 +441,6 @@ Avaa kartta Sijaintioikeudet tarvitaan nykysijainnin paikannukseen Oikeuksia puuttuu - Hauntarjoaja Tai valitse muu sijainti Valitse tämä sijainti Valitse sijainti diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 1fb0e8a40..ac621fd07 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -420,7 +420,6 @@ Choisir une localisation Sélectionner cette localisation Ou choisir une localisation - Moteurs de recherche Permissions manquantes La permission de la localisation est nécessaire pour trouver votre localisation actuelle Ouvrir la carte diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index af3cda2b2..3ce39d436 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -439,7 +439,6 @@ Hely kiválasztása Hely kiválasztása Vagy másik hely keresése - Keresési szolgáltató Hiányzó jogosultságok A jelenlegi pozíció meghatározásához helymeghatározási jogosultság szükséges Térkép megnyitása diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 0913cd05c..f397cc58d 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -368,7 +368,6 @@ Pilih sebuah lokasi Pilih lokasi ini Atau pilih sebuah lokasi - Penyedia pencarian Izin akses lokasi diperlukan untuk mencari lokasi Anda saat ini Buka peta Pilih lokasi baru diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 6ac7a95ac..286c34031 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -439,7 +439,6 @@ Scegli una posizione Seleziona questa posizione O scegli una posizione - Seleziona fornitore Permessi mancanti Per trovare la tua posizione sono richiesti i permessi di localizzazione Apri mappa diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 1ad9c8c93..72885c6a0 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -473,7 +473,6 @@ בחרו מיקום בחרו את המיקום הזה או בחרו מיקום - ספק חיפוש חסרות הרשאות הרשאות מיקום נדרשות על מנת למצוא את מיקומך הנוכחי פתיחת מפה diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 60949bd7d..5e9f64544 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -436,7 +436,6 @@ 場所を選択 この場所を選択 または場所を選ぶ - プロバイダーを検索 アクセス許可がありません 現在の場所を見つけるには位置のアクセス許可が必要です 地図を開く diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 3c93b6169..df1ab515f 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -441,7 +441,6 @@ 위치 선택 현위치 선택 위치 고르기 - 검색 제공업체 권한 누락 기기의 현재 위치를 확인하려면 위치 권한이 필요합니다 지도 열기 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 623fa3372..e64b4eb1c 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -435,7 +435,6 @@ Pasirinkti vietą Pasirinkti šią vietą Arba pasirinkti vietą - Paieškos tiekėjas Dingę leidimai Vietos leidimai reikalingi surasti jūsų esamą vietą Atidaryti žemėlapį diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 7166a11f1..1d45630b5 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -444,7 +444,6 @@ Velg et sted Velg dette stedet Eller velg et sted - Søketilbyder Manglende tilganger Plasseringstilganger trengs for å finne ditt nåværende sted Åpne kart diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index b16beda60..eaaaf4fd5 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -428,7 +428,6 @@ Kies een locatie Selecteer deze locatie Of kies een locatie - Zoek aanbieder Missende rechten Locatie rechten zijn nodig om je huidige locatie te vinden Open kaart diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 609a40088..87b2ba32e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -448,7 +448,6 @@ Wybierz lokalizację Ustaw wybraną lokalizację Lub wybierz lokalizację - Silnik wyszukiwania Brakujące uprawnienia Uprawnienia lokalizacji są wymagane do ustalenia twojej aktualnej lokalizacji Otwórz mapę diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 96a0330f7..39e069764 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -436,7 +436,6 @@ Escolher uma localização Selecionar essa localização Ou escolha uma localização - Provedor de pesquisa Permissões faltando Permissão de Localização é necessária para encontrar sua localização atual Abrir mapa diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 06299250d..de12a4d13 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -399,7 +399,6 @@ Abrir mapa Permissão de localização é necessária para encontrar a sua localização atual Permissões faltando - Provedor de pesquisa Ou escolha uma localização Selecione esta localização Escolher uma localização diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7c1149504..6720fa186 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -455,7 +455,6 @@ Выбрать местоположение Выбрать это местоположение Или выбрать местоположение - Поставщик системы поиска Отсутствующие разрешения Версия %s Google Tasks diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index e3d769663..1e92f41ba 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -435,7 +435,6 @@ Vybrať polohu Vybrať túto polohu Alebo vybrať polohu ručne - Vyhľadávanie poskytuje Chýbajúce oprávnenia Pre učenie súčasnej polohy sú potrebné povolenia Otvoriť mapu diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 0acc44b16..80e1e8d8d 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -439,7 +439,6 @@ Välj en plats Välj den här platsen Eller Välj en plats - Sökleverantör Saknar behörigheter Plats behörigheter behövs för att hitta din aktuella plats Öppna karta diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index c4e10c23b..115e3ebd6 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -273,7 +273,6 @@ வரைபடத்தைத் திறக்கவும் உங்கள் தற்போதைய இருப்பிடத்தைக் கண்டுபிடிக்க இருப்பிட அனுமதிகள் தேவை அனுமதிகள் இல்லை - தேடல் வழங்குநர் அல்லது இருப்பிடத்தைத் தேர்வுசெய்க இந்த இருப்பிடத்தைத் தேர்ந்தெடுக்கவும் இருப்பிடத்தைத் தேர்வுசெய்க diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index d021cf971..3189c2f44 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -441,7 +441,6 @@ Konum seç Bu konumu seç Ya da konum seç - Arama sağlayıcı Eksik izinler Geçerli konumunuzu bulmak için konum izinleri gereklidir Haritayı aç diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e74935da4..1844655c5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -457,7 +457,6 @@ Обрати місце Обрати це місце Або вказати інше місце - Знайти провайдера Відсутні дозволи Щоб визначити ваше місцезнаходження необхідний дозвіл на перегляд місцезнаходження Відкрити карту diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 5d6a8fd30..ca695d7f9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -434,7 +434,6 @@ 选择一个位置 选中这个位置 或选择一个位置 - 搜索引擎供应商 缺少权限 需要位置权限来发现您当前位置 打开地图 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d083cabb0..27c14921d 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -444,7 +444,6 @@ 開啟地圖 需要位置權限以找到您現在的位置 缺少權限 - 搜尋提供者 或選擇一個地點 選擇此地點 選擇一個地點 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index f5d25522b..947822b4d 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -292,16 +292,6 @@ 2 - - @string/mapbox - @string/map_search_google_places - - - - 0 - 1 - - @string/google_play_location_service @string/android_location_services diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index 7bb963d15..f3ab2e8a9 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -396,12 +396,10 @@ linkify_task_list linkify_task_edit Mapbox - Google Places Nominatim Google Maps OpenStreetMap Android - place_provider_v2 location_based_reminders geofence_service preference_screen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f1a5f557..bfb277341 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -523,7 +523,6 @@ File %1$s contained %2$s.\n\n Choose a location Select this location Or choose a location - Search provider Missing permissions This app collects location data to enable location-based reminders even when the app is closed or not in use. Location permissions are needed to find your current location diff --git a/app/src/main/res/xml/preferences_location.xml b/app/src/main/res/xml/preferences_location.xml index b2f258bca..b4b11c9e5 100644 --- a/app/src/main/res/xml/preferences_location.xml +++ b/app/src/main/res/xml/preferences_location.xml @@ -54,14 +54,6 @@ android:entryValues="@array/reverse_geocoder_values" android:summary="%s"/> - - \ No newline at end of file diff --git a/app/src/release/res/values/keys.xml b/app/src/release/res/values/keys.xml index fa04b6d97..f77ddd2ef 100644 --- a/app/src/release/res/values/keys.xml +++ b/app/src/release/res/values/keys.xml @@ -3,4 +3,5 @@ Tasks https://caldav.tasks.org https://nominatim.tasks.org + https://places.tasks.org \ No newline at end of file diff --git a/app/src/test/java/org/tasks/location/PlaceSearchGoogleTest.kt b/app/src/test/java/org/tasks/location/PlaceSearchGoogleTest.kt new file mode 100644 index 000000000..b36f786b7 --- /dev/null +++ b/app/src/test/java/org/tasks/location/PlaceSearchGoogleTest.kt @@ -0,0 +1,49 @@ +package org.tasks.location + +import org.junit.Assert.assertEquals +import org.junit.Test +import org.tasks.TestUtilities.readFile +import org.tasks.location.PlaceSearchGoogle.Companion.toJson +import org.tasks.location.PlaceSearchGoogle.Companion.toPlace +import org.tasks.location.PlaceSearchGoogle.Companion.toSearchResults + +class PlaceSearchGoogleTest { + @Test + fun placeSearchWithMultipleResults() { + val results = toSearchResults(readFile("google_places/search.json").toJson()) + + assertEquals( + listOf( + "ChIJfQQVCCMzjoARce0POzONI8I", + "ChIJQXqgXsiAj4ARqtl6U4GD-Cw", + "ChIJCWNdVNgr3YAR4pLlOt8CfEk", + "ChIJhTEH6lev3IARDMKC_pGF6nI" + ), + results.map { it.id } + ) + } + + @Test + fun validatePlace() { + val result = toSearchResults(readFile("google_places/search.json").toJson())[2] + + assertEquals("ChIJCWNdVNgr3YAR4pLlOt8CfEk", result.id) + assertEquals("Portillo's Hot Dogs", result.name) + assertEquals("La Palma Avenue, Buena Park, CA, USA", result.address) + } + + @Test + fun fetchPlace() { + val result = toPlace(readFile("google_places/fetch.json").toJson()) + + assertEquals("Magic Kingdom Park", result.name) + assertEquals("1180 Seven Seas Drive, Lake Buena Vista, FL 32836, USA", result.address) + assertEquals(28.417663, result.latitude, 0.0) + assertEquals(-81.581212, result.longitude, 0.0) + assertEquals("+1 407-939-5277", result.phone) + assertEquals( + "https://disneyworld.disney.go.com/destinations/magic-kingdom/?CMP=OKC-80007944_GM_WDW_destination_magickingdompark_NA", + result.url + ) + } +} \ No newline at end of file diff --git a/app/src/test/resources/google_places/fetch.json b/app/src/test/resources/google_places/fetch.json new file mode 100644 index 000000000..ef1aa08c4 --- /dev/null +++ b/app/src/test/resources/google_places/fetch.json @@ -0,0 +1,17 @@ +{ + "html_attributions": [], + "result": { + "formatted_address": "1180 Seven Seas Drive, Lake Buena Vista, FL 32836, USA", + "geometry": { + "location": { + "lat": 28.417663, + "lng": -81.581212 + } + }, + "international_phone_number": "+1 407-939-5277", + "name": "Magic Kingdom Park", + "place_id": "ChIJgUulalN-3YgRGoTaWM2LawY", + "website": "https://disneyworld.disney.go.com/destinations/magic-kingdom/?CMP=OKC-80007944_GM_WDW_destination_magickingdompark_NA" + }, + "status": "OK" +} \ No newline at end of file diff --git a/app/src/test/resources/google_places/search.json b/app/src/test/resources/google_places/search.json new file mode 100644 index 000000000..774d37ca2 --- /dev/null +++ b/app/src/test/resources/google_places/search.json @@ -0,0 +1,225 @@ +{ + "predictions": [ + { + "description": "portillo's hot dogs", + "matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ], + "structured_formatting": { + "main_text": "portillo's hot dogs", + "main_text_matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ] + }, + "terms": [ + { + "offset": 0, + "value": "portillo's hot dogs" + } + ] + }, + { + "description": "Portillo Diesel, Leo Avenue, San Jose, CA, USA", + "matched_substrings": [ + { + "length": 15, + "offset": 0 + } + ], + "place_id": "ChIJfQQVCCMzjoARce0POzONI8I", + "reference": "ChIJfQQVCCMzjoARce0POzONI8I", + "structured_formatting": { + "main_text": "Portillo Diesel", + "main_text_matched_substrings": [ + { + "length": 15, + "offset": 0 + } + ], + "secondary_text": "Leo Avenue, San Jose, CA, USA" + }, + "terms": [ + { + "offset": 0, + "value": "Portillo Diesel" + }, + { + "offset": 17, + "value": "Leo Avenue" + }, + { + "offset": 29, + "value": "San Jose" + }, + { + "offset": 39, + "value": "CA" + }, + { + "offset": 43, + "value": "USA" + } + ], + "types": [ + "car_repair", + "point_of_interest", + "establishment" + ] + }, + { + "description": "Portillo's Trucking, Franklin Street, Oakland, CA, USA", + "matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ], + "place_id": "ChIJQXqgXsiAj4ARqtl6U4GD-Cw", + "reference": "ChIJQXqgXsiAj4ARqtl6U4GD-Cw", + "structured_formatting": { + "main_text": "Portillo's Trucking", + "main_text_matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ], + "secondary_text": "Franklin Street, Oakland, CA, USA" + }, + "terms": [ + { + "offset": 0, + "value": "Portillo's Trucking" + }, + { + "offset": 21, + "value": "Franklin Street" + }, + { + "offset": 38, + "value": "Oakland" + }, + { + "offset": 47, + "value": "CA" + }, + { + "offset": 51, + "value": "USA" + } + ], + "types": [ + "moving_company", + "point_of_interest", + "establishment" + ] + }, + { + "description": "Portillo's Hot Dogs, La Palma Avenue, Buena Park, CA, USA", + "matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ], + "place_id": "ChIJCWNdVNgr3YAR4pLlOt8CfEk", + "reference": "ChIJCWNdVNgr3YAR4pLlOt8CfEk", + "structured_formatting": { + "main_text": "Portillo's Hot Dogs", + "main_text_matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ], + "secondary_text": "La Palma Avenue, Buena Park, CA, USA" + }, + "terms": [ + { + "offset": 0, + "value": "Portillo's Hot Dogs" + }, + { + "offset": 21, + "value": "La Palma Avenue" + }, + { + "offset": 38, + "value": "Buena Park" + }, + { + "offset": 50, + "value": "CA" + }, + { + "offset": 54, + "value": "USA" + } + ], + "types": [ + "meal_takeaway", + "restaurant", + "food", + "point_of_interest", + "establishment" + ] + }, + { + "description": "Portillo's Hot Dogs, Day Street, Moreno Valley, CA, USA", + "matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ], + "place_id": "ChIJhTEH6lev3IARDMKC_pGF6nI", + "reference": "ChIJhTEH6lev3IARDMKC_pGF6nI", + "structured_formatting": { + "main_text": "Portillo's Hot Dogs", + "main_text_matched_substrings": [ + { + "length": 19, + "offset": 0 + } + ], + "secondary_text": "Day Street, Moreno Valley, CA, USA" + }, + "terms": [ + { + "offset": 0, + "value": "Portillo's Hot Dogs" + }, + { + "offset": 21, + "value": "Day Street" + }, + { + "offset": 33, + "value": "Moreno Valley" + }, + { + "offset": 48, + "value": "CA" + }, + { + "offset": 52, + "value": "USA" + } + ], + "types": [ + "meal_takeaway", + "restaurant", + "food", + "point_of_interest", + "establishment" + ] + } + ], + "status": "OK" +} \ No newline at end of file diff --git a/deps_googleplay.txt b/deps_googleplay.txt index 5a6f17f94..4e884b4ae 100644 --- a/deps_googleplay.txt +++ b/deps_googleplay.txt @@ -186,8 +186,32 @@ +| +--- androidx.fragment:fragment:1.0.0 -> 1.2.5 (*) +| +--- com.google.android.gms:play-services-base:17.0.0 -> 17.3.0 (*) +| \--- com.google.android.gms:play-services-basement:17.0.0 -> 17.3.0 (*) -++--- com.google.android.libraries.places:places:2.4.0 -+| +--- androidx.appcompat:appcompat:1.0.0 -> 1.2.0 +++--- com.android.billingclient:billing:1.2.2 +++--- com.gitlab.bitfireAT:dav4jvm:2.1.1 ++| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.72 -> 1.4.30 (*) ++| \--- org.apache.commons:commons-lang3:3.9 +++--- com.gitlab.abaker:ical4android:0e928b567c ++| +--- org.mnode.ical4j:ical4j:3.0.21 ++| | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30 ++| | +--- commons-codec:commons-codec:1.11 ++| | +--- org.apache.commons:commons-lang3:3.8.1 -> 3.9 ++| | \--- org.apache.commons:commons-collections4:4.1 ++| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21 (*) ++| +--- com.sun.mail:android-mail:1.6.5 ++| | \--- com.sun.mail:android-activation:1.6.5 ++| +--- commons-io:commons-io:2.6 ++| +--- org.slf4j:slf4j-jdk14:1.7.30 ++| | \--- org.slf4j:slf4j-api:1.7.30 ++| \--- androidx.core:core-ktx:1.3.2 ++| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.71 -> 1.4.30 (*) ++| +--- androidx.annotation:annotation:1.1.0 ++| \--- androidx.core:core:1.3.2 (*) +++--- com.gitlab.bitfireAT:cert4android:26a91a729f ++| +--- androidx.databinding:databinding-common:4.1.1 -> 4.1.2 ++| +--- androidx.databinding:databinding-runtime:4.1.1 -> 4.1.2 (*) ++| +--- androidx.databinding:databinding-adapters:4.1.1 -> 4.1.2 (*) ++| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21 (*) ++| +--- androidx.appcompat:appcompat:1.2.0 +| | +--- androidx.annotation:annotation:1.1.0 +| | +--- androidx.core:core:1.3.0 -> 1.3.2 (*) +| | +--- androidx.cursoradapter:cursoradapter:1.0.0 @@ -213,8 +237,7 @@ +| | \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*) +| +--- androidx.cardview:cardview:1.0.0 +| | \--- androidx.annotation:annotation:1.0.0 -> 1.1.0 -+| +--- androidx.fragment:fragment:1.1.0 -> 1.2.5 (*) -+| +--- androidx.lifecycle:lifecycle-extensions:2.1.0 -> 2.2.0 ++| +--- androidx.lifecycle:lifecycle-extensions:2.2.0 +| | +--- androidx.lifecycle:lifecycle-runtime:2.2.0 (*) +| | +--- androidx.arch.core:core-common:2.1.0 (*) +| | +--- androidx.arch.core:core-runtime:2.1.0 (*) @@ -226,50 +249,6 @@ +| | +--- androidx.lifecycle:lifecycle-service:2.2.0 +| | | \--- androidx.lifecycle:lifecycle-runtime:2.2.0 (*) +| | \--- androidx.lifecycle:lifecycle-viewmodel:2.2.0 (*) -+| +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.1.0 -+| | +--- androidx.annotation:annotation:1.1.0 -+| | +--- androidx.core:core:1.1.0 -> 1.3.2 (*) -+| | +--- androidx.customview:customview:1.0.0 (*) -+| | \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*) -+| +--- com.android.volley:volley:1.1.1 -+| +--- com.google.android.datatransport:transport-api:2.2.0 -> 2.2.1 (*) -+| +--- com.google.android.datatransport:transport-backend-cct:2.3.0 -> 2.3.3 (*) -+| +--- com.google.android.datatransport:transport-runtime:2.2.3 -> 2.2.5 (*) -+| +--- com.google.android.gms:play-services-base:17.2.1 -> 17.3.0 (*) -+| +--- com.google.android.gms:play-services-basement:17.0.0 -> 17.3.0 (*) -+| +--- com.google.android.gms:play-services-location:17.0.0 -> 17.1.0 (*) -+| +--- com.google.android.gms:play-services-maps:17.0.0 (*) -+| +--- com.google.android.gms:play-services-tasks:17.0.0 -> 17.1.0 (*) -+| +--- com.google.auto.value:auto-value-annotations:1.6.2 -> 1.7.4 -+| \--- com.google.code.gson:gson:2.8.5 -> 2.8.6 -++--- com.android.billingclient:billing:1.2.2 -++--- com.gitlab.bitfireAT:dav4jvm:2.1.1 -+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.72 -> 1.4.30 (*) -+| \--- org.apache.commons:commons-lang3:3.9 -++--- com.gitlab.abaker:ical4android:0e928b567c -+| +--- org.mnode.ical4j:ical4j:3.0.21 -+| | +--- org.slf4j:slf4j-api:1.7.25 -> 1.7.30 -+| | +--- commons-codec:commons-codec:1.11 -+| | +--- org.apache.commons:commons-lang3:3.8.1 -> 3.9 -+| | \--- org.apache.commons:commons-collections4:4.1 -+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21 (*) -+| +--- com.sun.mail:android-mail:1.6.5 -+| | \--- com.sun.mail:android-activation:1.6.5 -+| +--- commons-io:commons-io:2.6 -+| +--- org.slf4j:slf4j-jdk14:1.7.30 -+| | \--- org.slf4j:slf4j-api:1.7.30 -+| \--- androidx.core:core-ktx:1.3.2 -+| +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.71 -> 1.4.30 (*) -+| +--- androidx.annotation:annotation:1.1.0 -+| \--- androidx.core:core:1.3.2 (*) -++--- com.gitlab.bitfireAT:cert4android:26a91a729f -+| +--- androidx.databinding:databinding-common:4.1.1 -> 4.1.2 -+| +--- androidx.databinding:databinding-runtime:4.1.1 -> 4.1.2 (*) -+| +--- androidx.databinding:databinding-adapters:4.1.1 -> 4.1.2 (*) -+| +--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21 (*) -+| +--- androidx.appcompat:appcompat:1.2.0 (*) -+| +--- androidx.cardview:cardview:1.0.0 (*) -+| +--- androidx.lifecycle:lifecycle-extensions:2.2.0 (*) +| +--- androidx.lifecycle:lifecycle-livedata-ktx:2.2.0 +| | +--- org.jetbrains.kotlin:kotlin-stdlib:1.3.50 -> 1.4.30 (*) +| | +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0 -> 1.4.1 @@ -307,7 +286,11 @@ +| | +--- androidx.annotation:annotation-experimental:1.0.0 +| | +--- androidx.fragment:fragment:1.0.0 -> 1.2.5 (*) +| | +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.2.0 (*) -+| | +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.1.0 (*) ++| | +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.1.0 ++| | | +--- androidx.annotation:annotation:1.1.0 ++| | | +--- androidx.core:core:1.1.0 -> 1.3.2 (*) ++| | | +--- androidx.customview:customview:1.0.0 (*) ++| | | \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*) +| | +--- androidx.transition:transition:1.2.0 +| | | +--- androidx.annotation:annotation:1.1.0 +| | | +--- androidx.core:core:1.0.1 -> 1.3.2 (*)