Android용 소비자 SDK 시작하기

소비자 SDK를 사용하면 주문형 차량 공유 및 배송 솔루션 백엔드 서비스와 통합된 기본 소비자 앱을 빌드하고 실행할 수 있습니다. 활성 경로를 표시하고 경로 업데이트에 응답하며 이동 오류를 처리할 수 있는 이동 및 주문 진행 앱을 만들 수 있습니다.

Consumer SDK에는 모듈식 아키텍처가 있으므로 특정 앱에 사용할 API 부분을 사용하고 자체 API, Fleet Engine에서 제공하는 백엔드 서비스, Google Maps Platform의 추가 API와 통합할 수 있습니다.

최소 시스템 요구사항

휴대기기에서는 Android 6.0(API 수준 23) 이상을 실행해야 합니다.

빌드 및 종속 항목 구성

Consumer SDK 버전 1.99.0 이상은 Google Maven 저장소를 사용하여 사용할 수 있습니다. 이전에 사용된 비공개 저장소 채널이 지원 중단되었습니다.

Gradle

build.gradle 파일에 다음을 추가합니다.

repositories {
    ...
    google()
}

Maven

pom.xml 파일에 다음을 추가합니다.

<project>
  ...
  <repositories>
    <repository>
      <id>google-maven-repository</id>
      <url>https://maven.google.com</url>
    </repository>
  </repositories>
  ...
</project>

프로젝트 구성

Android용 소비자 SDK를 사용하려면 앱에서 minSdkVersion 23 이상을 타겟팅해야 합니다.

소비자 SDK로 빌드된 앱을 실행하려면 Android 기기에 Google Play 서비스가 설치되어 있어야 합니다.

개발 프로젝트 설정하기

Google Cloud 콘솔에서 개발 프로젝트를 설정하고 프로젝트의 API 키를 가져오는 방법은 다음과 같습니다.

  1. Google Cloud 콘솔 프로젝트를 만들거나 Consumer SDK와 함께 사용할 기존 프로젝트를 선택합니다. 새 프로젝트가 Google Cloud 콘솔에 표시될 때까지 몇 분 정도 기다립니다.

  2. 데모 앱을 실행하려면 프로젝트에서 Android용 Maps SDK에 액세스할 수 있어야 합니다. Google Cloud 콘솔에서 API 및 서비스 > 라이브러리를 선택한 다음 Android용 Maps SDK를 검색하여 사용 설정합니다.

  3. API 및 서비스 > 사용자 인증 정보 > 사용자 인증 정보 만들기 > API 키를 선택하여 프로젝트의 API 키를 가져옵니다. API 키를 가져오는 방법에 대한 자세한 내용은 API 키 가져오기를 참고하세요.

앱에 소비자 SDK 추가

Consumer SDK는 비공개 Maven 저장소를 통해 제공됩니다. 저장소에는 SDK의 프로젝트 객체 모델 (.pom) 파일과 Javadocs가 포함됩니다. 앱에 소비자 SDK를 추가하려면 다음 단계를 따르세요.

  1. 이전 섹션에서 설명한 대로 호스트 Maven 저장소에 액세스하도록 환경을 설정합니다.

    settings.gradle에 중앙 종속 항목 관리 구성을 선언한 경우 다음과 같이 사용 중지합니다.

    • settings.gradle에서 다음 코드 블록을 삭제합니다.

      import org.gradle.api.initialization.resolve.RepositoriesMode
      dependencyResolutionManagement {
          repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
          repositories {
              google()
              mavenCentral()
          }
      }
      
  2. Gradle 또는 Maven 구성에 다음 종속 항목을 추가하여 원하는 버전의 소비자 SDK를 VERSION_NUMBER 자리표시자로 바꿉니다.

    Gradle

    build.gradle에 다음을 추가합니다.

    dependencies {
      ...
      implementation 'com.google.android.libraries.mapsplatform.transportation:transportation-consumer:VERSION_NUMBER'
    }
    

    Maven

    pom.xml에 다음을 추가합니다.

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.mapsplatform.transportation</groupId>
        <artifactId>transportation-consumer</artifactId>
        <version>VERSION_NUMBER</version>
      </dependency>
    </dependencies>
    
  3. Consumer SDK는 Maps SDK에 종속됩니다. 이 종속 항목은 Maps SDK 버전이 다음과 같이 빌드 구성 파일에 명시적으로 정의되지 않은 경우 새 버전의 Maps SDK가 출시되면 Consumer SDK는 필요한 최소 지원 Maps SDK 버전을 계속 활용하는 방식으로 구성됩니다.

    Gradle

    build.gradle에 다음을 추가합니다.

    dependencies {
      ...
      implementation 'com.google.android.gms:play-services-maps:18.1.0'
    }
    

    Maven

    pom.xml에 다음을 추가합니다.

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.gms</groupId>
        <artifactId>play-services-maps</artifactId>
        <version>18.1.0</version>
      </dependency>
    </dependencies>
    

