Android Auto için navigasyonu etkinleştirme

Bu bölümde, uygulamanızın gezinme deneyimini araç içi ana ünitelerde göstermek için Navigation SDK'yı Arabalar için Android uygulama kitaplığıyla nasıl kullanabileceğiniz açıklanmaktadır. Kullanıcının gösterge paneli sistemi Android Auto'yu destekliyorsa kullanıcılar telefonlarını cihaza bağlayarak uygulamanızı doğrudan araçlarının ekranında kullanabilir. Sesli yönlendirme aracın hoparlörlerinde de çalışır.

Android Auto için etkinleştirilmiş bir uygulamayı kullanarak rehberli navigasyon gösteren bir araç içi ana birim.

Arabalar için Android Uygulama Kitaplığı, sürücü güvenliği için onaylanmış bir dizi görsel şablon sağlayarak Android uygulamalarının Android Auto'da çalışmasını sağlar. Bu şablonlar, sürücünün dikkatini dağıtmayı azaltmak için kontrol panelindeki kullanıcı arayüzü kontrollerini telefondaki kontrollerle sınırlayacak şekilde tasarlanmıştır.

Navigasyon SDK'sı destekli uygulamanızın Android Auto ile çalışmasını sağladığınızda navigasyon deneyimi için ek bir görünüm sunarsınız. Bu sayede biri telefon, diğeri de araç içi birim için olmak üzere iki harita görünümü kullanılabilir. Her iki ekran da tekil bir öğe olan Navigator.java'dan rehberlik alır.

Kontrol panelindeki sistem, güvenlik açısından onaylanmış etkileşimli öğeleri gösterir. Böylece kullanıcı, gereksiz dikkat dağıtıcı unsurlar olmadan hedefine güvenli bir şekilde gidebilir. Kullanıcı, siparişleri kabul etme veya reddetme ya da müşterinin konumunu haritada görüntüleme gibi uygulamaya özel işlevlerle de etkileşimde bulunabilir. Sipariş durumu güncellemeleri, kontrol panelindeki birimde de gösterilebilir.

Android Auto ile adım adım yol tarifi gösteren bir ana birim. Bir Android telefonda aynı rota genel görünüm olarak gösteriliyor.

Bağlı telefon, standart Navigation SDK deneyimini veya uygulamanızdaki başka bir görünümü ya da iş akışını göstermeye devam edebilir. Bu özellik, araba ekranında iyi çalışmayabilecek özel işlevler sunmaya devam etmenize olanak tanır.

Kur

Uygulamanızın Android Auto ile çalışmasını sağlamanın ilk adımı, Android Auto ile bir araç hizmeti ayarlamak ve ardından Navigation SDK uygulamanızda TurnByTurn kitaplığını etkinleştirmektir.

Android Auto'yu kullanmaya başlama

Android Auto ile çalışmak üzere tasarlanmış Navigation SDK özelliklerini kullanmaya başlamadan önce, Android Auto'nun uygulamanızı keşfedebilmesi için uygulamanızda bir araç hizmeti ayarlamanız gerekir.

Aşağıdaki adımları uygulayın. Bu adımların tamamını Android for Cars geliştirici belgelerinde bulabilirsiniz:

  1. Android Auto'nun temel özelliklerini öğrenin.
  2. Arabalar için Android uygulama kitaplığını yükleyin.
  3. Uygulamanızın manifest dosyasını Android Auto'yu içerecek şekilde yapılandırın.
  4. Manifest dosyanızda minimum araç uygulaması seviyesini 1 olarak beyan edin.
  5. Etkinliğinizi ve CarAppService oluşturun.

Navigation SDK'yı ayarlama

Araba uygulaması hizmetinizi oluşturduktan sonra Navigation SDK ile çalışmaya başlayabilirsiniz.

  1. Henüz Navigation SDK'yı uygulamanıza entegre etmediyseniz projenizi oluşturun.
  2. Uygulamanız için adım adım yol tarifi feed'ini etkinleştirin.
  3. İsteğe bağlıdır. Navigation SDK'dan oluşturulan simgeleri kullanın.
  4. Haritayı çizin Screen sınıfında sağlanan Android Auto Surface'teki NavigationViewForAuto sınıfını kullanarak.
  5. Android Auto Navigation şablonunu TurnbyTurn kitaplığındaki verilerle doldurun.

