Skip to content

Commit

Permalink
Added support for latest SDKs (#4)
Browse files Browse the repository at this point in the history
* feat: Quick start app and tacking with geofence samples updated to work with v2 changes

ALS-1867

* feat: Code optimize

ALS-1867

* feat: Code optimize

ALS-1867

* feat: PR comment fixes and build issue fix

ALS-1867

* Remove the preview files.

* Update samples to use latest SDKs.
Also fixes up some minor issues with the API Key implementation.

* Updated sample with latest packages.

---------

Co-authored-by: shah <[email protected]>
Co-authored-by: wadhawh <[email protected]>
  • Loading branch information
3 people authored Nov 1, 2024
1 parent 2807e81 commit 8960770
Show file tree
Hide file tree
Showing 26 changed files with 250 additions and 215 deletions.
Binary file modified quick-start/README.md
Binary file not shown.
10 changes: 5 additions & 5 deletions quick-start/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ android {
versionCode = 1
versionName = "1.0.0"

buildConfigField("String", "API_KEY_REGION", "\"${customConfig.getProperty("API_KEY_REGION")}\"")
buildConfigField("String", "API_KEY", "\"${customConfig.getProperty("API_KEY")}\"")
buildConfigField("String", "IDENTITY_POOL_ID", "\"${customConfig.getProperty("IDENTITY_POOL_ID")}\"")
buildConfigField("String", "TRACKER_NAME", "\"${customConfig.getProperty("TRACKER_NAME")}\"")
buildConfigField("String", "REGION", "\"${customConfig.getProperty("REGION")}\"")
buildConfigField("String", "PLACE_INDEX", "\"${customConfig.getProperty("PLACE_INDEX")}\"")
buildConfigField("String", "MAP_NAME", "\"${customConfig.getProperty("MAP_NAME")}\"")
buildConfigField("String", "MAP_STYLE", "\"${customConfig.getProperty("MAP_STYLE")}\"")
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
Expand Down Expand Up @@ -49,7 +49,7 @@ android {
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
kotlinCompilerExtensionVersion = "1.5.7"
}
packaging {
resources {
Expand All @@ -74,9 +74,9 @@ dependencies {
implementation(libs.androidx.material3)
implementation(libs.org.maplibre.gl)
implementation(libs.com.squareup.okhttp3)
implementation(libs.location)
implementation(libs.auth)
implementation(libs.tracking)
implementation(libs.geoplaces)
testImplementation(libs.mockk)
testImplementation(libs.mockito.core)
androidTestImplementation(libs.androidx.uiautomator)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.amazon.androidquickstartapp.ui.theme.AndroidQuickStartAppTheme
import com.amazon.androidquickstartapp.ui.view.MapLoadScreen
import com.amazon.androidquickstartapp.ui.viewModel.MainViewModel
import com.amazon.androidquickstartapp.utils.Constants.REVERSE_GEO_THRESH_HOLD
import com.amazon.androidquickstartapp.utils.Constants.SERVICE_NAME
import com.amazon.androidquickstartapp.utils.Constants.TRACKER_LINE_LAYER
import com.amazon.androidquickstartapp.utils.Constants.TRACKER_LINE_SOURCE
import com.amazon.androidquickstartapp.utils.Constants.TRACKING_FREQUENCY_MILLIS
Expand All @@ -31,17 +30,13 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import org.maplibre.android.MapLibre
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.OnMapReadyCallback
import org.maplibre.android.maps.Style
import org.maplibre.android.module.http.HttpRequestUtil
import org.maplibre.geojson.Point
import software.amazon.location.auth.AuthHelper
import software.amazon.location.auth.AwsSignerInterceptor
import software.amazon.location.tracking.aws.LocationTrackingCallback
import software.amazon.location.tracking.config.LocationTrackerConfig
import software.amazon.location.tracking.database.LocationEntry
Expand All @@ -52,7 +47,6 @@ import software.amazon.location.tracking.util.TrackingSdkLogLevel
class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCameraIdleListener,
MapLibreMap.OnCameraMoveStartedListener {

private lateinit var authHelper: AuthHelper
private val mainViewModel: MainViewModel by viewModels()
private val coroutineScope = MainScope()
private var isRequestingForTracking = false
Expand Down Expand Up @@ -249,21 +243,9 @@ class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCame
private fun signInUser() {
coroutineScope.launch {
if (mainViewModel.checkValidations(this@MainActivity)) return@launch
authHelper = AuthHelper(applicationContext)
mainViewModel.initializeLocationCredentialsProvider(authHelper)
mainViewModel.initializeLocationCredentialsProvider(applicationContext)
mainViewModel.authenticated = true
mainViewModel.locationCredentialsProvider?.let {
HttpRequestUtil.setOkHttpClient(
OkHttpClient.Builder()
.addInterceptor(
AwsSignerInterceptor(
SERVICE_NAME,
mainViewModel.region,
it
)
)
.build()
)
mainViewModel.trackerCredentialsProvider?.let {
val config = LocationTrackerConfig(
trackerName = mainViewModel.trackerName,
logLevel = TrackingSdkLogLevel.DEBUG,
Expand All @@ -280,12 +262,7 @@ class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCame

override fun onMapReady(map: MapLibreMap) {
mainViewModel.mapLibreMap = map
map.setStyle(
Style.Builder()
.fromUri(
"https://maps.geo.${mainViewModel.region}.amazonaws.com/maps/v0/maps/${mainViewModel.mapName}/style-descriptor"
),
) {
map.setStyle(Style.Builder().fromUri(getMapUrl())) {
map.uiSettings.isAttributionEnabled = true
map.uiSettings.isLogoEnabled = false
map.uiSettings.attributionGravity = Gravity.BOTTOM or Gravity.END
Expand All @@ -308,6 +285,9 @@ class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCame
}
}

private fun getMapUrl() =
"https://maps.geo.${BuildConfig.API_KEY_REGION}.amazonaws.com/v2/styles/${mainViewModel.mapStyle}/descriptor?key=${BuildConfig.API_KEY}"

private fun getLabelFromPosition(latLng: LatLng) {
CoroutineScope(Dispatchers.IO).launch {
val label = mainViewModel.reverseGeocode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ import androidx.compose.runtime.setValue
import androidx.core.app.ActivityCompat
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import aws.sdk.kotlin.services.location.model.LocationException
import aws.sdk.kotlin.runtime.auth.credentials.StaticCredentialsProvider
import aws.sdk.kotlin.services.geoplaces.GeoPlacesClient
import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider
import aws.smithy.kotlin.runtime.net.url.Url
import com.amazon.androidquickstartapp.BuildConfig
import com.amazon.androidquickstartapp.R
import com.amazon.androidquickstartapp.utils.AmazonPlacesClient
import com.amazon.androidquickstartapp.utils.Constants
import com.amazon.androidquickstartapp.utils.Helper
import com.amazon.androidquickstartapp.utils.MapHelper
Expand All @@ -26,7 +31,6 @@ import org.maplibre.android.location.LocationComponent
import org.maplibre.android.location.OnLocationCameraTransitionListener
import org.maplibre.android.location.modes.CameraMode
import org.maplibre.android.maps.MapLibreMap
import com.amazon.androidquickstartapp.utils.AmazonLocationClient
import kotlinx.coroutines.async
import software.amazon.location.auth.AuthHelper
import software.amazon.location.auth.LocationCredentialsProvider
Expand All @@ -36,11 +40,12 @@ import software.amazon.location.tracking.config.LocationTrackerConfig

class MainViewModel : ViewModel() {
var locationTracker: LocationTracker? = null
var locationCredentialsProvider: LocationCredentialsProvider? = null
var trackerCredentialsProvider: LocationCredentialsProvider? = null
private var placesCredentialsProvider: LocationCredentialsProvider? = null
var authenticated by mutableStateOf(false)
var mapName by mutableStateOf(BuildConfig.MAP_NAME)
var region by mutableStateOf(BuildConfig.REGION)
var indexName by mutableStateOf(BuildConfig.PLACE_INDEX)
var mapStyle by mutableStateOf(BuildConfig.MAP_STYLE)
var apiKeyRegion by mutableStateOf(BuildConfig.API_KEY_REGION)
var apiKey by mutableStateOf(BuildConfig.API_KEY)
var identityPoolId by mutableStateOf(BuildConfig.IDENTITY_POOL_ID)
var trackerName by mutableStateOf(BuildConfig.TRACKER_NAME)
var label by mutableStateOf("")
Expand All @@ -52,10 +57,15 @@ class MainViewModel : ViewModel() {
var helper = Helper()
var mapHelper = MapHelper()
var layerSize: Int = 0
var getPlaceClient: GeoPlacesClient ?= null
var amazonPlacesClient: AmazonPlacesClient ?= null

suspend fun initializeLocationCredentialsProvider(authHelper: AuthHelper) {
locationCredentialsProvider = viewModelScope.async {
authHelper.authenticateWithCognitoIdentityPool(identityPoolId)
suspend fun initializeLocationCredentialsProvider(context: Context) {
trackerCredentialsProvider = viewModelScope.async {
AuthHelper.withCognitoIdentityPool(identityPoolId, context)
}.await()
placesCredentialsProvider = viewModelScope.async {
AuthHelper.withApiKey(apiKey, apiKeyRegion, context)
}.await()
}

Expand All @@ -71,23 +81,22 @@ class MainViewModel : ViewModel() {

suspend fun reverseGeocode(latLng: LatLng): String? {
try {
val amazonLocationClient =
locationCredentialsProvider?.getLocationClient()?.let { AmazonLocationClient(it) }
val response = amazonLocationClient?.reverseGeocode(
indexName,
if (getPlaceClient == null || amazonPlacesClient == null) {
placesCredentialsProvider?.let {
getPlaceClient =
GeoPlacesClient(it.getGeoPlacesClientConfig())
amazonPlacesClient = AmazonPlacesClient(getPlaceClient)
}
}
val response = amazonPlacesClient?.reverseGeocode(
latLng.longitude,
latLng.latitude,
mLanguage = "en",
mMaxResults = 1
)

return response?.results?.firstOrNull()?.place?.label
return response?.resultItems?.firstOrNull()?.title
} catch (e: Exception) {
if (e is LocationException && e.message.contains("expired")) {
viewModelScope.launch {
locationCredentialsProvider?.refresh()
}
}
e.printStackTrace()
return ""
}
Expand Down Expand Up @@ -147,19 +156,20 @@ class MainViewModel : ViewModel() {
)
return true
}
if (mapName.isEmpty()) {
helper.showToast(context.getString(R.string.error_please_enter_map_name), context)
if (mapStyle.isEmpty()) {
helper.showToast(context.getString(R.string.error_please_enter_map_style), context)
return true
}
if (region.isEmpty()) {
if (apiKeyRegion.isEmpty()) {
helper.showToast(context.getString(R.string.error_please_enter_region), context)
return true
}
if (indexName.isEmpty()) {
helper.showToast(
context.getString(R.string.error_please_enter_place_index),
context
)
if (apiKey.isEmpty()) {
helper.showToast(context.getString(R.string.error_please_enter_api_key), context)
return true
}
if (trackerName.isEmpty()) {
helper.showToast(context.getString(R.string.error_please_enter_tracker_name), context)
return true
}
return false
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,43 @@
package com.amazon.androidquickstartapp.utils


import aws.sdk.kotlin.services.location.LocationClient
import aws.sdk.kotlin.services.location.model.SearchPlaceIndexForPositionRequest
import aws.sdk.kotlin.services.location.model.SearchPlaceIndexForPositionResponse
import aws.sdk.kotlin.services.geoplaces.GeoPlacesClient
import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeRequest
import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeResponse
import com.amazon.androidquickstartapp.BuildConfig

/**
* Provides methods to interact with the Amazon Location service.
*
* @property locationClient An instance of LocationClient used for making requests to the Amazon Location service.
* @property geoPlacesClient An instance of LocationClient used for making requests to the Amazon Location service.
*/
class AmazonLocationClient(
private val locationClient: LocationClient
class AmazonPlacesClient(
private val geoPlacesClient: GeoPlacesClient?
) {

/**
* Reverse geocodes a location specified by longitude and latitude coordinates.
*
* @param placeIndexName The name of the place index resource to use for the reverse geocoding request.
* @param longitude The longitude of the location to reverse geocode.
* @param latitude The latitude of the location to reverse geocode.
* @param mLanguage The language to use for the reverse geocoding results.
* @param mMaxResults The maximum number of results to return.
* @return A response containing the reverse geocoding results.
*/
suspend fun reverseGeocode(
placeIndexName: String,
longitude: Double,
latitude: Double,
mLanguage: String,
mMaxResults: Int
): SearchPlaceIndexForPositionResponse {
val request = SearchPlaceIndexForPositionRequest {
indexName = placeIndexName
position = listOf(longitude, latitude)
): ReverseGeocodeResponse? {
val request = ReverseGeocodeRequest {
maxResults = mMaxResults
language = mLanguage
key = BuildConfig.API_KEY
queryPosition = listOf(longitude, latitude)
}

val response = locationClient.searchPlaceIndexForPosition(request)
val response = geoPlacesClient?.reverseGeocode(request)
return response
}
}
7 changes: 4 additions & 3 deletions quick-start/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
<string name="label_region">Region</string>
<string name="label_place_index_name">Place index name</string>
<string name="error_please_enter_identity_pool_id">Please enter identity pool id</string>
<string name="error_please_enter_map_name">Please enter map name</string>
<string name="error_please_enter_region">Please enter region</string>
<string name="error_please_enter_place_index">Please enter place index name</string>
<string name="error_please_enter_map_style">Please enter map style</string>
<string name="error_please_enter_region">Please enter API Key region</string>
<string name="error_please_enter_api_key">Please enter API Key</string>
<string name="error_please_enter_tracker_name">Please enter Tracker Name</string>
<string name="label_permission_required">Permission Required</string>
<string name="label_open_settings">Open Settings</string>
<string name="label_cancel">Cancel</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.amazon.androidquickstartapp.ui.viewModel

import android.content.Context
import aws.sdk.kotlin.services.location.LocationClient
import aws.sdk.kotlin.services.location.model.Place
import aws.sdk.kotlin.services.location.model.SearchForPositionResult
import aws.sdk.kotlin.services.location.model.SearchPlaceIndexForPositionResponse
import aws.sdk.kotlin.services.geoplaces.GeoPlacesClient
import aws.sdk.kotlin.services.geoplaces.model.Address
import aws.sdk.kotlin.services.geoplaces.model.PlaceType
import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeResponse
import aws.sdk.kotlin.services.geoplaces.model.ReverseGeocodeResultItem
import com.amazon.androidquickstartapp.utils.AmazonPlacesClient
import com.amazon.androidquickstartapp.utils.Constants.EXPECTED_LABEL
import io.mockk.coEvery

import io.mockk.mockk
import io.mockk.mockkConstructor
import junit.framework.TestCase.assertEquals
Expand All @@ -16,7 +17,6 @@ import org.junit.Before
import org.junit.Test
import org.maplibre.android.geometry.LatLng
import org.mockito.Mock
import com.amazon.androidquickstartapp.utils.AmazonLocationClient
import software.amazon.location.auth.LocationCredentialsProvider


Expand All @@ -25,38 +25,38 @@ class MainViewModelReverseGeocodeTest {
private lateinit var viewModel: MainViewModel

@Mock
private lateinit var locationCredentialsProvider: LocationCredentialsProvider
private lateinit var geoPlacesClient: GeoPlacesClient

@Mock
lateinit var context: Context

@Before
fun setUp() {
context = mockk(relaxed = true)
locationCredentialsProvider = mockk()
geoPlacesClient = mockk()
viewModel = MainViewModel()
mockkConstructor(LocationCredentialsProvider::class)
mockkConstructor(AmazonLocationClient::class)
mockkConstructor(AmazonPlacesClient::class)
}

@Test
fun `test reverseGeocode`() {
val mockAmazonLocationClient = mockk<LocationClient>()
coEvery {
locationCredentialsProvider.getLocationClient()
} returns mockAmazonLocationClient
val searchPlaceIndexForPositionResponse = SearchPlaceIndexForPositionResponse.invoke {
results = listOf(SearchForPositionResult.invoke {
distance = 20.0
val mockAmazonLocationClient = mockk<AmazonPlacesClient>()
viewModel.getPlaceClient = geoPlacesClient
viewModel.amazonPlacesClient = mockAmazonLocationClient
val reverseGeocodeResponse = ReverseGeocodeResponse {
resultItems = listOf(ReverseGeocodeResultItem {
distance = 20L
placeId = "11"
place = Place.invoke {
title = "test"
placeType = PlaceType.Block
address = Address {
label = EXPECTED_LABEL
}
})
summary = null
pricingBucket = "test"
}
viewModel.locationCredentialsProvider = locationCredentialsProvider
coEvery { mockAmazonLocationClient.searchPlaceIndexForPosition(any()) } returns searchPlaceIndexForPositionResponse
coEvery { mockAmazonLocationClient.reverseGeocode(any(), any(), any(), any()) } returns reverseGeocodeResponse
val latLng = LatLng(37.7749, -122.4194)

runBlocking {
Expand Down
Loading

0 comments on commit 8960770

Please sign in to comment.