پیمایش را برای Android Auto فعال کنید

این بخش توضیح می‌دهد که چگونه می‌توانید از Navigation SDK با کتابخانه برنامه Android for Cars برای نمایش تجربه پیمایش برنامه خود در یونیت‌های اصلی داخل داشبورد استفاده کنید. اگر سیستم داشبورد راننده از Android Auto پشتیبانی می‌کند، رانندگان می‌توانند با اتصال تلفن خود به دستگاه، از برنامه شما مستقیماً روی نمایشگر خودروی خود استفاده کنند. راهنمای صوتی نیز روی بلندگوهای خودرو اجرا می شود.

یک یونیت سر درون داشبورد که با استفاده از یک برنامه فعال برای Android Auto، پیمایش هدایت‌شده را نمایش می‌دهد.

کتابخانه برنامه Android for Cars با ارائه مجموعه‌ای از الگوهای تصویری تأیید شده برای ایمنی راننده، برنامه‌های Android را قادر می‌سازد تا در Android Auto اجرا شوند. این الگوها به منظور کاهش حواس‌پرتی راننده، کنترل‌های رابط کاربری درون داشبورد را از کنترل‌های تلفن محدود می‌کنند.

وقتی برنامه مجهز به SDK ناوبری خود را فعال می‌کنید تا با Android Auto کار کند، نمای دیگری برای تجربه پیمایش ارائه می‌دهید. این امکان برای دو نمای نقشه را فراهم می کند - یکی برای تلفن و دیگری برای یونیت اصلی. هر دو نمایشگر از Navigator.java راهنمایی دریافت می کنند که یک تک تن است.

سیستم داخل داشبورد عناصر تعاملی تایید شده ایمنی را نمایش می‌دهد تا راننده بتواند با خیال راحت به مقصد خود بدون حواس‌پرتی ناخواسته حرکت کند. درایور همچنین می‌تواند با عملکردهای خاص برنامه شما، مانند پذیرش یا رد سفارشات، یا مشاهده مکان مشتری روی نقشه، تعامل داشته باشد. به‌روزرسانی‌های وضعیت سفارش نیز می‌تواند در واحد داخل داشبورد ظاهر شود.

یک یونیت سر درون داشبورد که راهنمای حرکتی را با Android Auto نمایش می‌دهد.گوشی اندرویدی که همان مسیر را به عنوان نمای کلی نمایش می دهد.

تلفن پیوست می‌تواند به نمایش تجربه استاندارد SDK پیمایش یا هر نمای یا گردش کار دیگری در برنامه شما ادامه دهد . این به شما امکان می دهد به ارائه عملکردهای سفارشی ادامه دهید که ممکن است روی صفحه نمایش خودرو به خوبی کار نکند.

برپایی

اولین بخش از کارکرد برنامه شما با Android Auto شامل راه‌اندازی یک سرویس خودرو با Android Auto و سپس فعال کردن کتابخانه TurnByTurn در برنامه Navigation SDK است.

با Android Auto شروع کنید

قبل از شروع کار با ویژگی های Navigation SDK که برای کار با Android Auto طراحی شده اند، باید یک سرویس ماشین برای برنامه خود راه اندازی کنید تا Android Auto بتواند آن را کشف کند.

این مراحل را دنبال کنید، که همه آنها را می توان در اسناد توسعه دهندگان Android for Cars یافت:

  1. با ویژگی های اصلی Android Auto آشنا شوید .
  2. کتابخانه برنامه Android for Cars را نصب کنید .
  3. فایل مانیفست برنامه خود را به گونه‌ای پیکربندی کنید که Android Auto را در بر بگیرد.
  4. حداقل سطح برنامه ماشین را در مانیفست خود 1 اعلام کنید .
  5. CarAppService و session خود را ایجاد کنید .

Navigation SDK را تنظیم کنید

پس از ایجاد سرویس برنامه ماشین خود، آماده کار با Navigation SDK هستید.

  1. اگر قبلاً Navigation SDK را در برنامه خود ادغام نکرده اید، پروژه خود را راه اندازی کنید .
  2. فید راهنمای TurnbyTurn را برای برنامه خود فعال کنید .
  3. اختیاری. از نمادهای ایجاد شده از Navigation SDK استفاده کنید .
  4. نقشه را با استفاده از کلاس NavigationViewForAuto روی سطح Android Auto ارائه شده در کلاس Screen بکشید.
  5. الگوی پیمایش خودکار Android را با داده‌های کتابخانه TurnbyTurn پر کنید .

