Front-End Web & Mobile
Add Maps to your Android app with AWS Amplify Geo, powered by Amazon Location Service
This blog post was written by Erica Eaton – Software Development Engineer at AWS Amplify.
Today’s release of AWS Amplify Geo for Android allows developers to quickly and easily add customizable maps with markers and location search to their Android applications. The location APIs are powered by Amazon Location Service and map rendering is from the popular open-source map library, MapLibre.
Benefits
- Add maps and search functionality to your Android application.
- Leverages the cost-effectiveness and privacy benefits of Amazon Location Service.
- Uses the popular open-source library, MapLibre GL Native, for on device rendering.
What we’ll build
Today, we’ll build an Android application that allows a user to find movie theaters and the closest restaurants to the movie theater they select, so they can quickly satisfy their hunger after a long movie. To achieve this, we’ll set up Amplify Geo with the Amplify MapLibre Adapter to render a map, allow a user to search for a movie theater, and display restaurants near that theater on the map.
Prerequisites
- Install Android Studio 4.0 or higher
- Install Android SDK API level 21 or higher
- Get the latest version of the Amplify CLI by running
npm install -g @aws-amplify/cli
- If you have not yet configured the Amplify CLI, please follow the Amplify documentation to do so.
1. Set up an Android App
Let’s get started by creating a new Android project. In Android Studio, choose Create New Project. Select Empty Activity as the project template and press Next.
On the next screen, enter “Movies” for the name. Select Kotlin as the language and API 21 for the minimum SDK, then press Finish.
2. Set up Amplify Geo
Now that you have an Android project, you’ll install Amplify and set up Amplify Geo. In the terminal, navigate to your project directory and run amplify init
. Respond to the prompts that appear using the following answers:
? Enter a name for the project
`Movies`
? Initialize the project with the above configuration?
`n`
? Enter a name for the environment
`dev`
? Choose your default editor:
`Android Studio`
? Choose the type of app that you're building
`android`
? Where is your Res directory:
`app/src/main/res`
? Select the authentication method you want to use:
`AWS profile`
? Please choose the profile you want to use
`default`
You’ll see a message that your project has been successfully initialized. Now let’s set up the Geo resources. First, you’ll add the map capability using the command amplify add geo
.
? Select which capability you want to add:
Map (visualize the geospatial data)
? geo category resources require auth (Amazon Cognito). Do you want to add auth now? (Y/n)
Y
? Do you want to use the default authentication and security configuration?
Default configuration
? How do you want users to be able to sign in?
Username
? Do you want to configure advanced settings?
No, I am done.
? Provide a name for the Map:
You can use the default name or any name you'd like.
? Who can access this Map?
Authorized and Guest users
? Do you want to configure advanced settings? (y/N)
N
You now have maps configured for your project. The last Geo capability you need to add is location search. You’ll use amplify add geo
again to do this.
? Select which capability you want to add:
Location search (search by places, addresses, coordinates)
? Provide a name for the location search index (place index):
You can use the default name or any name you'd like.
? Who can access this search index?
Authorized and Guest users
? Do you want to configure advanced settings? (y/N)
N
Now that your Amplify CLI setup is complete, do amplify push
to provision the resources in the cloud. You’ve now setup all the resources you need for your Amplify project.
While the resources are being provisioned, add the Amplify dependencies to your app and configure Amplify.
First, add the following lines in your app-level build.gradle(Module: Movies.app) file to allow your app to use Java 8 features and add the necessary dependencies for Amplify Geo:
android {
compileOptions {
// Support for Java 8 features
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
// Amplify dependencies
implementation 'com.amplifyframework:core:1.35.2'
implementation 'com.amplifyframework:aws-auth-cognito:1.35.2'
implementation 'com.amplifyframework:aws-geo-location:1.0.0'
implementation 'com.amplifyframework:maplibre-adapter:1.0.0'
// Support for Java 8 features
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
}
Click “Sync now” in the notification bar at the top right to sync your project with the updated build.gradle file.
You’re almost done setting up Amplify Geo, you just need to configure Amplify in your app. Create a new Kotlin class called MoviesApp in the same directory as your MainActivity. Extend MoviesApp
from android.app.Application
. Add an onCreate
function in the MoviesApp
class that configures Amplify. When you’re done, your MoviesApp
class should look like this:
class MoviesApp: Application() {
override fun onCreate() {
super.onCreate()
try {
Amplify.addPlugin(AWSCognitoAuthPlugin())
Amplify.addPlugin(AWSLocationGeoPlugin())
Amplify.configure(applicationContext)
Log.i("MoviesApp", "Initialized Amplify")
} catch (error: AmplifyException) {
Log.e("MoviesApp", "Could not initialize Amplify", error)
}
}
}
To finish initializing Amplify in your app, add android:name=".MoviesApp"
to the application element in your app’s AndroidManifest.xml file.
3. Display a Map in your App
With the setup complete, you’ll now add a map to your app. Since your map will be used to search for places, you’ll use AmplifyMapView
, which provides built-in search capabilities. In the activity_main.xml file in the app/src/main/res/layout/ directory of your project, remove the existing contents and add the following:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.amplifyframework.geo.maplibre.view.AmplifyMapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:map_showZoomControls="true"
map:map_zoomLevel="12"
map:map_centerLatitude="47.62"
map:map_centerLongitude="-122.33"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
map:map_zoomLevel
sets the initial zoom for the map (default is a zoom level of 14 that displays building-level detail). map:map_centerLatitude
and map:map_centerLongitude
set the initial latitude and longitude the map is centered on (default is 0 latitude and 0 longitude). You can change the zoomLevel
, centerLatitude
, and centerLongitude
as you like.
In your app’s MainActivity, create a variable to store the instance of AmplifyMapView
:
private val amplifyMapView by lazy {
findViewById<AmplifyMapView>(R.id.mapView)
}
You now have an app that displays a map and allows users to search for places and it looks like this:
Don’t stop here, continue on to the next step to allow users to search for movie theaters and find nearby restaurants.
4. Find the Closest Restaurants to a Movie Theater
By default, when a place (movie theater in this case) on the map is selected, AmplifyMapView
will show a popup with more information about that place. To also search for restaurants near that movie theater, you need to listen for when a place is selected. Set the onPlaceSelect
listener in the onCreate
function of your app’s MainActivity:
amplifyMapView.onPlaceSelect { place, symbol ->
// place is an instance of AmazonLocationPlace
// symbol is an instance of Symbol from MapLibre
}
Now when a place on the map is selected, the informational popup from AmplifyMapView
will still be shown, but the onPlaceSelect
listener will also be invoked. To search for the 2 closest restaurants to a movie theater selected on the map, add the following code inside the onPlaceSelect
listener you just added:
val searchQuery = "restaurant"
val position = place.coordinates
val options = GeoSearchByTextOptions.builder()
.maxResults(2) // return 2 restaurants max
.searchArea(SearchArea.near(position)) // search near the selected movie theater
.build()
Amplify.Geo.searchByText(searchQuery, options,
{ Log.i("MoviesApp", "Searched for restaurants.") },
{ Log.e("MoviesApp", "Failed to search for restaurants", it) }
)
When the user searches for a movie theater and selects one on the map, your app now searches for the 2 closest restaurants to that movie theater. Those restaurants aren’t yet displayed on the map though. A new symbol (i.e a marker) needs to be added to the map for each of the 2 nearby restaurants. You’ll also add the place information to that symbol so it can be displayed when a user selects that restaurant. When you’re done, the onPlaceSelect
listener will look as follows:
amplifyMapView.onPlaceSelect { place, symbol ->
val searchQuery = "restaurant"
val position = place.coordinates
val options = GeoSearchByTextOptions.builder()
.maxResults(2) // return 2 restaurants max
.searchArea(SearchArea.near(position)) // search near the selected movie theater
.build()
Amplify.Geo.searchByText(searchQuery, options,
{ nearbyRestaurants ->
this.runOnUiThread {
nearbyRestaurants.places.forEach { restaurant ->
val currentRestaurant = restaurant as AmazonLocationPlace
amplifyMapView.mapView.getStyle { map, style ->
val restaurantLocation = currentRestaurant.coordinates.toLatLng()
amplifyMapView.mapView.symbolManager.create(
SymbolOptions()
.withIconImage("place-active")
.withLatLng(restaurantLocation)
.withData(currentRestaurant.toJsonElement())
)
}
}
}
},
{ Log.e("MoviesApp", "Failed to search for restaurants", it) }
)
}
It’s finally time to see the result of all your hard work. Run your app! In the search bar of the app, search for “movie theater”. You’ll see markers appear on the map for movie theaters. Click one of the markers. A popup will appear with information about that place and additional markers will be added to the map for nearby restaurants.
Clean Up
Now that you’ve finished this walkthrough, if you don’t plan to use the movies app any further, delete the backend resources to avoid incurring unexpected costs from these resources.
Use the command amplify delete
to delete the backend resources.
Conclusion
You have now built an app to search for places on a map and show restaurants near a place. In the process, you learned about multiple features Amplify Geo offers, including displaying a map in your app, searching for places near a location, and displaying location markers on the map.
Next Steps
Check out the Amplify Geo documentation to learn more about the features Amplify Geo offers. We’re working on new features for the Geo category, so keep an eye out on our Twitter and Discord communication channels. If you have any questions, let us know in #geo-help on Discord.