Android 앱 생성 - Amazon Location Service

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

Android 앱 생성

이 섹션에서는 지도, 위치 검색 기능, 포그라운드 추적 기능을 갖춘 Android 애플리케이션을 생성합니다. 먼저 Amazon 위치 리소스, Amazon Cognito 자격 증명 및 애플리케이션을 위한 API 키를 생성합니다.

앱을 위한 Amazon Location 리소스 생성

아직 리소스가 없는 경우 애플리케이션에서 사용할 Amazon Location 리소스를 생성해야 합니다. 여기서는 애플리케이션에 지도를 표시하는 맵 리소스, 지도에서 위치를 검색하는 장소 색인, 지도에서 객체를 추적하는 추적기를 생성합니다.

애플리케이션에 위치 리소스를 추가하려면
  1. 사용하려는 맵 스타일을 선택합니다.

    1. Amazon Location 콘솔의 페이지에서 맵 생성을 선택하여 맵 스타일을 미리 볼 수 있습니다.

    2. 새 맵 리소스의 이름설명을 추가합니다. 맵 리소스에 사용하는 이름을 기록해 둡니다. 이는 튜토리얼 뒷부분에서 스크립트 파일을 만들 때 필요합니다.

    3. 맵에는 HERE 맵 스타일을 선택하는 것이 좋습니다.

      참고

      맵 스타일을 선택하면 사용할 맵 데이터 공급자도 선택됩니다. 애플리케이션이 배송 차량이나 직원 등 업무에서 사용하는 자산을 추적하거나 라우팅하는 경우 HERE만을 지리적 위치 제공업체로 사용할 수 있습니다. 자세한 내용은 AWS 서비스 약관의 섹션 82를 참조하세요.

    4. Amazon Location 이용 약관에 동의한 다음 맵 생성을 선택합니다. 선택한 맵과 상호 작용할 수 있습니다. 확대, 축소하거나 원하는 방향으로 이동할 수 있습니다.

    5. 새 맵 리소스에 대해 표시되는 Amazon 리소스 이름(ARN)을 기록해 둡니다. 이 튜토리얼의 뒷부분에서 이를 사용하여 올바른 인증을 생성할 수 있습니다.

  2. 사용하려는 장소 색인을 선택합니다.

    1. Amazon Location 콘솔의 장소 색인 페이지에서 장소 색인 생성을 선택합니다.

    2. 새 장소 색인 리소스의 이름설명을 추가합니다. 장소 색인 리소스에 사용하는 이름을 적어 둡니다. 이는 튜토리얼 뒷부분에서 스크립트 파일을 만들 때 필요합니다.

    3. 데이터 공급자를 선택합니다.

      참고

      대부분의 경우 이미 선택한 맵 공급자와 일치하는 데이터 공급자를 선택하세요. 이렇게 하면 검색 결과가 맵과 일치되게 할 수 있습니다.

      애플리케이션이 배송 차량이나 직원 등 업무에서 사용하는 자산을 추적하거나 라우팅하는 경우 HERE만을 지리적 위치 제공업체로 사용할 수 있습니다. 자세한 내용은 AWS 서비스 약관의 섹션 82를 참조하세요.

    4. 데이터 스토리지 옵션을 선택합니다. 이 튜토리얼에서는 결과가 저장되지 않으므로 아니오, 한 번만 사용합니다를 선택할 수 있습니다.

    5. Amazon Location 이용 약관에 동의한 다음 장소 색인 생성을 선택합니다.

    6. 새 장소 색인 리소스에 표시되는 ARN을 기록해 둡니다. 이는 튜토리얼의 다음 섹션에서 올바른 인증을 만드는 데 사용합니다.

  3. Amazon 위치 콘솔을 사용하여 트래커를 만들려면

    1. Amazon Location Service 콘솔을 엽니다.

    2. 왼쪽 탐색 창에서 트래커를 선택합니다.

    3. 트래커 생성을 선택합니다.

    4. 필수 필드를 모두 입력합니다.

    5. 포지션 필터링에서는 기본 설정인 을 사용하는 것이 좋습니다. TimeBased

    6. 트래커 생성을 선택하여 완료하세요.

애플리케이션에 대한 인증 설정

이 자습서에서 만든 애플리케이션은 익명으로 사용되므로 사용자가 로그인하지 않아도 애플리케이션을 사용할 AWS 수 있습니다. 하지만 Amazon Location Service API를 사용하려면 인증이 필요합니다. API 키 또는 Amazon Cognito를 사용하여 익명 사용자에게 인증 및 권한 부여를 제공할 수 있습니다. 이 자습서에서는 Amazon Cognito와 API 키를 사용하여 애플리케이션을 인증합니다.

