diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 6be996f999..bb3923b2db 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -1,5 +1,8 @@
+
+
+
-
\ No newline at end of file
+
diff --git a/rxbindings/build.gradle b/rxbindings/build.gradle
index 78061b9c03..72ffa620c3 100644
--- a/rxbindings/build.gradle
+++ b/rxbindings/build.gradle
@@ -13,7 +13,11 @@
* permissions and limitations under the License.
*/
-apply plugin: 'com.android.library'
+plugins {
+ id "com.android.library"
+ id "kotlin-android"
+}
+
apply from: rootProject.file("configuration/checkstyle.gradle")
apply from: rootProject.file("configuration/publishing.gradle")
@@ -26,6 +30,7 @@ dependencies {
testImplementation project(path: ':testutils')
testImplementation dependency.junit
testImplementation dependency.mockito
+ testImplementation dependency.mockk
testImplementation dependency.androidx.test.core
testImplementation dependency.robolectric
testImplementation project(path: ':rxbindings')
diff --git a/rxbindings/src/main/java/com/amplifyframework/rx/RxAmplify.java b/rxbindings/src/main/java/com/amplifyframework/rx/RxAmplify.java
index b4f144f81c..1216307aa3 100644
--- a/rxbindings/src/main/java/com/amplifyframework/rx/RxAmplify.java
+++ b/rxbindings/src/main/java/com/amplifyframework/rx/RxAmplify.java
@@ -41,6 +41,7 @@ private RxAmplify() {}
@SuppressWarnings({"checkstyle:all"}) public static final RxHubCategoryBehavior Hub = new RxHubBinding();
@SuppressWarnings({"checkstyle:all"}) public static final RxDataStoreCategoryBehavior DataStore = new RxDataStoreBinding();
@SuppressWarnings({"checkstyle:all"}) public static final RxPredictionsCategoryBehavior Predictions = new RxPredictionsBinding();
+ @SuppressWarnings({"checkstyle:all"}) public static final RxGeoCategoryBehavior Geo = new RxGeoBinding();
/**
* Read the configuration from amplifyconfiguration.json file.
diff --git a/rxbindings/src/main/java/com/amplifyframework/rx/RxGeoBinding.java b/rxbindings/src/main/java/com/amplifyframework/rx/RxGeoBinding.java
new file mode 100644
index 0000000000..3042ed753a
--- /dev/null
+++ b/rxbindings/src/main/java/com/amplifyframework/rx/RxGeoBinding.java
@@ -0,0 +1,95 @@
+/*
+ * 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.rx;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.amplifyframework.core.Amplify;
+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 java.util.Collection;
+import java.util.Objects;
+
+import io.reactivex.rxjava3.core.Single;
+
+final class RxGeoBinding implements RxGeoCategoryBehavior {
+ private final GeoCategoryBehavior delegate;
+
+ RxGeoBinding() {
+ delegate = Amplify.Geo;
+ }
+
+ @VisibleForTesting
+ RxGeoBinding(@NonNull GeoCategoryBehavior delegate) {
+ this.delegate = Objects.requireNonNull(delegate);
+ }
+
+ @Override
+ public Single> getAvailableMaps() {
+ return toSingle(delegate::getAvailableMaps);
+ }
+
+ @Override
+ public Single getDefaultMap() {
+ return toSingle(delegate::getDefaultMap);
+ }
+
+ @Override
+ public Single getMapStyleDescriptor() {
+ return toSingle(delegate::getMapStyleDescriptor);
+ }
+
+ @Override
+ public Single getMapStyleDescriptor(@NonNull GetMapStyleDescriptorOptions options) {
+ return toSingle((onResult, onError) -> delegate.getMapStyleDescriptor(options, onResult, onError));
+ }
+
+ @Override
+ public Single searchByText(@NonNull String query) {
+ return toSingle((onResult, onError) -> delegate.searchByText(query, onResult, onError));
+ }
+
+ @Override
+ public Single searchByText(@NonNull String query, @NonNull GeoSearchByTextOptions options) {
+ return toSingle((onResult, onError) -> delegate.searchByText(query, options, onResult, onError));
+ }
+
+ @Override
+ public Single searchByCoordinates(@NonNull Coordinates position) {
+ return toSingle((onResult, onError) -> delegate.searchByCoordinates(position, onResult, onError));
+ }
+
+ @Override
+ public Single searchByCoordinates(
+ @NonNull Coordinates position,
+ @NonNull GeoSearchByCoordinatesOptions options
+ ) {
+ return toSingle((onResult, onError) -> delegate.searchByCoordinates(position, options, onResult, onError));
+ }
+
+ private static Single toSingle(RxAdapters.VoidBehaviors.ResultEmitter behavior) {
+ return RxAdapters.VoidBehaviors.toSingle(behavior);
+ }
+}
diff --git a/rxbindings/src/main/java/com/amplifyframework/rx/RxGeoCategoryBehavior.java b/rxbindings/src/main/java/com/amplifyframework/rx/RxGeoCategoryBehavior.java
new file mode 100644
index 0000000000..35d580aa2c
--- /dev/null
+++ b/rxbindings/src/main/java/com/amplifyframework/rx/RxGeoCategoryBehavior.java
@@ -0,0 +1,114 @@
+/*
+ * 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.rx;
+
+import androidx.annotation.NonNull;
+
+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 java.util.Collection;
+
+import io.reactivex.rxjava3.core.Single;
+
+/**
+ * An Rx-idiomatic expression of the behaviors in {@link GeoCategoryBehavior}.
+ */
+public interface RxGeoCategoryBehavior {
+ /**
+ * Gets a collection of maps and their corresponding styles.
+ *
+ * @return An Rx {@link Single} which emits a {@link MapStyle} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single> getAvailableMaps();
+
+ /**
+ * Gets the default map and style from available maps.
+ *
+ * @return An Rx {@link Single} which emits a {@link MapStyle} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single getDefaultMap();
+
+ /**
+ * Uses default options to get map style descriptor JSON.
+ *
+ * @return An Rx {@link Single} which emits a {@link MapStyleDescriptor} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single getMapStyleDescriptor();
+
+ /**
+ * Uses given options to get map style descriptor JSON.
+ *
+ * @param options Options to specify for this operation.
+ * @return An Rx {@link Single} which emits a {@link MapStyleDescriptor} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single getMapStyleDescriptor(@NonNull GetMapStyleDescriptorOptions options);
+
+ /**
+ * Searches for locations that match text query.
+ *
+ * @param query Search query text.
+ * @return An Rx {@link Single} which emits a {@link GeoSearchResult} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single searchByText(@NonNull String query);
+
+ /**
+ * Searches for locations that match text query.
+ *
+ * @param query Search query text.
+ * @param options Search options to use.
+ * @return An Rx {@link Single} which emits a {@link GeoSearchResult} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single searchByText(
+ @NonNull String query,
+ @NonNull GeoSearchByTextOptions options
+ );
+
+ /**
+ * Searches for location with given set of coordinates.
+ *
+ * @param position Coordinates to look-up.
+ * @return An Rx {@link Single} which emits a {@link GeoSearchResult} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single searchByCoordinates(@NonNull Coordinates position);
+
+ /**
+ * Searches for location with given set of coordinates.
+ *
+ * @param position Coordinates to look-up.
+ * @param options Search options to use.
+ * @return An Rx {@link Single} which emits a {@link GeoSearchResult} on success, or a
+ * {@link GeoException} on failure
+ */
+ Single searchByCoordinates(
+ @NonNull Coordinates position,
+ @NonNull GeoSearchByCoordinatesOptions options
+ );
+}
diff --git a/rxbindings/src/test/java/com/amplifyframework/rx/RxGeoBindingTest.kt b/rxbindings/src/test/java/com/amplifyframework/rx/RxGeoBindingTest.kt
new file mode 100644
index 0000000000..ce95160611
--- /dev/null
+++ b/rxbindings/src/test/java/com/amplifyframework/rx/RxGeoBindingTest.kt
@@ -0,0 +1,279 @@
+/*
+ * 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.rx
+
+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 java.util.concurrent.TimeUnit
+import org.junit.Test
+
+private const val TIMEOUT_SECONDS = 2L
+
+/**
+ * Unit tests for the [RxGeoBinding] class
+ */
+class RxGeoBindingTest {
+ private val delegate = mockk()
+ private val geo = RxGeoBinding(delegate)
+
+ @Test
+ fun `returns available maps`() {
+ val maps = listOf(MapStyle("a", "b"), MapStyle("c", "d"))
+ every { delegate.getAvailableMaps(any(), any()) } answers {
+ val callback = firstArg>>()
+ callback.accept(maps)
+ }
+
+ val observer = geo.availableMaps.test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(maps)
+ }
+
+ @Test
+ fun `returns error for available maps`() {
+ val error = GeoException("message", "suggestion")
+ every { delegate.getAvailableMaps(any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.availableMaps.test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+
+ @Test
+ fun `returns default map`() {
+ val map = MapStyle("map", "style")
+ every { delegate.getDefaultMap(any(), any()) } answers {
+ val callback = firstArg>()
+ callback.accept(map)
+ }
+
+ val observer = geo.defaultMap.test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(map)
+ }
+
+ @Test
+ fun `returns error for default map`() {
+ val error = GeoException("message", "suggestion")
+ every { delegate.getDefaultMap(any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.defaultMap.test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+
+ @Test
+ fun `returns map style descriptor`() {
+ val descriptor = MapStyleDescriptor("")
+ every { delegate.getMapStyleDescriptor(any(), any()) } answers {
+ val callback = firstArg>()
+ callback.accept(descriptor)
+ }
+
+ val observer = geo.mapStyleDescriptor.test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(descriptor)
+ }
+
+ @Test
+ fun `returns map style descriptor with options`() {
+ val options = GetMapStyleDescriptorOptions.builder().mapName("map").build()
+ val descriptor = MapStyleDescriptor("")
+ every { delegate.getMapStyleDescriptor(options, any(), any()) } answers {
+ val callback = secondArg>()
+ callback.accept(descriptor)
+ }
+
+ val observer = geo.getMapStyleDescriptor(options).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(descriptor)
+ }
+
+ @Test
+ fun `returns error for map style descriptor`() {
+ val error = GeoException("message", "suggestion")
+ every { delegate.getMapStyleDescriptor(any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.mapStyleDescriptor.test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+
+ @Test
+ fun `returns error for map style descriptor with options`() {
+ val error = GeoException("message", "suggestion")
+ val options = GetMapStyleDescriptorOptions.builder().mapName("map").build()
+ every { delegate.getMapStyleDescriptor(options, any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.getMapStyleDescriptor(options).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+
+ @Test
+ fun `returns search by text`() {
+ val query = "query"
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByText(query, any(), any()) } answers {
+ val callback = secondArg>()
+ callback.accept(searchResult)
+ }
+
+ val observer = geo.searchByText(query).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(searchResult)
+ }
+
+ @Test
+ fun `returns search by text with options`() {
+ val query = "query"
+ val options = GeoSearchByTextOptions.builder().maxResults(2).build()
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByText(query, options, any(), any()) } answers {
+ val callback = thirdArg>()
+ callback.accept(searchResult)
+ }
+
+ val observer = geo.searchByText(query, options).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(searchResult)
+ }
+
+ @Test
+ fun `returns error for search by text`() {
+ val query = "query"
+ val error = GeoException("message", "suggestion")
+ every { delegate.searchByText(query, any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.searchByText(query).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+
+ @Test
+ fun `returns error for search by text with options`() {
+ val query = "query"
+ val options = GeoSearchByTextOptions.builder().maxResults(5).build()
+ val error = GeoException("message", "suggestion")
+ every { delegate.searchByText(query, options, any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.searchByText(query, options).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+
+ @Test
+ fun `returns search by coordinates`() {
+ val coordinates = Coordinates(2.0, 5.0)
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByCoordinates(coordinates, any(), any()) } answers {
+ val callback = secondArg>()
+ callback.accept(searchResult)
+ }
+
+ val observer = geo.searchByCoordinates(coordinates).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(searchResult)
+ }
+
+ @Test
+ fun `returns search by coordinates with options`() {
+ val coordinates = Coordinates(2.0, 5.0)
+ val options = GeoSearchByCoordinatesOptions.builder().maxResults(6).build()
+ val searchResult = GeoSearchResult.withPlaces(emptyList())
+ every { delegate.searchByCoordinates(coordinates, options, any(), any()) } answers {
+ val callback = thirdArg>()
+ callback.accept(searchResult)
+ }
+
+ val observer = geo.searchByCoordinates(coordinates, options).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoErrors().assertValue(searchResult)
+ }
+
+ @Test
+ fun `returns error for search by coordinates`() {
+ val coordinates = Coordinates(3.0, 4.0)
+ val error = GeoException("message", "suggestion")
+ every { delegate.searchByCoordinates(coordinates, any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.searchByCoordinates(coordinates).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+
+ @Test
+ fun `returns error for search by coordinates with options`() {
+ val coordinates = Coordinates(3.0, 4.0)
+ val options = GeoSearchByCoordinatesOptions.builder().maxResults(6).build()
+ val error = GeoException("message", "suggestion")
+ every { delegate.searchByCoordinates(coordinates, options, any(), any()) } answers {
+ val callback = lastArg>()
+ callback.accept(error)
+ }
+
+ val observer = geo.searchByCoordinates(coordinates, options).test()
+ observer.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ observer.assertNoValues().assertError(error)
+ }
+}