앱에 API 키 추가하기

앱에 소비자 SDK를 추가한 후 API 키를 추가합니다. 개발 프로젝트를 설정할 때 가져온 프로젝트 API 키를 사용해야 합니다.

이 섹션에서는 앱에서 더 안전하게 참조할 수 있도록 API 키를 저장하는 방법을 설명합니다. 버전 제어 시스템에 API 키를 체크인하면 안 됩니다. 이 파일은 프로젝트의 루트 디렉터리에 있는 local.properties 파일에 저장해야 합니다. local.properties 파일에 관한 자세한 내용은 Gradle 속성 파일을 참고하세요.

이 작업을 간단히 진행하고 싶다면 Android용 Secrets Gradle Plugin을 사용하세요.

플러그인을 설치하여 API 키를 저장하는 방법은 다음과 같습니다.

  1. 루트 수준 build.gradle 파일을 열고 다음 코드를 buildscript 아래 dependencies 요소에 추가합니다.

    Groovy

    buildscript {
        dependencies {
            // ...
            classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0"
        }
    }
    

    Kotlin

    buildscript {
        dependencies {
            // ...
            classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0")
        }
    }
    
  2. 앱 수준 build.gradle 파일을 열고 plugins 요소에 다음 코드를 추가합니다.

    Groovy

    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
    

    Kotlin

    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    
  3. Android 스튜디오를 사용하는 경우 프로젝트를 Gradle과 동기화합니다.

  4. 프로젝트 수준 디렉터리에서 local.properties을 열고 다음 코드를 추가합니다. YOUR_API_KEY를 직접 생성한 API 키로 변경합니다.

    MAPS_API_KEY=YOUR_API_KEY
    
  5. AndroidManifest.xml 파일에서 com.google.android.geo.API_KEY로 이동한 후 android:value 속성을 다음과 같이 업데이트합니다.

    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="${MAPS_API_KEY}" />
    

다음 예는 샘플 앱의 전체 매니페스트를 보여줍니다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.consumerapidemo">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/_AppTheme">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

앱에 필수 저작자 표시를 포함합니다.

앱에서 소비자 SDK를 사용하는 경우 앱의 법적 고지 섹션에 저작자 표시 텍스트와 오픈소스 라이선스를 포함해야 합니다. 저작자 표시는 독립적인 메뉴 항목 또는 정보 메뉴 항목의 일부로 포함하는 것이 가장 좋습니다.

라이선스 정보는 보관 취소된 AAR 파일의 'third_party_licenses.txt' 파일에서 확인할 수 있습니다.

오픈소스 알림을 포함하는 방법은 https://developers.google.com/android/guides/opensource를 참고하세요.

소비자 SDK 인증

Consumer SDK는 JSON 웹 토큰을 사용한 인증을 제공합니다. JSON 웹 토큰 (JWT)은 서비스에 하나 이상의 클레임을 제공하는 JSON 기반 액세스 토큰입니다. 예를 들어 서버는 '관리자로 로그인함'이라는 클레임이 있는 토큰을 생성하여 클라이언트에 제공할 수 있습니다. 그러면 클라이언트는 이 토큰을 사용하여 토큰이 관리자로 로그인되었음을 증명할 수 있습니다.