참고

Amazon Location Service에서의 Amazon Cognito 또는 API 키 사용에 대한 자세한 내용은 섹션 Amazon Location Service에 액세스 권한 부여을 참조하세요.

다음 자습서에서는 생성한 지도, 장소 색인, 추적기에 대한 인증을 설정하는 방법과 Amazon Location에 대한 권한을 설정하는 방법을 보여줍니다.

인증 설정
  1. Amazon 로케이션 콘솔로 이동하여 왼쪽 메뉴에서 API 키를 선택합니다.

  2. 'API 키 생성'을 클릭합니다. API 키는 이전에 생성한 Amazon Location Service 리소스와 동일한 AWS 계정 및 지역에 있어야 한다는 점을 기억하십시오.

  3. 'API 키 생성' 페이지에 필수 세부 정보를 입력합니다.

    • 이름: API 키의 이름을 입력합니다 (예:). MyAppKey

    • 리소스: 이전에 생성한 Amazon Location Service Map and Place 인덱스 리소스를 선택합니다. '리소스 추가'를 선택하여 여러 리소스를 추가할 수 있습니다. 이렇게 하면 API 키를 지정된 리소스와 함께 사용할 수 있습니다.

    • 조치: 이 API 키에 대해 승인된 작업을 지정합니다. 최소한 자습서가 의도한 대로 geo:SearchPlaceIndexForPosition 작동하도록 geo:GetMap 선택하고 확인하십시오.

    • 선택 사항으로 설명, 만료 시간, 태그 또는 리퍼러 등을 추가하여 키 사용을 특정 도메인으로 제한하여 자습서가 해당 도메인 내에서만 작동하도록 할 수 있습니다. https://www.example.com

  4. API 키 생성을 클릭하여 API 키를 생성합니다.

  5. v1.public.a1b2c3d4예를 들어 나중에 자습서에서 사용할 수 있도록 API 키 표시를 선택하고 키 값을 복사합니다.

추적을 위한 IAM 정책을 생성합니다.
  1. 관리자 권한이 있는 사용자로 https://console.aws.amazon.com/iam/의 IAM 콘솔에 로그인합니다.

  2. 탐색 창에서 정책을 선택합니다.

  3. 콘텐츠 창에서 정책 생성을 선택합니다.

  4. JSON 옵션을 선택한 다음 이 JSON 정책을 복사하여 JSON 텍스트 상자에 붙여넣습니다.

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "geo:GetMapTile", "geo:GetMapStyleDescriptor", "geo:GetMapSprites", "geo:GetMapGlyphs", "geo:SearchPlaceIndexForPosition", "geo:GetDevicePositionHistory", "geo:BatchUpdateDevicePosition" ], "Resource": [ "arn:aws:geo:{Region}:{Account}:map/{MapName}", "arn:aws:geo:{Region}:{Account}:place-index/{IndexName}", "arn:aws:geo:{Region}:{Account}:tracker/{TrackerName}" ] } ] }

    다음은 추적에 대한 정책 예제입니다. 이 예제를 자체 정책에 사용하려면,, TrackerName자리 표시자를 바꾸십시오. Region Account

    참고

    인증되지 않은 자격 증명 풀은 보안되지 않은 인터넷 사이트에 노출되기 위한 것이지만, 이는 기간 제한이 있는 표준 AWS 자격 증명으로 교환된다는 점에 유의하십시오.

    인증되지 않은 자격 증명 풀과 관련된 IAM 역할의 범위를 적절하게 지정하는 것이 중요합니다. Amazon Location Service와 함께 Amazon Cognito에서 정책을 사용하고 적절하게 범위를 지정하는 방법에 대한 자세한 내용은 Amazon Location Service에 대한 액세스 권한 부여를 참조하십시오.

  5. 검토 및 생성 페이지에서 정책 이름 필드에 이름을 입력합니다. 정책에서 부여한 권한을 검토한 다음 [Create Policy] 를 선택하여 작업 내용을 저장합니다.

새로운 정책이 관리형 정책 목록에 나타나며 연결 준비가 완료됩니다.

