diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 74709d9df5..6be996f999 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,6 +1,14 @@
+
+
@@ -120,6 +128,9 @@
+
+
+
\ No newline at end of file
diff --git a/core-kotlin/src/main/java/com/amplifyframework/kotlin/core/Amplify.kt b/core-kotlin/src/main/java/com/amplifyframework/kotlin/core/Amplify.kt
index 4e50124de5..9d1ecb9b43 100644
--- a/core-kotlin/src/main/java/com/amplifyframework/kotlin/core/Amplify.kt
+++ b/core-kotlin/src/main/java/com/amplifyframework/kotlin/core/Amplify.kt
@@ -24,6 +24,7 @@ import com.amplifyframework.core.plugin.Plugin
import com.amplifyframework.kotlin.api.KotlinApiFacade
import com.amplifyframework.kotlin.auth.KotlinAuthFacade
import com.amplifyframework.kotlin.datastore.KotlinDataStoreFacade
+import com.amplifyframework.kotlin.geo.KotlinGeoFacade
import com.amplifyframework.kotlin.hub.KotlinHubFacade
import com.amplifyframework.kotlin.predictions.KotlinPredictionsFacade
import com.amplifyframework.kotlin.storage.KotlinStorageFacade
@@ -41,6 +42,7 @@ class Amplify {
val Analytics = AnalyticsCategory()
val API = KotlinApiFacade()
val Auth = KotlinAuthFacade()
+ val Geo = KotlinGeoFacade()
val Logging = LoggingCategory()
val Storage = KotlinStorageFacade()
val Hub = KotlinHubFacade()
diff --git a/core-kotlin/src/main/java/com/amplifyframework/kotlin/geo/Geo.kt b/core-kotlin/src/main/java/com/amplifyframework/kotlin/geo/Geo.kt
new file mode 100644
index 0000000000..fffe28e056
--- /dev/null
+++ b/core-kotlin/src/main/java/com/amplifyframework/kotlin/geo/Geo.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.kotlin.geo
+
+import com.amplifyframework.geo.models.Coordinates
+import com.amplifyframework.geo.models.MapStyle
+import com.amplifyframework.geo.models.MapStyleDescriptor
+import com.amplifyframework.geo.options.GeoSearchByCoordinatesOptions
+import com.amplifyframework.geo.options.GeoSearchByTextOptions
+import com.amplifyframework.geo.options.GetMapStyleDescriptorOptions
+import com.amplifyframework.geo.result.GeoSearchResult
+
+interface Geo {
+ /**
+ * Gets a collection of maps and their corresponding styles.
+ *
+ * @return A collection of all available [MapStyle].
+ */
+ suspend fun getAvailableMaps(): Collection
+
+ /**
+ * Gets the default map and style from available maps.
+ *
+ * @return The default [MapStyle].
+ */
+ suspend fun getDefaultMap(): MapStyle
+
+ /**
+ * Uses given options to get map style descriptor JSON.
+ *
+ * @param options Options to specify for this operation.
+ * @return The [MapStyleDescriptor] matching the given options.
+ */
+ suspend fun getMapStyleDescriptor(
+ options: GetMapStyleDescriptorOptions = GetMapStyleDescriptorOptions.defaults()
+ ): MapStyleDescriptor
+
+ /**
+ * Searches for locations that match text query.
+ *
+ * @param query Search query text.
+ * @param options Search options to use.
+ * @return The [GeoSearchResult] for the query and options.
+ */
+ suspend fun searchByText(
+ query: String,
+ options: GeoSearchByTextOptions = GeoSearchByTextOptions.defaults()
+ ): GeoSearchResult
+
+ /**
+ * Searches for location with given set of coordinates.
+ *
+ * @param position Coordinates to look-up.
+ * @param options Search options to use.
+ * @return The [GeoSearchResult] for the position and options.
+ */
+ suspend fun searchByCoordinates(
+ position: Coordinates,
+ options: GeoSearchByCoordinatesOptions = GeoSearchByCoordinatesOptions.defaults()
+ ): GeoSearchResult
+}
diff --git a/core-kotlin/src/main/java/com/amplifyframework/kotlin/geo/KotlinGeoFacade.kt b/core-kotlin/src/main/java/com/amplifyframework/kotlin/geo/KotlinGeoFacade.kt
new file mode 100644
index 0000000000..e30163a931
--- /dev/null
+++ b/core-kotlin/src/main/java/com/amplifyframework/kotlin/geo/KotlinGeoFacade.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.kotlin.geo
+
+import com.amplifyframework.core.Amplify
+import com.amplifyframework.geo.GeoCategoryBehavior
+import com.amplifyframework.geo.models.Coordinates
+import com.amplifyframework.geo.models.MapStyle
+import com.amplifyframework.geo.options.GeoSearchByCoordinatesOptions
+import com.amplifyframework.geo.options.GeoSearchByTextOptions
+import com.amplifyframework.geo.options.GetMapStyleDescriptorOptions
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+
+class KotlinGeoFacade(private val delegate: GeoCategoryBehavior = Amplify.Geo) : Geo {
+ override suspend fun getAvailableMaps(): Collection = suspendCoroutine { continuation ->
+ delegate.getAvailableMaps(
+ { continuation.resume(it) },
+ { continuation.resumeWithException(it) }
+ )
+ }
+
+ override suspend fun getDefaultMap() = suspendCoroutine { continuation ->
+ delegate.getDefaultMap(
+ { continuation.resume(it) },
+ { continuation.resumeWithException(it) }
+ )
+ }
+
+ override suspend fun getMapStyleDescriptor(options: GetMapStyleDescriptorOptions) =
+ suspendCoroutine { continuation ->
+ delegate.getMapStyleDescriptor(
+ options,
+ { continuation.resume(it) },
+ { continuation.resumeWithException(it) }
+ )
+ }
+
+ override suspend fun searchByText(
+ query: String,
+ options: GeoSearchByTextOptions
+ ) = suspendCoroutine { continuation ->
+ delegate.searchByText(
+ query,
+ options,
+ { continuation.resume(it) },
+ { continuation.resumeWithException(it) }
+ )
+ }
+
+ override suspend fun searchByCoordinates(
+ position: Coordinates,
+ options: GeoSearchByCoordinatesOptions
+ ) = suspendCoroutine { continuation ->
+ delegate.searchByCoordinates(
+ position,
+ options,
+ { continuation.resume(it) },
+ { continuation.resumeWithException(it) }
+ )
+ }
+}
diff --git a/core-kotlin/src/test/java/com/amplifyframework/kotlin/geo/KotlinGeoFacadeTest.kt b/core-kotlin/src/test/java/com/amplifyframework/kotlin/geo/KotlinGeoFacadeTest.kt
new file mode 100644
index 0000000000..0efb5516bb
--- /dev/null
+++ b/core-kotlin/src/test/java/com/amplifyframework/kotlin/geo/KotlinGeoFacadeTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.kotlin.geo
+
+import com.amplifyframework.core.Consumer
+import com.amplifyframework.geo.GeoCategoryBehavior
+import com.amplifyframework.geo.GeoException
+import com.amplifyframework.geo.models.Coordinates
+import com.amplifyframework.geo.models.MapStyle
+import com.amplifyframework.geo.models.MapStyleDescriptor
+import com.amplifyframework.geo.options.GeoSearchByCoordinatesOptions
+import com.amplifyframework.geo.options.GeoSearchByTextOptions
+import com.amplifyframework.geo.options.GetMapStyleDescriptorOptions
+import com.amplifyframework.geo.result.GeoSearchResult
+import io.mockk.every
+import io.mockk.mockk
+import junit.framework.Assert.assertSame
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+
+/**
+ * Unit tests for the [KotlinGeoFacade] class.
+ */
+internal class KotlinGeoFacadeTest {
+ private val delegate = mockk()
+ private val geo = KotlinGeoFacade(delegate)
+
+ @Test
+ fun `gets available maps`() = runBlocking {
+ val maps = listOf(
+ MapStyle("a", "b"),
+ MapStyle("c", "d")
+ )
+ every { delegate.getAvailableMaps(any(), any()) } answers {
+ val callback = firstArg>>()
+ callback.accept(maps)
+ }
+ val result = geo.getAvailableMaps()
+ assertSame(maps, result)
+ }
+
+ @Test(expected = GeoException::class)
+ fun `throws available map error`(): Unit = runBlocking {
+ val error = GeoException("message", "suggestion")
+ every { delegate.getAvailableMaps(any(), any()) } answers {
+ val callback = secondArg>()
+ callback.accept(error)
+ }
+ geo.getAvailableMaps()
+ }
+
+ @Test
+ fun `gets default map`() = runBlocking {
+ val map = MapStyle("name", "style")
+ every { delegate.getDefaultMap(any(), any()) } answers {
+ val callback = firstArg>()
+ callback.accept(map)
+ }
+ val result = geo.getDefaultMap()
+ assertSame(map, result)
+ }
+
+ @Test(expected = GeoException::class)
+ fun `throws default map error`(): Unit = runBlocking {
+ val error = GeoException("message", "suggestion")
+ every { delegate.getDefaultMap(any(), any()) } answers {
+ val callback = secondArg>()
+ callback.accept(error)
+ }
+ geo.getDefaultMap()
+ }
+
+ @Test
+ fun `returns map style descriptor`() = runBlocking {
+ val descriptor = MapStyleDescriptor("")
+ every { delegate.getMapStyleDescriptor(any(), any(), any()) } answers {
+ val callback = secondArg>()
+ callback.accept(descriptor)
+ }
+ val result = geo.getMapStyleDescriptor()
+ assertSame(descriptor, result)
+ }
+
+ @Test
+ fun `returns map style descriptor with options`() = runBlocking {
+ val descriptor = MapStyleDescriptor("")
+ val options = GetMapStyleDescriptorOptions.builder().mapName("map").build()
+ every { delegate.getMapStyleDescriptor(options, any(), any()) } answers {
+ val callback = secondArg>()
+ callback.accept(descriptor)
+ }
+ val result = geo.getMapStyleDescriptor(options)
+ assertSame(descriptor, result)
+ }
+
+ @Test(expected = GeoException::class)
+ fun `throws map style descriptor error`(): Unit = runBlocking {
+ val error = GeoException("message", "suggestion")
+ every { delegate.getMapStyleDescriptor(any(), any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+ geo.getMapStyleDescriptor()
+ }
+
+ @Test
+ fun `returns search by text result`() = runBlocking {
+ val query = "query"
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByText(query, any(), any(), any()) } answers {
+ val callback = thirdArg>()
+ callback.accept(searchResult)
+ }
+ val result = geo.searchByText(query)
+ assertSame(searchResult, result)
+ }
+
+ @Test
+ fun `returns search by text result with options`() = runBlocking {
+ val query = "query"
+ val options = GeoSearchByTextOptions.builder().maxResults(4).build()
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByText(query, options, any(), any()) } answers {
+ val callback = thirdArg>()
+ callback.accept(searchResult)
+ }
+ val result = geo.searchByText(query, options)
+ assertSame(searchResult, result)
+ }
+
+ @Test(expected = GeoException::class)
+ fun `throws search by text error`(): Unit = runBlocking {
+ val query = "query"
+ val error = GeoException("message", "suggestion")
+ every { delegate.searchByText(query, any(), any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+ geo.searchByText(query)
+ }
+
+ @Test
+ fun `returns search by coordinates result`() = runBlocking {
+ val position = Coordinates()
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByCoordinates(position, any(), any(), any()) } answers {
+ val callback = thirdArg>()
+ callback.accept(searchResult)
+ }
+ val result = geo.searchByCoordinates(position)
+ assertSame(searchResult, result)
+ }
+
+ @Test
+ fun `returns search by coordinates result with options`() = runBlocking {
+ val position = Coordinates()
+ val options = GeoSearchByCoordinatesOptions.builder().maxResults(3).build()
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByCoordinates(position, options, any(), any()) } answers {
+ val callback = thirdArg>()
+ callback.accept(searchResult)
+ }
+ val result = geo.searchByCoordinates(position, options)
+ assertSame(searchResult, result)
+ }
+
+ @Test(expected = GeoException::class)
+ fun `throws search by coordinates error`(): Unit = runBlocking {
+ val position = Coordinates()
+ val error = GeoException("message", "suggestion")
+ every { delegate.searchByCoordinates(position, any(), any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+ geo.searchByCoordinates(position)
+ }
+}