Uygulamanıza gezinme bilgileri sağlayan kayıtlı bir hizmetiniz ve uygulamanızın Android Auto'ya bağlanabildiği için artık uygulamanızın Android Auto ile düzgün çalışması için gereken diğer gezinme öğelerini oluşturmaya hazırsınız:

Harita ve navigasyon kullanıcı arayüzünü çizme

NavigationViewForAuto sınıfı, Android Auto ekranlarında bir harita ve navigasyon kullanıcı arayüzü oluşturur. Bu özellik, NavigationView telefonlar için sunulan özelliklerin çoğunu sağlar ancak etkileşim sınırlıdır. Android Auto'nun sağladığı Surface üzerine çizim yapmak için NavigationViewForAuto simgesini kullanın:

private boolean isSurfaceReady(SurfaceContainer surfaceContainer) {
  return surfaceContainer.getSurface() != null
        && surfaceContainer.getDpi() != 0
        && surfaceContainer.getHeight() != 0
        && surfaceContainer.getWidth() != 0;
}

@Override
public void onSurfaceAvailable(@NonNull SurfaceContainer surfaceContainer) {
  if (!isSurfaceReady(surfaceContainer)) {
    return;
   }
  virtualDisplay =
      getCarContext()
          .getSystemService(DisplayManager.class)
          .createVirtualDisplay(
            VIRTUAL_DISPLAY_NAME,
            surfaceContainer.getWidth(),
            surfaceContainer.getHeight(),
            surfaceContainer.getDpi(),
            surfaceContainer.getSurface(),
            DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
    presentation = new Presentation(getCarContext(), virtualDisplay.getDisplay());

    navigationView = new NavigationViewForAuto(getCarContext());
    navigationView.onCreate(null);
    navigationView.onStart();
    navigationView.onResume();

    presentation.setContentView(navigationView);
    presentation.show();

    navigationView.getMapAsync(googleMap -> this.googleMap = googleMap);
  }

@Override
public void onSurfaceDestroyed(@NonNull SurfaceContainer surfaceContainer) {
  navigationView.onPause();
  navigationView.onStop();
  navigationView.onDestroy();

  presentation.dismiss();
  virtualDisplay.release();
}

Harita etkileşimini etkinleştirme

Android Auto, sürücü güvenliği için ekran yüzeyi etkileşimini bir dizi SurfaceCallback yöntemiyle sınırlar. Bu geri çağırmaları kullanarak, sürücünün gösterge paneli ekranında haritayla sınırlı etkileşimini destekleyin. Örneğin, onClick ve onScale, kullanıcının dokunma ve sıkıştırma hareketlerine karşılık gelir. Etkileşim geri çağırmaları, map action strip işlevini aşağıdaki şekilde kullanmalıdır:

  • Harita etkileşimi geri aramalarını almak için uygulamanızda Action.PAN düğmesi kullanılmalıdır.

  • Ek kullanıcı işlemlerini desteklemek için harita işlem şeridine düğmeler ekleyin.

Yüzey geri aramalarını etkinleştirme

@NonNull
@Override
public Template onGetTemplate() {
  return new NavigationTemplate.Builder()
    .setActionStrip(new ActionStrip.Builder().build())
    .setMapActionStrip(new ActionStrip.Builder().addAction(Action.PAN).build())
    .build();
}

Sıkıştırarak yakınlaştırma

@Override
public void onScale(float focusX, float focusY, float scaleFactor) {
  CameraUpdate update =
      CameraUpdateFactory.zoomBy((scaleFactor - 1),
                                  new Point((int) focusX, (int) focusY));
  googleMap.animateCamera(update); // map is set in onSurfaceAvailable.
}

Kaydırma

@Override
public void onScroll(float distanceX, float distanceY) {
  googleMap.moveCamera(CameraUpdateFactory.scrollBy(distanceX, distanceY));
}

Navigasyon yol tariflerini görüntüleme

Bu bölümde, gezinme gönderileri için bir gözlemci oluşturma ve dönüş kartı şablonunda gezinme talimatlarını doldurma konuları ele alınmaktadır.

Android Auto'da yol tarifi için dönüş kartı.

Android Auto Navigation Template, mevcut yolculukla ilgili navigasyon bilgilerini gösteren bir dönüş kartı sağlar. Navigation SDK'daki TurnByTurn kitaplığı, bu gezinme bilgilerini sağlar. Kodunuz, Android Auto gezinme şablonunu doldurmak için bu bilgileri kullanır.

Gözlemci ayarlama

Aşağıdaki örnekte, SampleApplication, MutableLiveData<NavInfo> nesnesini koruyan özel bir uygulama sınıfıdır. Gözlemci, gezinme nesnesinden bir güncelleme aldığında bu NavInfo nesnesini SampleApplication sınıfı tarafından tutulan NavInfoMutableLiveData öğesine gönderir.

Aşağıdaki örnek, Android Auto Screen uygulamasında bu nesne için bir gözlemci kaydeder.

public SampleAndroidAutoNavigationScreen(@NonNull CarContext carContext,
                                     SampleApplication application) {
  super(carContext);
  getCarContext().getCarService(AppManager.class).setSurfaceCallback(this);
  application.getNavInfoMutableLiveData().observe(this, this::processNextStep);
}

Gezinme bilgilerini doldurma

Aşağıdaki kod snippet'inde, Android Auto şablonunun adımlar, mesafeler ve simgeler dahil olmak üzere mevcut rota bilgileriyle nasıl doldurulacağı gösterilmektedir. Bu görüntüleme öğeleri hakkında daha fazla bilgiyi Feed'i görüntüleme ile doldurma başlıklı makalede bulabilirsiniz.

Kod örneğini görmek için genişletin.

private RoutingInfo currentRoutingInfo;

@NonNull
@Override
public Template onGetTemplate() {
NavigationTemplate.Builder navigationTemplateBuilder =
  new NavigationTemplate.Builder()
    .setActionStrip(...)
    .setMapActionStrip(...)
  if (currentRoutingInfo != null) {
    navigationTemplateBuilder.setNavigationInfo(currentRoutingInfo);
  }
  return navigationTemplateBuilder.build();
}

private void processNextStep(NavInfo navInfo) {
  if (navInfo == null || navinfo.getCurrentStep() == null) {
    return;
  }

/**
*   Converts data received from the Navigation data feed
*   into Android-Auto compatible data structures. For more information
*   see the "Ensure correct maneuver types" below.
*/
  Step currentStep = buildStepFromStepInfo(navInfo.getCurrentStep());
  Distance distanceToStep =
              buildDistanceFromMeters(navInfo.getDistanceToCurrentStepMeters());

  currentRoutingInfo =
     new RoutingInfo.Builder().setCurrentStep(currentStep, distanceToStep).build();

  // Invalidate the current template which leads to another onGetTemplate call.
  invalidate();
}

private Step buildStepFromStepInfo(StepInfo stepInfo) {
  IconCompat maneuverIcon =
               IconCompat.createWithBitmap(stepInfo.getManeuverBitmap());
  Maneuver.Builder
            maneuverBuilder = newManeuver.Builder(
                  ManeuverConverter
                          .getAndroidAutoManeuverType(stepInfo.getManeuver()));
  CarIcon maneuverCarIcon = new CarIcon.Builder(maneuverIcon).build();
  maneuverBuilder.setIcon(maneuverCarIcon);
  Step.Builder stepBuilder =
    new Step.Builder()
       .setRoad(stepInfo.getFullRoadName())
       .setCue(stepInfo.getFullInstructionText())
       .setManeuver(maneuverBuilder.build());

  if (stepInfo.getLanes() != null
           && stepInfo.getLanesBitmap() != null) {
    for (Lane lane : buildAndroidAutoLanesFromStep(stepInfo)) {
      stepBuilder.addLane(lane);
    }
    IconCompat lanesIcon =
               IconCompat.createWithBitmap(stepInfo.getLanesBitmap());
    CarIcon lanesImage = new CarIcon.Builder(lanesIcon).build();
    stepBuilder.setLanesImage(lanesImage);
  }
    return stepBuilder.build();
}

/*
*   Constructs a {@code Distance} object in imperial measurement units.
*   In a real world scenario, units would be based on locale.
*/
private Distance buildDistanceFromMeters(int distanceMeters) {

// Distance can be negative so set the min distance to 0.
  int remainingFeet = (int) max(0, distanceMeters * DistanceConstants.FEET_PER_METER);
  double remainingMiles = ((double) remainingFeet) / DistanceConstants.FEET_PER_MILE;

// Only use the tenths place digit if distance is less than 10 miles and show
// feet if distance is less than 0.25 miles.

  if (remainingMiles >= DistanceConstants.MIN_MILES_TO_SHOW_INTEGER) {
    return Distance.create((int) round(remainingMiles), Distance.UNIT_MILES);
  } else if (remainingMiles >= 0.25) {
    return Distance.create((int) remainingMiles, Distance.UNIT_MILES);
  } else {
    return Distance.create(remainingFeet, Distance.UNIT_FEET);
  }
}

Doğru manevra türlerini doğrulama

Android Auto Car kitaplığında kullanılan manevra türleri, TurnByTurn kitaplığının sağladığı manevralarla bire bir eşleşir. Ancak Navigasyon SDK'sı manevralarını Android Auto Car kitaplığında geçerli bir bildirime dönüştürmeniz gerekir. Aşağıdaki tabloda, birkaç alanın karşılığı gösterilmektedir. Ardından, kolaylık sağlaması için örnek bir dönüştürücü yardımcı programı verilmektedir.

Adım Adım Kütüphane Manevrası Android Auto Maneuver
DEPART TYPE_DEPART
DESTINATION TYPE_DESTINATION
DESTINATION_LEFT TYPE_DESTINATION_LEFT
DESTINATION_RIGHT TYPE_DESTINATION_RIGHT
TURN_U_TURN_CLOCKWISE TYPE_U_TURN_RIGHT
ON_RAMP_LEFT TYPE_ON_RAMP_NORMAL_LEFT
ON_RAMP_RIGHT TYPE_ON_RAMP_NORMAL_RIGHT
ON_RAMP_SLIGHT_LEFT TYPE_ON_RAMP_SLIGHT_LEFT
FORK_RIGHT TYPE_FORK_RIGHT

Kod örneğini görmek için genişletin.

import com.google.android.libraries.mapsplatform.turnbyturn.model.Maneuver;
import com.google.common.collect.ImmutableMap;
import javax.annotation.Nullable;

/** Converter that converts between turn-by-turn and Android Auto Maneuvers. */
public final class ManeuverConverter {
  private ManeuverConverter() {}

  // Map from turn-by-turn Maneuver to Android Auto Maneuver.Type.
  private static final ImmutableMap<Integer, Integer> MANEUVER_TO_ANDROID_AUTO_MANEUVER_TYPE =
      ImmutableMap.<Integer, Integer>builder()
          .put(Maneuver.DEPART, androidx.car.app.navigation.model.Maneuver.TYPE_DEPART)
          .put(Maneuver.DESTINATION, androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION)
          .put(
              Maneuver.DESTINATION_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_LEFT)
          .put(
              Maneuver.DESTINATION_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_DESTINATION_RIGHT)
          .put(Maneuver.STRAIGHT, androidx.car.app.navigation.model.Maneuver.TYPE_STRAIGHT)
          .put(Maneuver.TURN_LEFT, androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_LEFT)
          .put(
              Maneuver.TURN_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_TURN_NORMAL_RIGHT)
          .put(Maneuver.TURN_KEEP_LEFT, androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_LEFT)
          .put(Maneuver.TURN_KEEP_RIGHT, androidx.car.app.navigation.model.Maneuver.TYPE_KEEP_RIGHT)
          .put(
              Maneuver.TURN_SLIGHT_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_LEFT)
          .put(
              Maneuver.TURN_SLIGHT_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SLIGHT_RIGHT)
          .put(
              Maneuver.TURN_SHARP_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_TURN_SHARP_LEFT)
          .put(
              Maneuver.TURN_SHARP_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_SHARP_RIGHT)
          .put(
              Maneuver.TURN_U_TURN_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_U_TURN_RIGHT)
          .put(
              Maneuver.TURN_U_TURN_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_U_TURN_LEFT)
          .put(
              Maneuver.MERGE_UNSPECIFIED,
              androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_SIDE_UNSPECIFIED)
          .put(Maneuver.MERGE_LEFT, androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_LEFT)
          .put(Maneuver.MERGE_RIGHT, androidx.car.app.navigation.model.Maneuver.TYPE_MERGE_RIGHT)
          .put(Maneuver.FORK_LEFT, androidx.car.app.navigation.model.Maneuver.TYPE_FORK_LEFT)
          .put(Maneuver.FORK_RIGHT, androidx.car.app.navigation.model.Maneuver.TYPE_FORK_RIGHT)
          .put(
              Maneuver.ON_RAMP_UNSPECIFIED,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_NORMAL_RIGHT)
          .put(
              Maneuver.ON_RAMP_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_NORMAL_LEFT)
          .put(
              Maneuver.ON_RAMP_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_NORMAL_RIGHT)
          .put(
              Maneuver.ON_RAMP_KEEP_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_NORMAL_LEFT)
          .put(
              Maneuver.ON_RAMP_KEEP_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_NORMAL_RIGHT)
          .put(
              Maneuver.ON_RAMP_SLIGHT_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_SLIGHT_LEFT)
          .put(
              Maneuver.ON_RAMP_SLIGHT_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_SLIGHT_RIGHT)
          .put(
              Maneuver.ON_RAMP_SHARP_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_SHARP_LEFT)
          .put(
              Maneuver.ON_RAMP_SHARP_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_SHARP_RIGHT)
          .put(
              Maneuver.ON_RAMP_U_TURN_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_U_TURN_RIGHT)
          .put(
              Maneuver.ON_RAMP_U_TURN_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ON_RAMP_U_TURN_LEFT)
          .put(
              Maneuver.OFF_RAMP_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_NORMAL_LEFT)
          .put(
              Maneuver.OFF_RAMP_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_NORMAL_RIGHT)
          .put(
              Maneuver.OFF_RAMP_KEEP_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_SLIGHT_LEFT)
          .put(
              Maneuver.OFF_RAMP_KEEP_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT)
          .put(
              Maneuver.OFF_RAMP_SLIGHT_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_SLIGHT_LEFT)
          .put(
              Maneuver.OFF_RAMP_SLIGHT_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_SLIGHT_RIGHT)
          .put(
              Maneuver.OFF_RAMP_SHARP_LEFT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_NORMAL_LEFT)
          .put(
              Maneuver.OFF_RAMP_SHARP_RIGHT,
              androidx.car.app.navigation.model.Maneuver.TYPE_OFF_RAMP_NORMAL_RIGHT)
          .put(
              Maneuver.ROUNDABOUT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW)
          .put(
              Maneuver.ROUNDABOUT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW)
          .put(
              Maneuver.ROUNDABOUT_STRAIGHT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CW)
          .put(
              Maneuver.ROUNDABOUT_STRAIGHT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_ENTER_CCW)
          .put(
              Maneuver.ROUNDABOUT_LEFT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_LEFT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_RIGHT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_RIGHT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SLIGHT_LEFT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SLIGHT_LEFT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SLIGHT_RIGHT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SLIGHT_RIGHT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SHARP_LEFT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SHARP_LEFT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SHARP_RIGHT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_SHARP_RIGHT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_U_TURN_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_U_TURN_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver
                  .TYPE_ROUNDABOUT_ENTER_AND_EXIT_CCW_WITH_ANGLE)
          .put(
              Maneuver.ROUNDABOUT_EXIT_CLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_EXIT_CW)
          .put(
              Maneuver.ROUNDABOUT_EXIT_COUNTERCLOCKWISE,
              androidx.car.app.navigation.model.Maneuver.TYPE_ROUNDABOUT_EXIT_CCW)
          .put(Maneuver.FERRY_BOAT, androidx.car.app.navigation.model.Maneuver.TYPE_FERRY_BOAT)
          .put(Maneuver.FERRY_TRAIN, androidx.car.app.navigation.model.Maneuver.TYPE_FERRY_TRAIN)
          .put(Maneuver.NAME_CHANGE, androidx.car.app.navigation.model.Maneuver.TYPE_NAME_CHANGE)
          .buildOrThrow();

  /** Represents the roundabout turn angle for a slight turn in either right or left directions. */
  private static final int ROUNDABOUT_ANGLE_SLIGHT = 10;

  /** Represents the roundabout turn angle for a normal turn in either right or left directions. */
  private static final int ROUNDABOUT_ANGLE_NORMAL = 45;

  /** Represents the roundabout turn angle for a sharp turn in either right or left directions. */
  private static final int ROUNDABOUT_ANGLE_SHARP = 135;

  /** Represents the roundabout turn angle for a u-turn in either right or left directions. */
  private static final int ROUNDABOUT_ANGLE_U_TURN = 180;

  /**
   * Returns the corresponding {@link androidx.car.app.navigation.model.Maneuver.Type} for the given
   * direction {@link Maneuver}
   *
   * @throws {@link IllegalArgumentException} if the given maneuver does not have a corresponding
   *     Android Auto Maneuver type.
   */
  public static int getAndroidAutoManeuverType(@Maneuver int maneuver) {
    if (MANEUVER_TO_ANDROID_AUTO_MANEUVER_TYPE.containsKey(maneuver)) {
      return MANEUVER_TO_ANDROID_AUTO_MANEUVER_TYPE.get(maneuver);
    }
    throw new IllegalArgumentException(
        String.format(
            "Given turn-by-turn Maneuver %d cannot be converted to an Android Auto equivalent.",
            maneuver));
  }

  /**
   * Returns the corresponding Android Auto roundabout angle for the given turn {@link Maneuver}.
   * Returns {@code null} if given maneuver does not involve a roundabout with a turn.
   */
  @Nullable
  public static Integer getAndroidAutoRoundaboutAngle(@Maneuver int maneuver) {
    if (maneuver == Maneuver.ROUNDABOUT_LEFT_CLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_RIGHT_CLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_LEFT_COUNTERCLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_RIGHT_COUNTERCLOCKWISE) {
      return ROUNDABOUT_ANGLE_NORMAL;
    }
    if (maneuver == Maneuver.ROUNDABOUT_SHARP_LEFT_CLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_SHARP_RIGHT_CLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_SHARP_LEFT_COUNTERCLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_SHARP_RIGHT_COUNTERCLOCKWISE) {
      return ROUNDABOUT_ANGLE_SHARP;
    }
    if (maneuver == Maneuver.ROUNDABOUT_SLIGHT_LEFT_CLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_SLIGHT_RIGHT_CLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_SLIGHT_LEFT_COUNTERCLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_SLIGHT_RIGHT_COUNTERCLOCKWISE) {
      return ROUNDABOUT_ANGLE_SLIGHT;
    }
    if (maneuver == Maneuver.ROUNDABOUT_U_TURN_CLOCKWISE
        || maneuver == Maneuver.ROUNDABOUT_U_TURN_COUNTERCLOCKWISE) {
      return ROUNDABOUT_ANGLE_U_TURN;
    }
    return null;
  }
}