추적에 대한 인증을 설정하세요.
  1. Amazon Cognito 콘솔에서 맵 애플리케이션에 대한 인증을 설정합니다.

  2. 자격 증명 풀 페이지를 엽니다.

    참고

    생성한 풀은 이전 섹션에서 생성한 Amazon Location Service 리소스와 동일한 AWS 계정 및 AWS 지역에 있어야 합니다.

  3. 자격 증명 풀 생성을 선택합니다.

  4. ID 풀 신뢰 구성 단계부터 시작합니다. 사용자 액세스 인증의 경우 게스트 액세스를 선택하고 다음을 누릅니다.

  5. 권한 구성 페이지에서 기존 IAM 역할 사용을 선택하고 이전 단계에서 생성한 IAM 역할의 이름을 입력합니다. 준비가 되면 다음을 눌러 다음 단계로 넘어갑니다.

  6. 속성 구성 페이지에서 자격 증명 풀의 이름을 입력합니다. 그런 다음 [다음] 을 누릅니다.

  7. 검토 및 생성 페이지에서 제공되는 모든 정보를 검토한 다음 ID 풀 생성을 누릅니다.

  8. ID 풀 페이지를 열고 방금 생성한 자격 증명 풀을 선택합니다. 그런 다음 나중에 사용할 내용을 IdentityPoolId 브라우저 스크립트에 복사하거나 기록해 둡니다.

기본 Android 애플리케이션 만들기

이 자습서에서는 지도를 포함하고 사용자가 지도상의 특정 위치에 있는 내용을 찾을 수 있도록 하는 Android 애플리케이션을 만듭니다.

먼저 Android 스튜디오의 새 프로젝트 마법사를 사용하여 빈 Kotlin 애플리케이션을 생성합니다.

빈 애플리케이션을 만들려면 () AndroidStudio
  1. 시작 AndroidStudio. 메뉴를 열고 [파일], [새로 만들기], [ 프로젝트] 를 선택합니다.

  2. 휴대폰 및 태블릿 탭에서 빈 활동을 선택한 후 다음을 선택합니다.

  3. 애플리케이션의 이름, 패키지 이름저장 위치를 선택합니다.

  4. 언어 드롭다운 목록에서 Kotlin을 선택합니다.

  5. 마침을 선택하여 빈 애플리케이션을 생성합니다.

애플리케이션에 대화형 맵 추가

이제 기본 애플리케이션을 만들었으므로 애플리케이션에 맵 컨트롤을 추가할 수 있습니다. 이 자습서에서는 맵 뷰를 관리하는 데 API 키를 사용합니다. 맵 컨트롤 자체는 API 키와 함께 MapLibre 네이티브 라이브러리의 일부이며 MapLibre, 맵 데이터는 Amazon Location에서 가져옵니다.

애플리케이션에 맵을 추가하려면 다음 작업을 수행해야 합니다.

  • 프로젝트에 MapLibre 종속성을 추가합니다.

  • compose를 사용하여 맵 뷰 코드를 설정합니다.

  • 지도를 표시하는 코드를 작성하세요.

앱에 맵을 추가하려면 다음 절차를 따르세요.

  1. 프로젝트에 MapLibre 종속 항목 추가

    1. 에서 AndroidStudio 보기 메뉴를 선택하고 도구, 창, 프로젝트를 선택합니다. 그러면 프로젝트 창이 열리고 프로젝트의 모든 파일에 접근할 수 있습니다.

    2. 프로젝트 창에서 gradle을 연 다음 트리 뷰에서 libs.versions.toml 파일을 엽니다. 그러면 편집할 libs.versions.toml 파일이 열립니다. 이제 libs.versions.toml 파일에 아래 버전과 라이브러리 데이터를 추가하세요.

      [versions] ... auth = "0.2.4" tracking = "0.2.4" [libraries] ... auth = { group = "software.amazon.location", name = "auth", version.ref = "auth" } tracking = { module = "software.amazon.location:tracking", version.ref = "tracking" } [plugins] ...
    3. libs.versions.toml파일 편집을 마친 후에는 프로젝트를 다시 AndroidStudio 동기화해야 합니다. libs.versions.toml편집 창 상단에 AndroidStudio 동기화하라는 메시지가 표시됩니다. 계속하기 전에 'Sync Now'를 선택하여 프로젝트를 동기화하세요.

    4. 프로젝트 창의 트리 뷰에서 Gradle Script를 열고 애플리케이션 모듈에 사용할 build.gradle 파일을 선택합니다. 그러면 편집할 build.gradle 파일이 열립니다.

    5. 파일 하단의 종속성 섹션에 다음 종속 항목을 추가합니다.

      dependencies { ... implementation(libs.org.maplibre.gl) }
    6. Gradle 종속 항목 추가를 완료한 후에는 Android 스튜디오에서 프로젝트를 다시 동기화해야 합니다. Android 스튜디오의 build.gradle 편집 창 상단에서 Sync Now를 선택하여 계속하기 전에 프로젝트를 동기화합니다.

  2. 이제 compose를 사용하여 맵 뷰 코드를 설정해 보겠습니다. 다음 단계를 사용합니다.

    1. 프로젝트 창에서 트리 뷰의 패키지 이름을 App, Java, 열고 ui 폴더로 이동한 다음 ui 폴더 안에 있는 디렉토리를 생성합니다.

    2. 디렉터리 안에서 MapLoadScreen.kt 파일을 생성합니다.

    3. 다음 코드를 MapLoadScreen.kt 파일에 추가합니다.

      import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView import org.maplibre.android.maps.OnMapReadyCallback @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  3. 지도를 표시하는 코드를 작성하세요.

    1. 다음 코드를 MainActivity.kt 파일에 추가합니다.

      // ...other imports 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 class MainActivity : ComponentActivity(), OnMapReadyCallback { private val region = "YOUR_AWS_REGION" private val mapName = "YOUR_AWS_MAP_NAME" private val apiKey = "YOUR_AWS_API_KEY" override fun onCreate(savedInstanceState: Bundle?) { MapLibre.getInstance(this) super.onCreate(savedInstanceState) setContent { TestMapAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MapLoadScreen(this) } } } } override fun onMapReady(map: MapLibreMap) { map.setStyle( Style.Builder() .fromUri( "https://maps.geo.$region.amazonaws.com/maps/v0/maps/$mapName/style-descriptor?key=$apiKey" ), ) { map.uiSettings.isAttributionEnabled = true map.uiSettings.isLogoEnabled = false map.uiSettings.attributionGravity = Gravity.BOTTOM or Gravity.END val initialPosition = LatLng(47.6160281982247, -122.32642111977668) map.cameraPosition = CameraPosition.Builder() .target(initialPosition) .zoom(14.0) .build() } } }
    2. MainActivity.kt 파일을 저장합니다. 이제 애플리케이션을 구축할 수 있습니다. 앱을 실행하려면 에뮬레이션할 기기를 설정하거나 기기에서 앱을 사용해야 할 수 있습니다. AndroidStudio 이 앱을 사용하면 맵 컨트롤이 어떻게 작동하는지 확인할 수 있습니다. 맵에서 드래그한 다음 손가락을 집어 확대하여 패닝할 수 있습니다.

      다음 섹션에서는 지도에 마커를 추가하고 맵을 이동할 때 마커가 있는 위치의 주소를 표시합니다.