소비자 SDK는 애플리케이션에서 제공하는 JSON 웹 토큰을 사용하여 Fleet Engine과 통신합니다. 자세한 내용은 Fleet Engine 인증 및 승인을 참고하세요.

승인 토큰은 토큰의 authorization 헤더에 tripid:TRIP_ID 클레임을 포함해야 하며, 여기서 TRIP_ID는 이동 ID입니다. 이렇게 하면 소비자 SDK가 차량 위치, 경로, 도착예정시간을 포함한 이동 세부정보에 액세스할 수 있습니다.

JSON 웹 토큰 콜백

소비자 SDK는 초기화 중에 애플리케이션에 승인 토큰 콜백을 등록합니다. SDK는 애플리케이션을 호출하여 승인이 필요한 모든 네트워크 요청의 토큰을 가져옵니다.

콜백 구현에서는 승인 토큰을 캐시하고 expiry 시간이 지난 경우에만 새로고침하는 것이 좋습니다. 토큰은 1시간 만료 후에 발급되어야 합니다.

승인 토큰 콜백은 TripService 서비스에 필요한 서비스 토큰을 지정합니다. 또한 컨텍스트에 필요한 tripId도 제공합니다.

다음 코드 예에서는 승인 토큰 콜백을 구현하는 방법을 보여줍니다.

Java

class JsonAuthTokenFactory implements AuthTokenFactory {

  private static final String TOKEN_URL =
      "https://yourauthserver.example/token";

  private static class CachedToken {
    String tokenValue;
    long expiryTimeMs;
    String tripId;
  }

  private CachedToken token;

  /*
  * This method is called on a background thread. Blocking is OK. However, be
  * aware that no information can be obtained from Fleet Engine until this
  * method returns.
  */
  @Override
  public String getToken(AuthTokenContext context) {
    // If there is no existing token or token has expired, go get a new one.
    String tripId = context.getTripId();
    if (tripId == null) {
      throw new RuntimeException("Trip ID is missing from AuthTokenContext");
    }
    if (token == null || System.currentTimeMillis() > token.expiryTimeMs ||
        !tripId.equals(token.tripId)) {
      token = fetchNewToken(tripId);
    }
    return token.tokenValue;
  }

  private static CachedToken fetchNewToken(String tripId) {
    String url = TOKEN_URL + "/" + tripId;
    CachedToken token = new CachedToken();

    try (Reader r = new InputStreamReader(new URL(url).openStream())) {
      com.google.gson.JsonObject obj
          = com.google.gson.JsonParser.parseReader(r).getAsJsonObject();

      token.tokenValue = obj.get("ServiceToken").getAsString();
      token.expiryTimeMs = obj.get("TokenExpiryMs").getAsLong();

      /*
      * The expiry time could be an hour from now, but just to try and avoid
      * passing expired tokens, we subtract 5 minutes from that time.
      */
      token.expiryTimeMs -= 5 * 60 * 1000;
    } catch (IOException e) {
      /*
      * It's OK to throw exceptions here. The error listeners will receive the
      * error thrown here.
      */
      throw new RuntimeException("Could not get auth token", e);
    }
    token.tripId = tripId;

    return token;
  }
}

Kotlin

class JsonAuthTokenFactory : AuthTokenFactory() {

  private var token: CachedToken? = null

  /*
  * This method is called on a background thread. Blocking is OK. However, be
  * aware that no information can be obtained from Fleet Engine until this
  * method returns.
  */
  override fun getToken(context: AuthTokenContext): String {
    // If there is no existing token or token has expired, go get a new one.
    val tripId = 
      context.getTripId() ?: 
        throw RuntimeException("Trip ID is missing from AuthTokenContext")

    if (token == null || System.currentTimeMillis() > token.expiryTimeMs ||
        tripId != token.tripId) {
      token = fetchNewToken(tripId)
    }

    return token.tokenValue
  }

  class CachedToken(
    var tokenValue: String? = "", 
    var expiryTimeMs: Long = 0,
    var tripId: String? = "",
  )

