Skip to content

Commit

Permalink
Issue mozilla-mobile#5989: Create LocationService interface and make …
Browse files Browse the repository at this point in the history
…MozillaLocationService implement it.
  • Loading branch information
pocmo committed Feb 25, 2020
1 parent e0760af commit 728dd04
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 27 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.service.location

/**
* Interface describing a [LocationService] that returns a [Region].
*/
interface LocationService {
/**
* Determines the current [Region] of the user.
*/
suspend fun fetchRegion(readFromCache: Boolean = true): Region?

/**
* A [Region] returned by the location service.
*
* The [Region] use region codes and names from the GENC dataset, which is for the most part
* compatible with the ISO 3166 standard. While the API endpoint and [Region] class refers to
* country, no claim about the political status of any region is made by this service.
*
* @param countryCode Country code; ISO 3166.
* @param countryName Name of the country (English); ISO 3166.
*/
data class Region(
val countryCode: String,
val countryName: String
)

companion object {
/**
* Creates a dummy [LocationService] implementation that always returns `null`.
*/
fun dummy() = object : LocationService {
override suspend fun fetchRegion(readFromCache: Boolean): Region? = null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,46 +51,33 @@ class MozillaLocationService(
private val client: Client,
apiKey: String,
serviceUrl: String = GEOIP_SERVICE_URL
) {
) : LocationService {
private val regionServiceUrl = (serviceUrl + "country?key=%s").format(apiKey)

/**
* Determines the current [Region] based on the IP address used to access the service.
* Determines the current [LocationService.Region] based on the IP address used to access the service.
*
* https://mozilla.github.io/ichnaea/api/region.html
*
* @param readFromCache Whether a previously returned region (from the cache) can be returned
* (default) or whether a request to the service should always be made.
*/
suspend fun fetchRegion(readFromCache: Boolean = true): Region? = withContext(Dispatchers.IO) {
override suspend fun fetchRegion(
readFromCache: Boolean
): LocationService.Region? = withContext(Dispatchers.IO) {
if (readFromCache) {
context.loadCachedRegion()?.let { return@withContext it }
}

client.fetchRegion(regionServiceUrl)?.also { context.cacheRegion(it) }
}

/**
* A [Region] returned by the location service.
*
* The [Region] use region codes and names from the GENC dataset, which is for the most part
* compatible with the ISO 3166 standard. While the API endpoint and [Region] class refers to
* country, no claim about the political status of any region is made by this service.
*
* @param countryCode Country code; ISO 3166.
* @param countryName Name of the country (English); ISO 3166.
*/
data class Region(
val countryCode: String,
val countryName: String
)
}

private fun Context.loadCachedRegion(): MozillaLocationService.Region? {
private fun Context.loadCachedRegion(): LocationService.Region? {
val cache = regionCache()

return if (cache.contains(KEY_COUNTRY_CODE) && cache.contains(KEY_COUNTRY_NAME)) {
MozillaLocationService.Region(
LocationService.Region(
cache.getString(KEY_COUNTRY_CODE, null)!!,
cache.getString(KEY_COUNTRY_NAME, null)!!
)
Expand All @@ -99,7 +86,7 @@ private fun Context.loadCachedRegion(): MozillaLocationService.Region? {
}
}

private fun Context.cacheRegion(region: MozillaLocationService.Region) {
private fun Context.cacheRegion(region: LocationService.Region) {
regionCache()
.edit()
.putString(KEY_COUNTRY_CODE, region.countryCode)
Expand All @@ -119,7 +106,7 @@ private fun Context.regionCache(): SharedPreferences {
return getSharedPreferences(CACHE_FILE, Context.MODE_PRIVATE)
}

private fun Client.fetchRegion(regionServiceUrl: String): MozillaLocationService.Region? {
private fun Client.fetchRegion(regionServiceUrl: String): LocationService.Region? {
val request = Request(
url = regionServiceUrl,
method = Request.Method.POST,
Expand All @@ -143,15 +130,15 @@ private fun Client.fetchRegion(regionServiceUrl: String): MozillaLocationService
}
}

private fun Response.toRegion(): MozillaLocationService.Region? {
private fun Response.toRegion(): LocationService.Region? {
if (!isSuccess) {
return null
}

use {
return try {
val json = JSONObject(body.string(Charsets.UTF_8))
MozillaLocationService.Region(
LocationService.Region(
json.getString(KEY_COUNTRY_CODE),
json.getString(KEY_COUNTRY_NAME)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package mozilla.components.service.location.search

import mozilla.components.browser.search.provider.localization.SearchLocalization
import mozilla.components.browser.search.provider.localization.SearchLocalizationProvider
import mozilla.components.service.location.LocationService
import mozilla.components.service.location.MozillaLocationService
import java.util.Locale

Expand All @@ -18,7 +19,7 @@ import java.util.Locale
* provider.
*/
class RegionSearchLocalizationProvider(
private val service: MozillaLocationService
private val service: LocationService
) : SearchLocalizationProvider {
override suspend fun determineRegion(): SearchLocalization {
val region = service.fetchRegion(readFromCache = true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package mozilla.components.service.location

import junit.framework.TestCase.assertNull
import kotlinx.coroutines.runBlocking
import org.junit.Test

class LocationServiceTest {
@Test
fun `dummy implementation returns null`() {
runBlocking {
assertNull(LocationService.dummy().fetchRegion(false))
assertNull(LocationService.dummy().fetchRegion(true))
assertNull(LocationService.dummy().fetchRegion(false))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package mozilla.components.service.location.search

import kotlinx.coroutines.runBlocking
import mozilla.components.service.location.LocationService
import mozilla.components.service.location.MozillaLocationService
import mozilla.components.support.test.mock
import org.junit.After
Expand Down Expand Up @@ -34,7 +35,7 @@ class RegionSearchLocalizationProviderTest {
fun `Uses region from service`() {
runBlocking {
val service: MozillaLocationService = mock()
doReturn(MozillaLocationService.Region("RU", "Russia"))
doReturn(LocationService.Region("RU", "Russia"))
.`when`(service).fetchRegion(ArgumentMatchers.anyBoolean())

val provider = RegionSearchLocalizationProvider(service)
Expand All @@ -52,7 +53,7 @@ class RegionSearchLocalizationProviderTest {
Locale.setDefault(Locale("de", "DE"))

val service: MozillaLocationService = mock()
doReturn(MozillaLocationService.Region("RU", "Russia"))
doReturn(LocationService.Region("RU", "Russia"))
.`when`(service).fetchRegion(ArgumentMatchers.anyBoolean())

val provider = RegionSearchLocalizationProvider(service)
Expand Down
5 changes: 5 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ permalink: /changelog/
* **feature-accounts-push**
* Updated `FxaPushSupportFeature` to use the new `AutoPushFeature` APIs.

* **service-location**
* Created `LocationService` interface and made `MozillaLocationService` implement it.
* `RegionSearchLocalizationProvider` now accepts any `LocationService` implementation.
* Added `LocationService.dummy()` which creates a dummy `LocationService` implementation that always returns `null` when asked for a `LocationService.Region`.

# 33.0.0

* [Commits](https://github.com/mozilla-mobile/android-components/compare/v32.0.0...v33.0.0)
Expand Down

0 comments on commit 728dd04

Please sign in to comment.