애플리케이션에 역지오코딩 검색 추가

이제 특정 위치에서 항목을 찾을 수 있는 애플리케이션에 역지오코딩 검색을 추가해 보겠습니다. Android 앱 사용을 단순화하기 위해 화면 중앙을 검색해 보겠습니다. 새 위치를 찾으려면 지도를 검색하려는 위치로 이동하세요. 지도 중앙에 마커를 배치하여 검색 위치를 표시합니다.

역방향 지오코딩 검색 추가는 두 부분으로 구성됩니다.

  • 화면 중앙에 마커를 추가하여 검색 위치를 사용자에게 표시합니다.

  • 결과를 표시할 텍스트 상자를 추가한 다음 마커 위치에 무엇이 있는지 검색하여 텍스트 상자에 표시합니다.

애플리케이션에 마커를 추가하려면
  1. 이 이미지를 프로젝트의 app/res/drawable 폴더에 저장합니다 red_marker.png (에서 이미지에 액세스할 수도 있음) GitHub. 이미지를 직접 만들 수도 있습니다. 표시하지 않으려는 부분에 투명도가 있는.png 파일을 사용할 수도 있습니다.

  2. MapLoadScreen.kt 파일에 다음 코드를 추가합니다.

    // ...other imports import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.ui.Alignment import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.amazon.testmapapp.R @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) Box( modifier = Modifier .align(Alignment.Center), ) { Image( painter = painterResource(id = R.drawable.red_marker), contentDescription = "marker", modifier = Modifier .size(40.dp) .align(Alignment.Center), ) } } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  3. 앱을 빌드하고 실행하여 기능을 미리 살펴보세요.

이제 앱 화면에 마커가 생겼습니다. 이 경우에는 움직이지 않는 정적 이미지입니다. 맵 뷰의 중앙을 표시하는 데 사용되며, 여기서 검색할 위치입니다. 다음 절차에서는 해당 위치에 검색을 추가합니다.