  private companion object {
    const val TOKEN_URL = "https://yourauthserver.example/token"

    fun fetchNewToken(tripId: String) {
      val url = "$TOKEN_URL/$tripId"
      val token = CachedToken()

      try {
        val reader = InputStreamReader(URL(url).openStream())

        reader.use {
          val obj = com.google.gson.JsonParser.parseReader(r).getAsJsonObject()

          token.tokenValue = obj.get("ServiceToken").getAsString()
          token.expiryTimeMs = obj.get("TokenExpiryMs").getAsLong()

          /*
          * The expiry time could be an hour from now, but just to try and avoid
          * passing expired tokens, we subtract 5 minutes from that time.
          */
          token.expiryTimeMs -= 5 * 60 * 1000
        }
      } catch (e: IOException) {
        /*
        * It's OK to throw exceptions here. The error listeners will receive the
        * error thrown here.
        */
        throw RuntimeException("Could not get auth token", e)
      }

      token.tripId = tripId

      return token
    }
  }
}

API 초기화하기

이 절차를 수행하기 전에 적절한 서비스와 Consumer SDK를 사용 설정했다고 가정합니다.

ConsumerApi 인스턴스 가져오기

소비자 SDK를 사용하려면 앱에서 ConsumerApi를 비동기식으로 초기화해야 합니다. API는 싱글톤입니다. 초기화 메서드는 AuthTokenFactory를 사용합니다. 팩토리는 필요한 경우 사용자의 새 JWT 토큰을 생성합니다.

providerId는 Google Cloud 프로젝트의 프로젝트 ID입니다. 프로젝트를 만드는 방법에 관한 자세한 내용은 Fleet Engine 사용자 가이드를 참고하세요.

앱은 소비자 SDK 인증에 설명된 대로 AuthTokenFactory를 구현해야 합니다.

Java

Task<ConsumerApi> consumerApiTask = ConsumerApi.initialize(
    this, "myProviderId", authTokenFactory);

consumerApiTask.addOnSuccessListener(
  consumerApi -> this.consumerApi = consumerApi);

Kotlin

val consumerApiTask =
  ConsumerApi.initialize(this, "myProviderId", authTokenFactory)

consumerApiTask?.addOnSuccessListener { consumerApi: ConsumerApi ->
  this@YourActivity.consumerApi = consumerApi
}

기본 렌더기를 요청하도록 Maps SDK 초기화

소비자 SDK v2.0.0은 Android용 Maps SDK v18.1.0 이상을 지원합니다. 선호하는 Google 지도 렌더기를 지정하는 요청을 지원합니다. 자세한 내용은 새 지도 렌더기(선택)를 참고하세요.

Maps SDK를 종속 항목으로 추가

Gradle

build.gradle에 다음을 추가합니다.

dependencies {
  //...
  implementation "com.google.android.gms:play-services-maps:18.1.0"
}

Maven

pom.xml에 다음을 추가합니다.

 <dependencies>
   ...
   <dependency>
     <groupId>com.google.android.gms</groupId>
     <artifactId>play-services-maps</artifactId>
     <version>18.1.0</version>
   </dependency>
 </dependencies>

소비자 SDK를 초기화하기 전에 Maps SDK 초기화

Application 또는 시작 Activity 클래스에서 MapsInitializer.initialize()를 호출하고 소비자 SDK를 초기화하기 전에 렌더기 요청 결과를 기다립니다.

java

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  initViews();

  MapsInitializer.initialize(getApplicationContext(), Renderer.LATEST,
      new OnMapsSdkInitializedCallback() {
        @Override
        public void onMapsSdkInitialized(Renderer renderer) {
          switch (renderer) {
            case LATEST:
              Log.i("maps_renderer", "LATEST renderer");
              break;
            case LEGACY:
              Log.i("maps_renderer", "LEGACY renderer");
              break;
          }

          initializeConsumerSdk();
        }
      });
}

Kotlin

fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.main)
  initViews()

  MapsInitializer.initialize(
    getApplicationContext(), Renderer.LATEST,
    object : OnMapsSdkInitializedCallback() {
      fun onMapsSdkInitialized(renderer: Renderer?) {
        when (renderer) {
          LATEST -> Log.i("maps_renderer", "LATEST renderer")
          LEGACY -> Log.i("maps_renderer", "LEGACY renderer")
        }
        initializeConsumerSdk()
      }
    })
  }