اکنون که یک سرویس ثبت‌شده برای ارائه اطلاعات ناوبری به برنامه خود دارید و برنامه شما می‌تواند به Android Auto متصل شود، آماده هستید تا بقیه عناصر ناوبری لازم برای عملکرد صحیح برنامه خود با Android Auto را ایجاد کنید:

نقشه و رابط کاربری ناوبری را بکشید

کلاس NavigationViewForAuto یک نقشه و رابط کاربری پیمایش را در صفحه‌های Android Auto ارائه می‌کند. این بیشتر عملکردهای مشابه NavigationView را برای تلفن ها ارائه می دهد، اما با تعامل محدود. از NavigationViewForAuto برای کشیدن روی سطح ارائه شده توسط Android Auto استفاده کنید:

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();
}

تعامل نقشه را فعال کنید

برای اطمینان از ایمنی راننده، Android Auto تعامل سطح صفحه را به یک سری از روش‌های SurfaceCallback محدود می‌کند. از این تماس‌ها برای پشتیبانی از تعامل محدود راننده با نقشه روی صفحه‌نمایش داشبورد استفاده کنید. به عنوان مثال، onClick و onScale مربوط به ژست‌های ضربه و پینچ کاربر هستند. تماس های تعاملی باید از نوار اقدام نقشه به صورت زیر استفاده کنند:

  • برای دریافت تماس های تعاملی نقشه، برنامه شما باید از دکمه Action.PAN استفاده کند.

  • برای پشتیبانی از اقدامات اضافی کاربر، دکمه هایی را به نوار اقدام نقشه اضافه کنید.

فعال کردن تماس های سطحی

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

زوم با خرج کردن

@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.
}

پانینگ

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

نمایش جهت های ناوبری

این بخش نحوه تنظیم یک ناظر برای پست های ناوبری و پر کردن جهت های ناوبری در الگوی کارت نوبت را پوشش می دهد.

کارت نوبت برای راهنمایی Android Auto.

الگوی پیمایش خودکار Android یک کارت نوبت ارائه می دهد که اطلاعات ناوبری مربوط به سفر فعلی را نمایش می دهد. کتابخانه TurnByTurn در Navigation SDK این اطلاعات پیمایش را ارائه می دهد که کد شما از آن برای پر کردن الگوی پیمایش Android Auto استفاده می کند.

یک ناظر راه اندازی کنید

در مثال زیر، SampleApplication یک کلاس برنامه سفارشی است که یک شی MutableLiveData<NavInfo> را نگهداری می کند. هنگامی که ناظر یک به روز رسانی از شی ناوبر دریافت می کند، این شی NavInfo را به NavInfoMutableLiveData که توسط کلاس SampleApplication نگهداری می شود، پست می کند.

مثال زیر یک ناظر را برای این شی در اجرای آن در Android Auto Screen ثبت می کند.

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

اطلاعات ناوبری را پر کنید

قطعه کد زیر نحوه پر کردن الگوی Android Auto با اطلاعات مسیریابی فعلی، از جمله مراحل، مسافت‌ها و نمادها را نشان می‌دهد. می توانید اطلاعات بیشتری درباره این عناصر نمایشگر در پر کردن صفحه نمایش فید بخوانید.

برای دیدن نمونه کد باز کنید.

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);
  }
}

از انواع صحیح مانور اطمینان حاصل کنید

انواع مانورهای مورد استفاده در کتابخانه Android Auto Car یک به یک با مانورهای ارائه شده توسط کتابخانه TurnByTurn مطابقت دارد. با این حال، باید مانورهای Navigation SDK را به یک اعلامیه معتبر در کتابخانه Android Auto Car تبدیل کنید. جدول زیر مکاتبات چند فیلد را نشان می‌دهد و به دنبال آن یک ابزار مبدل نمونه برای راحتی شما نشان می‌دهد.

مانور گام به گام کتابخانه مانور خودکار اندروید
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

برای دیدن نمونه کد باز کنید.

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;
  }
}