특정 위치의 역방향 지오코딩 검색을 앱에 추가하려면
  1. 프로젝트 창의 트리 뷰에서 Gradle to libs.versions.toml file을 엽니다. 그러면 편집할 libs.versions.toml 파일이 열립니다. 이제 libs.versions.toml 파일에 아래 버전과 라이브러리 데이터를 추가하세요.

    [versions] ... okhttp = "4.12.0" [libraries] ... com-squareup-okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } [plugins] ...
  2. libs.versions.toml파일 편집을 마친 후에는 프로젝트를 다시 AndroidStudio 동기화해야 합니다. libs.versions.toml편집 창 상단에 AndroidStudio 동기화하라는 메시지가 표시됩니다. 계속하기 전에 'Sync Now'를 선택하여 프로젝트를 동기화하세요.

  3. 프로젝트 창의 트리 뷰에서 Gradle Script를 열고 애플리케이션 모듈에 사용할 build.gradle 파일을 선택합니다. 그러면 편집할 build.gradle 파일이 열립니다.

  4. 파일 하단의 종속성 섹션에 다음 종속 항목을 추가합니다.

    dependencies { ... implementation(libs.com.squareup.okhttp3) }
  5. Gradle 종속성 편집을 마친 후에는 프로젝트를 다시 AndroidStudio 동기화해야 합니다. build.gradle편집 창 상단에 AndroidStudio 동기화하라는 메시지가 표시됩니다. SyncNow계속하기 전에 프로젝트 동기화를 선택하십시오.

  6. 이제 트리 뷰에서 요청 디렉터리에 데이터를 추가하고 ReverseGeocodeRequest.kt 데이터 클래스를 생성합니다. 클래스에 다음 코드를 추가합니다.

    import com.google.gson.annotations.SerializedName data class ReverseGeocodeRequest( @SerializedName("Language") val language: String, @SerializedName("MaxResults") val maxResults: Int, @SerializedName("Position") val position: ListDouble )
  7. 이제 트리 뷰에서 응답 디렉터리에 데이터를 추가하고 ReverseGeocodeResponse.kt 데이터 클래스를 생성합니다. 그 안에 다음 코드를 추가합니다.

    import com.google.gson.annotations.SerializedName data class ReverseGeocodeResponse( @SerializedName("Results") val results: ListResult ) data class Result( @SerializedName("Place") val place: Place ) data class Place( @SerializedName("Label") val label: String )
  8. 이제 프로젝트 창에서 트리 뷰에서 App, Java, 패키지 이름을 열고 ui 폴더 안에 있는 ui 폴더로 이동하여 ViewModel 디렉토리를 생성합니다.

  9. 뷰모델 디렉터리 내에서 파일을 생성합니다MainViewModel.kt.

  10. 다음 코드를 MainViewModel.kt 파일에 추가합니다.

    import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import com.amazon.testmapapp.data.request.ReverseGeocodeRequest import com.amazon.testmapapp.data.response.ReverseGeocodeResponse import com.google.gson.Gson import java.io.IOException import okhttp3.Call import okhttp3.Callback import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.MapLibreMap class MainViewModel : ViewModel() { var label by mutableStateOf("") var isLabelAdded: Boolean by mutableStateOf(false) var client = OkHttpClient() var mapLibreMap: MapLibreMap? = null fun reverseGeocode(latLng: LatLng, apiKey: String) { val region = "YOUR_AWS_REGION" val indexName = "YOUR_AWS_PLACE_INDEX" val url = "https://places.geo.${region}.amazonaws.com/places/v0/indexes/${indexName}/search/position?key=${apiKey}" val requestBody = ReverseGeocodeRequest( language = "en", maxResults = 1, position = listOf(latLng.longitude, latLng.latitude) ) val json = Gson().toJson(requestBody) val mediaType = "application/json".toMediaTypeOrNull() val request = Request.Builder() .url(url) .post(json.toRequestBody(mediaType)) .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { e.printStackTrace() } override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { val jsonResponse = response.body?.string() val reverseGeocodeResponse = Gson().fromJson(jsonResponse, ReverseGeocodeResponse::class.java) val responseLabel = reverseGeocodeResponse.results.firstOrNull()?.place?.label if (responseLabel != null) { label = responseLabel isLabelAdded = true } } } }) } }
  11. 파일이 아직 열리지 않은 경우 이전 절차에서와 같이 MapLoadScreen.kt 파일을 여세요. 다음 코드를 추가합니다. 그러면 해당 위치의 리버스 지오코딩 검색 결과를 볼 수 있는 Compose Text 뷰가 생성됩니다.

    // ...other imports import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.sp import com.amazon.testmapapp.ui.viewModel.MainViewModel @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, mainViewModel: MainViewModel, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) Box( modifier = Modifier .align(Alignment.Center), ) { Image( painter = painterResource(id = R.drawable.red_marker), contentDescription = "marker", modifier = Modifier .size(40.dp) .align(Alignment.Center), ) } if (mainViewModel.isLabelAdded) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom ) { Box( modifier = Modifier .fillMaxWidth() .background(Color.White), ) { Text( text = mainViewModel.label, modifier = Modifier .padding(16.dp) .align(Alignment.Center) .testTag("label") .semantics { contentDescription = "label" }, fontSize = 14.sp, ) } Spacer(modifier = Modifier.height(80.dp)) } } } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  12. 앱의 Java에 있는 패키지 이름 폴더에서 AndroidStudio 파일을 엽니다. MainActivity.kt 다음과 같이 코드를 수정합니다.

    // ...other imports import androidx.activity.viewModels import com.amazon.testmapapp.ui.viewModel.MainViewModel class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCameraMoveStartedListener, MapLibreMap.OnCameraIdleListener { private val mainViewModel: MainViewModel by viewModels() private val region = "YOUR_AWS_REGION" private val mapName = "YOUR_AWS_MAP_NAME" private val apiKey = "YOUR_AWS_API_KEY" override fun onCreate(savedInstanceState: Bundle?) { MapLibre.getInstance(this) super.onCreate(savedInstanceState) setContent { TestMapAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MapLoadScreen(this, mainViewModel) } } } } override fun onMapReady(map: MapLibreMap) { map.setStyle( Style.Builder() .fromUri( "https://maps.geo.$region.amazonaws.com/maps/v0/maps/$mapName/style-descriptor?key=$apiKey" ), ) { map.uiSettings.isAttributionEnabled = true map.uiSettings.isLogoEnabled = false map.uiSettings.attributionGravity = Gravity.BOTTOM or Gravity.END val initialPosition = LatLng(47.6160281982247, -122.32642111977668) map.cameraPosition = CameraPosition.Builder() .target(initialPosition) .zoom(14.0) .build() map.addOnCameraMoveStartedListener(this) map.addOnCameraIdleListener(this) map.cameraPosition.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } override fun onCameraMoveStarted(p0: Int) { mainViewModel.label = "" mainViewModel.isLabelAdded = false } override fun onCameraIdle() { if (!mainViewModel.isLabelAdded) { mainViewModel.mapLibreMap?.cameraPosition?.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } }

    이 코드는 맵 뷰에서 작동합니다. 가상 카메라 위치는 맵 뷰를 정의합니다 MapLibre. 맵을 이동하는 것은 가상 카메라를 움직이는 것으로 생각할 수 있습니다.

    • ViewModel 레이블 변수 포함: 이 변수는 텍스트 작성 뷰의 데이터를 설정합니다.

    • onMapReady레이블 변수 포함: 이 변수는 작성 텍스트 보기에서 데이터를 설정합니다. ----sep----:이 함수는 두 개의 새 이벤트를 등록하도록 업데이트되었습니다.

    • onCameraMove이벤트는 사용자가 맵을 이동할 때마다 발생합니다. 일반적으로 맵을 이동할 때는 사용자가 맵 이동을 완료할 때까지 검색을 숨기려고 합니다.

    • onCameraIdle이벤트는 사용자가 맵 이동을 일시 중지할 때 발생합니다. 이 이벤트는 리버스 지오코드 함수를 호출하여 지도 중앙을 검색합니다.

    • reverseGeocode(latLng: LatLng, apiKey: String): 이벤트에서 onCameraIdle 호출되는 이 함수는 지도 중앙에서 위치를 검색하고 레이블을 업데이트하여 결과를 표시합니다. 지도의 중심 (카메라가 바라보는 위치) 을 정의하는 카메라 타겟을 사용합니다.

  13. 파일을 저장하고 앱을 빌드하고 실행하여 제대로 작동하는지 확인하세요.