사용자 인터페이스 만들기

ConsumerMapFragment 또는 ConsumerMapView를 사용하여 애플리케이션의 사용자 인터페이스를 만들 수 있습니다. ConsumerMapFragment에서는 Fragment를 사용하여 지도를 정의할 수 있고, ConsumerMapView에서는 View를 사용할 수 있습니다. 차량 공유 기능은 ConsumerMapViewConsumerMapFragment에서 모두 동일하므로 View 또는 Fragment 중 애플리케이션에 더 적합한 기능을 기준으로 하나를 선택할 수 있습니다.

API 19 (KitKat) 및 벡터 드로어블 지원 추가

앱 디자인에 API 19 (KitKat) 기기 및 벡터 드로어블 지원이 필요하다면 Activity에 다음 코드를 추가합니다. 이 코드는 AppCompatActivity를 확장하여 Consumer SDK의 벡터 드로어블을 사용합니다.

Java

// ...
import android.support.v7.app.AppCompatActivity;

// ...

public class ConsumerTestActivity extends AppCompatActivity {
  // ...
}

Kotlin

// ...
import android.support.v7.app.AppCompatActivity

// ...

class ConsumerTestActivity : AppCompatActivity() {
  // ...
}

지도 프래그먼트 또는 뷰 추가하기

앱 레이아웃 XML 파일 (/res/layout에 위치)에서 정의하는 Android 프래그먼트 또는 뷰에서 여정 공유를 표시하는 지도를 만듭니다. 그러면 프래그먼트 (또는 뷰)에서 여정 공유 지도에 대한 액세스 권한을 제공하며, 이 액세스 권한은 앱에서 액세스하고 수정할 수 있습니다. 또한 지도는 ConsumerController에 핸들을 제공하므로 앱에서 여정 공유 환경을 제어하고 맞춤설정할 수 있습니다.

여정 공유 지도 및 컨트롤러

다음 코드 예와 같이 여정 공유 지도를 프래그먼트 (ConsumerMapFragment 사용) 또는 뷰 (ConsumerMapView 사용)로 정의합니다. 그러면 onCreate() 메서드가 getConsumerGoogleMapAsync(callback)를 호출해야 합니다. 그러면 콜백에서 ConsumerGoogleMap를 비동기식으로 반환합니다. 그런 다음 ConsumerGoogleMap를 사용하여 여정 공유를 표시하면 앱에서 필요에 따라 이를 업데이트할 수 있습니다.

ConsumerMapFragment

다음 코드 예와 같이 애플리케이션 레이아웃 XML 파일에서 프래그먼트를 정의합니다.

<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.google.android.libraries.mapsplatform.transportation.consumer.view.ConsumerMapFragment"
    android:id="@+id/consumer_map_fragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

getConsumerGoogleMapAsync() 호출은 onCreate() 메서드에서 발생해야 합니다.

Java

public class SampleAppActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {

    // Find the ConsumerMapFragment.
    ConsumerMapFragment consumerMapFragment =
        (ConsumerMapFragment) fragmentManager.findFragmentById(R.id.consumer_map_fragment);

    // Initiate the callback that returns the map.
    if (consumerMapFragment != null) {
      consumerMapFragment.getConsumerGoogleMapAsync(
          new ConsumerMapReadyCallback() {
            // The map returned in the callback is used to access the ConsumerController.
            @Override
            public void onConsumerMapReady(@NonNull ConsumerGoogleMap consumerGoogleMap) {
              ConsumerController consumerController = consumerGoogleMap.getConsumerController();
            }
          });
    }
  }

}

Kotlin

class SampleAppActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    // Find the ConsumerMapFragment.
    val consumerMapFragment =
      fragmentManager.findFragmentById(R.id.consumer_map_fragment) as ConsumerMapFragment

    consumerMapFragment.getConsumerGoogleMapAsync(
      object : ConsumerMapReadyCallback() {
        override fun onConsumerMapReady(consumerGoogleMap: ConsumerGoogleMap) {
          val consumerController = consumerGoogleMap.getConsumerController()!!
        }
      }
    )
  }
}
ConsumerMapView

뷰는 XML 파일에 정의된 대로 프래그먼트나 활동에서 사용할 수 있습니다.

<com.google.android.libraries.mapsplatform.transportation.consumer.view.ConsumerMapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/consumer_map_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

getConsumerGoogleMapAsync() 호출은 onCreate()에서 발생해야 합니다. 콜백 매개변수 외에도 포함하는 활동이나 프래그먼트, 그리고 MapView의 구성 속성을 포함하는 GoogleMapOptions (null일 수 있음)가 필요합니다. 활동 또는 프래그먼트 기본 클래스는 수명 주기에 관한 액세스를 제공하므로 각각 FragmentActivity 또는 지원 Fragment여야 합니다.

Java

public class SampleAppActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    ConsumerMapView mapView = findViewById(R.id.consumer_map_view);

    if (mapView != null) {
      mapView.getConsumerGoogleMapAsync(
          new ConsumerMapReadyCallback() {
            // The map returned in the callback is used to access the ConsumerController.
            @Override
            public void onConsumerMapReady(@NonNull ConsumerGoogleMap consumerGoogleMap) {
              ConsumerController consumerController = consumerGoogleMap.getConsumerController();
            }
          }, this, null);
    }
  }

}

Kotlin

class SampleAppActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    val mapView = findViewById(R.id.consumer_map_view) as ConsumerMapView

    mapView.getConsumerGoogleMapAsync(
      object : ConsumerMapReadyCallback() {
        // The map returned in the callback is used to access the ConsumerController.
        override fun onConsumerMapReady(consumerGoogleMap: ConsumerGoogleMap) {
          val consumerController = consumerGoogleMap.getConsumerController()!!
        }
      },
      /* fragmentActivity= */ this,
      /* googleMapOptions= */ null,
    )
  }
}

프래그먼트의 MapView는 활동의 MapView에 관한 위 예와 같습니다. 단, 프래그먼트가 프래그먼트 onCreateView() 메서드에 MapView가 포함된 레이아웃을 확장한다는 점이 다릅니다.

Java

public class MapViewInFragment extends Fragment {

  @Override
  public View onCreateView(
      @NonNull LayoutInflater layoutInflater,
      @Nullable ViewGroup viewGroup,
      @Nullable Bundle bundle) {
    return layoutInflater.inflate(R.layout.consumer_map_view, viewGroup, false);
  }

}

Kotlin

class MapViewInFragment : Fragment() {
  override fun onCreateView(
    layoutInflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
  ): View {
    return layoutInflater.inflate(R.layout.consumer_map_view, viewGroup, false)
  }
}

여행에 초점을 맞추도록 카메라 확대/축소 조정

Maps SDK에 내장된 기본 내 위치 버튼은 기기의 중앙에 카메라를 배치합니다.

활성 여정 공유 세션이 있는 경우 기기 위치가 아닌 여정에 초점을 맞추도록 카메라를 중앙에 배치할 수 있습니다.

Android용 소비자 SDK 기본 제공 솔루션: AutoCamera

기기 위치 대신 이동 경로에 집중할 수 있도록 소비자 SDK는 기본적으로 사용 설정되는 AutoCamera 기능을 제공합니다. 여정 공유 경로와 다음 이동 경유지에 초점을 맞춰 카메라가 확대/축소합니다.

AutoCamera

카메라 동작 맞춤설정

카메라 동작을 더 세밀하게 제어해야 하는 경우 ConsumerController.setAutoCameraEnabled()를 사용하여 자동 카메라를 사용 중지하거나 사용 설정할 수 있습니다.

ConsumerController.getCameraUpdate()는 해당 시점의 권장 카메라 경계를 반환합니다. 그런 다음 이 CameraUpdateGoogleMap.moveCamera() 또는 GoogleMap.animateCamera()의 인수로 제공할 수 있습니다.

차량 공유 및 지도 액세스

애플리케이션에서 차량 공유 및 지도 상호작용을 지원하려면 ConsumerGoogleMapConsumerController에 대한 액세스 권한이 필요합니다. ConsumerMapFragmentConsumerMapView는 모두 ConsumerMapReadyCallback에서 ConsumerGoogleMap를 비동기식으로 반환합니다. ConsumerGoogleMapgetConsumerController()에서 ConsumerController를 반환합니다. 다음과 같이 ConsumerGoogleMapConsumerController에 액세스할 수 있습니다.