검색 기능이 있는 퀵 스타트 애플리케이션이 완성되었습니다.

애플리케이션에 추적 추가

샘플 애플리케이션에 추적을 추가하려면 다음 단계를 따르십시오.

  1. 프로젝트에 추적 및 인증 SDK 종속 항목을 추가하세요.

  2. .xml 파일에 권한 및 서비스 항목을 포함하세요. AndroidManifest

  3. compose를 사용하여 추적 시작/중지 버튼 코드를 설정합니다.

  4. LocationTracker 객체를 만들고 추적을 시작하고 중지하기 위한 코드를 추가합니다.

  5. Android 에뮬레이터로 테스트 경로를 만드세요.

  1. 프로젝트에 추적 및 인증 SDK 종속 항목을 추가하세요.

    1. 프로젝트 창에서 gradle을 연 다음 트리 뷰에서 libs.versions.toml 파일을 엽니다. 그러면 편집할 libs.versions.toml 파일이 열립니다. 이제 libs.versions.toml 파일에 아래 버전과 라이브러리 데이터를 추가하세요.

      [versions] ... auth = "0.0.1" tracking = "0.0.1" [libraries] ... auth = { group = "software.amazon.location", name = "auth", version.ref = "auth" } tracking = { module = "software.amazon.location:tracking", version.ref = "tracking" } [plugins] ...
    2. libs.versions.toml파일 편집을 마친 후에는 프로젝트를 다시 AndroidStudio 동기화해야 합니다. libs.versions.toml편집 창 상단에 AndroidStudio 동기화하라는 메시지가 표시됩니다. 계속하기 전에 'Sync Now'를 선택하여 프로젝트를 동기화하세요.

    3. 프로젝트 창의 트리 뷰에서 Gradle Script를 열고 애플리케이션 모듈에 사용할 build.gradle 파일을 선택합니다. 그러면 편집할 build.gradle 파일이 열립니다.

    4. 파일 하단의 종속성 섹션에 다음 종속 항목을 추가합니다.

      dependencies { ... implementation(libs.auth) implementation(libs.tracking) }
    5. Gradle 종속성 편집을 마친 후에는 프로젝트를 다시 AndroidStudio 동기화해야 합니다. build.gradle 편집 창 상단에 동기화하라는 메시지가 표시됩니다. AndroidStudio SyncNow계속하기 전에 프로젝트 동기화를 선택하세요.

  2. AndroidManifest.xml 파일에 권한 및 서비스 항목을 포함하세요.

    1. 에 올바른 권한 및 서비스 항목을 포함하려면 다음 코드로 파일을 업데이트하십시오AndroidManifest.xml file.

      <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.AndroidQuickStartApp" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@style/Theme.AndroidQuickStartApp"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
  3. compose로 추적 시작/중지 버튼 코드를 설정하세요.

    1. ic_pause 및 ic_play라는 드로어블 아래에 res에서 재생 및 일시 중지 이미지 두 개를 추가합니다. 에서 이미지에 액세스할 수도 있습니다. GitHub

    2. 파일이 아직 열리지 않은 경우 이전 절차에서와 같이 MapLoadScreen.kt 파일을 여세요. 다음 코드를 추가합니다. 그러면 Compose Button 뷰가 생성되어 이를 클릭하여 추적을 시작하고 중지할 수 있습니다.

      // ...other imports import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, mainViewModel: MainViewModel, onStartStopTrackingClick: () -> Unit ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) Box( modifier = Modifier .align(Alignment.Center), ) { Image( painter = painterResource(id = R.drawable.red_marker), contentDescription = "marker", modifier = Modifier .size(40.dp) .align(Alignment.Center), ) } if (mainViewModel.isLabelAdded) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom ) { Box( modifier = Modifier .fillMaxWidth() .background(Color.White), ) { Text( text = mainViewModel.label, modifier = Modifier .padding(16.dp) .align(Alignment.Center) .testTag("label") .semantics { contentDescription = "label" }, fontSize = 14.sp, ) } Spacer(modifier = Modifier.height(80.dp)) } } Column( modifier = Modifier .fillMaxSize() .padding(bottom = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Bottom, ) { Button( onClick = onStartStopTrackingClick, modifier = Modifier .padding(horizontal = 16.dp) ) { Text( text = if (mainViewModel.isLocationTrackingForegroundActive) "Stop tracking" else "Start tracking", color = Color.Black ) Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) Image( painter = painterResource(id = if (mainViewModel.isLocationTrackingForegroundActive) R.drawable.ic_pause else R.drawable.ic_play), contentDescription = if (mainViewModel.isLocationTrackingForegroundActive) "stop_tracking" else "start_tracking" ) } } } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  4. LocationTracker 객체를 만들고 추적을 시작하고 중지하기 위한 코드를 추가하세요.

    1. MainViewModel.kt파일 내에 다음 코드를 추가합니다.

      ... var isLocationTrackingForegroundActive: Boolean by mutableStateOf(false) var locationTracker: LocationTracker? = null
    2. 다음 코드를 MainActivity.kt 파일에 추가합니다.

      // ...other imports import software.amazon.location.auth.AuthHelper import software.amazon.location.auth.LocationCredentialsProvider import software.amazon.location.tracking.LocationTracker import software.amazon.location.tracking.aws.LocationTrackingCallback import software.amazon.location.tracking.config.LocationTrackerConfig import software.amazon.location.tracking.database.LocationEntry import software.amazon.location.tracking.filters.DistanceLocationFilter import software.amazon.location.tracking.filters.TimeLocationFilter import software.amazon.location.tracking.util.TrackingSdkLogLevel class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCameraMoveStartedListener, MapLibreMap.OnCameraIdleListener { private val mainViewModel: MainViewModel by viewModels() private val poolId = "YOUR_AWS_IDENTITY_POOL_ID" private val trackerName = "YOUR_AWS_TRACKER_NAME" private val region = "YOUR_AWS_REGION" private val mapName = "YOUR_AWS_MAP_NAME" private val apiKey = "YOUR_AWS_API_KEY" private val coroutineScope = MainScope() private lateinit var locationCredentialsProvider: LocationCredentialsProvider private lateinit var authHelper: AuthHelper override fun onCreate(savedInstanceState: Bundle?) { MapLibre.getInstance(this) super.onCreate(savedInstanceState) setContent { TestMapAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MapLoadScreen(this, mainViewModel, onStartStopTrackingClick = { if (mainViewModel.isLocationTrackingForegroundActive) { mainViewModel.isLocationTrackingForegroundActive = false mainViewModel.locationTracker?.stop() } else { if (checkLocationPermission(this)) return@MapLoadScreen mainViewModel.isLocationTrackingForegroundActive = true mainViewModel.locationTracker?.start(locationTrackingCallback = object : LocationTrackingCallback { override fun onLocationAvailabilityChanged(locationAvailable: Boolean) { } override fun onLocationReceived(location: LocationEntry) { } override fun onUploadSkipped(entries: LocationEntry) { } override fun onUploadStarted(entries: ListLocationEntry) { } override fun onUploaded(entries: ListLocationEntry) { } }) } }) } } } authenticateUser() } private fun authenticateUser() { coroutineScope.launch { authHelper = AuthHelper(applicationContext) locationCredentialsProvider = authHelper.authenticateWithCognitoIdentityPool( poolId, ) locationCredentialsProvider.let { val config = LocationTrackerConfig( trackerName = trackerName, logLevel = TrackingSdkLogLevel.DEBUG, latency = 1000, frequency = 5000, waitForAccurateLocation = false, minUpdateIntervalMillis = 5000, ) mainViewModel.locationTracker = LocationTracker( applicationContext, it, config, ) mainViewModel.locationTracker?.enableFilter(TimeLocationFilter()) mainViewModel.locationTracker?.enableFilter(DistanceLocationFilter()) } } } private fun checkLocationPermission(context: Context) = ActivityCompat.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION, ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( context, Manifest.permission.ACCESS_COARSE_LOCATION, ) != PackageManager.PERMISSION_GRANTED override fun onMapReady(map: MapLibreMap) { map.setStyle( Style.Builder() .fromUri( "https://maps.geo.$region.amazonaws.com/maps/v0/maps/$mapName/style-descriptor?key=$apiKey" ), ) { mainViewModel.mapLibreMap = map map.uiSettings.isAttributionEnabled = true map.uiSettings.isLogoEnabled = false map.uiSettings.attributionGravity = Gravity.BOTTOM or Gravity.END val initialPosition = LatLng(47.6160281982247, -122.32642111977668) map.cameraPosition = CameraPosition.Builder() .target(initialPosition) .zoom(14.0) .build() map.addOnCameraMoveStartedListener(this) map.addOnCameraIdleListener(this) map.cameraPosition.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } override fun onCameraMoveStarted(p0: Int) { mainViewModel.label = "" mainViewModel.isLabelAdded = false } override fun onCameraIdle() { if (!mainViewModel.isLabelAdded) { mainViewModel.mapLibreMap?.cameraPosition?.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } }

      위 코드는 를 사용하여 LocationTracker 객체를 만드는 방법과 를 사용하여 추적을 시작하고 중지하는 방법을 보여줍니다 LocationTracker. AuthHelper

      • authenticateUser(): 이 메서드는 AuthHelper 및 LocationTracker 객체를 생성합니다.

      • onStartStopTrackingClick: 이 콜백은 사용자가 추적 시작/중지 버튼을 클릭하면 트리거되며, 이 버튼을 클릭하면 Tracking SDK로 추적이 시작/중지됩니다.

  5. Android 에뮬레이터로 테스트 경로를 생성하세요.

    1. Android 스튜디오를 사용하여 AVD를 실행하여 에뮬레이터를 엽니다.

    2. 에뮬레이터 툴바에서 더보기 (점 3개) 아이콘을 클릭하여 확장 컨트롤을 엽니다.

    3. 사이드바에서 위치를 선택하여 위치를 엽니다.

    4. GPX 데이터를 사용하거나 지도를 클릭하고 소스 및 목적지 데이터를 선택하여 경로를 생성합니다.

    5. 경로 재생을 클릭하여 시뮬레이션을 시작하고 GPS 경로 시뮬레이션을 시작합니다.

    6. 애플리케이션을 실행하고 시뮬레이션된 경로를 처리하는 방식을 관찰하여 애플리케이션을 테스트하십시오.

Android 퀵스타트 애플리케이션의 전체 데모입니다.

다음에 있는 것

이 애플리케이션의 소스 코드는 에서 사용할 수 있습니다 GitHub.

Amazon Location을 최대한 활용하려면 다음 리소스를 확인하세요.