Java

private ConsumerGoogleMap consumerGoogleMap;
private ConsumerController consumerController;
private ConsumerMapView consumerMapView;

consumerMapView.getConsumerGoogleMapAsync(
    new ConsumerMapReadyCallback() {
      @Override
      public void onConsumerMapReady(@NonNull ConsumerGoogleMap consumerMap) {
        consumerGoogleMap = consumerMap;
        consumerController = consumerMap.getConsumerController();
      }
    },
    this, null);

Kotlin

var consumerGoogleMap: ConsumerGoogleMap
var consumerController: ConsumerController
val consumerMapView = findViewById(R.id.consumer_map_view) as ConsumerMapView

consumerMapView.getConsumerGoogleMapAsync(
  object : ConsumerMapReadyCallback() {
    override fun onConsumerMapReady(consumerMap: ConsumerGoogleMap) {
      consumerGoogleMap = consumerMap
      consumerController = consumerMap.getConsumerController()
    },
    /* fragmentActivity= */ this,
    /* googleMapOptions= */ null,
  }
)

ConsumerGoogleMap

ConsumerGoogleMapGoogleMap 클래스의 래퍼 클래스입니다. 이 API를 사용하면 앱에서 GoogleMap와 동일한 API를 사용하여 지도와 상호작용할 수 있습니다. 소비자 지도를 사용하면 앱과 차량 공유 기능이 동일한 기본 GoogleMap과 원활하게 상호작용할 수 있습니다. 예를 들어 GoogleMap는 단일 콜백 등록만 허용하지만 ConsumerGoogleMap는 이중 등록 콜백을 지원합니다. 이러한 콜백을 사용하면 앱 및 차량 공유에서 순차적으로 호출되는 콜백을 등록할 수 있습니다.

ConsumerController

ConsumerController는 이동 모니터링, 이동 상태 제어, 위치 설정과 같은 차량 공유 기능에 대한 액세스를 제공합니다.

여정 공유 설정

백엔드에서 소비자를 차량과 매칭한 후 JourneySharingSession를 사용하여 여정 공유 사용자 인터페이스를 시작합니다. 여정 공유에는 일치하는 차량 위치와 경로가 표시됩니다. 앱에 SDK를 구현한 후 이동 모니터링, 업데이트 수신 대기, 오류 처리를 위한 기능을 추가할 수 있습니다. 다음 절차에서는 백엔드 서비스가 있고 소비자와 차량을 일치시키는 서비스가 작동한다고 가정합니다.

  1. TripModel 객체에 리스너를 등록하여 예상 도착 시간 (ETA) 및 차량이 도착 전에 이동해야 하는 거리와 같은 이동에 관한 세부정보를 가져옵니다.

    Java

    // Create a TripModel instance for listening to updates to the trip specified by this trip name.
    String tripName = ...;
    TripModelManager tripModelManager = consumerApi.getTripModelManager();
    TripModel tripModel = tripModelManager.getTripModel(tripName);
    
    // Create a JourneySharingSession instance based on the TripModel.
    JourneySharingSession session = JourneySharingSession.createInstance(tripModel);
    
    // Add the JourneySharingSession instance on the map for updating the UI.
    consumerController.showSession(session);
    
    // Register for trip update events.
    tripModel.registerTripCallback(new TripModelCallback() {
      @Override
      public void onTripETAToNextWaypointUpdated(
          TripInfo tripInfo, @Nullable Long timestampMillis) {
        // ...
      }
    
      @Override
      public void onTripActiveRouteRemainingDistanceUpdated(
          TripInfo tripInfo, @Nullable Integer distanceMeters) {
        // ...
      }
    
      // ...
    });
    

    Kotlin

    // Create a TripModel instance for listening to updates to the trip specified by this trip name.
    val tripName = "tripName"
    val tripModelManager = consumerApi.getTripModelManager()
    val tripModel = tripModelManager.getTripModel(tripName)
    
    // Create a JourneySharingSession instance based on the TripModel.
    val session = JourneySharingSession.createInstance(tripModel)
    
    // Add the JourneySharingSession instance on the map for updating the UI.
    consumerController.showSession(session)
    
    // Register for trip update events.
    tripModel.registerTripCallback(
      object : TripModelCallback() {
        override fun onTripETAToNextWaypointUpdated(
          tripInfo: TripInfo,
          timestampMillis: Long?,
        ) {
          // ...
        }
    
        override fun onTripActiveRouteRemainingDistanceUpdated(
          tripInfo: TripInfo,
          distanceMeters: Int?,
        ) {
          // ...
        }
    
      // ...
    })
    
  2. TripModelOptions을(를) 사용하여 이동을 구성합니다.

    Java

    // Set refresh interval to 2 seconds.
    TripModelOptions tripOptions =
        TripModelOptions.builder().setRefreshIntervalMillis(2000).build();
    tripModel.setTripModelOptions(tripOptions);
    

    Kotlin

    // Set refresh interval to 2 seconds.
    val tripOptions = TripModelOptions.builder().setRefreshIntervalMillis(2000).build()
    tripModel.setTripModelOptions(tripOptions)
    

여정 공유 중지

호스트 활동이 소멸되는 경우와 같이 더 이상 필요하지 않은 경우 여정 공유를 중지해야 합니다. 여정 공유를 중지하면 Fleet Engine에 대한 네트워크 요청도 중지되고 메모리 누수가 방지됩니다.

다음 샘플 코드는 여정 공유를 중지하는 방법을 보여줍니다.

Java

public class MainActivity extends AppCompatActivity
    implements ConsumerViewModel.JourneySharingListener  {

  // Class implementation

  @Override
  protected void onDestroy() {
    super.onDestroy();

    if (journeySharingSession != null) {
      journeySharingSession.stop();
    }
  }
}

Kotlin

class SampleAppActivity : AppCompatActivity(), ConsumerViewModel.JourneySharingListener {

  // Class implementation

  override fun onDestroy() {
    super.onDestroy()

    journeySharingSession?.stop()
  }
}

경로 오류 처리

onTripRefreshError 메서드는 경로 모니터링 중에 발생하는 오류를 표시합니다. Consumer SDK 오류에 대한 매핑은 Google Cloud Platform에 설정된 것과 동일한 HTTP/RPC 가이드라인을 따릅니다. 이동 모니터링 중에 표시되는 일반적인 오류는 다음과 같습니다.

HTTP RPC 설명
400명 INVALID_ARGUMENT 클라이언트가 잘못된 이동 이름을 지정했습니다. 이동 이름은 providers/{provider_id}/trips/{trip_id} 형식을 따라야 합니다. provider_id는 서비스 제공업체가 소유한 Cloud 프로젝트의 ID여야 합니다.
401 UNAUTHENTICATED 잘못된 JWT 토큰으로 인해 요청이 인증되지 않았습니다. 이 오류는 JWT 토큰이 이동 ID 없이 서명되었거나 JWT 토큰이 만료된 경우에 발생합니다.
403 PERMISSION_DENIED 클라이언트에 충분한 권한이 없습니다. 이 오류는 JWT 토큰이 유효하지 않거나, 클라이언트에 권한이 없거나, 클라이언트 프로젝트에 API가 사용 설정되지 않은 경우에 발생합니다. JWT 토큰이 누락되었거나 토큰이 요청된 이동 ID와 일치하지 않는 여행 ID로 서명되었을 수 있습니다.
429 RESOURCE_EXHAUSTED 리소스 할당량이 0이거나 트래픽 속도가 한도를 초과합니다.
503 UNAVAILABLE 서비스를 사용할 수 없습니다. 일반적으로 서버가 다운됩니다.
504 DEADLINE_EXCEEDED 요청 기한이 지났습니다. 이는 호출자가 메서드의 기본 기한보다 짧은 기한을 설정하고 (즉, 요청된 기한이 서버가 요청을 처리하기에 충분하지 않음) 요청이 기한 내에 완료되지 않은 경우에만 발생합니다.

자세한 내용은 소비자 SDK 오류 처리를 참고하세요.