Android Auto এর জন্য নেভিগেশন সক্ষম করুন

ইন-ড্যাশ হেড ইউনিটগুলিতে আপনার অ্যাপের নেভিগেশন অভিজ্ঞতা প্রদর্শন করতে আপনি কীভাবে Android for Cars অ্যাপ লাইব্রেরির সাথে নেভিগেশন SDK ব্যবহার করতে পারেন তা এই বিভাগটি বর্ণনা করে। যদি একজন ড্রাইভারের ইন-ড্যাশ সিস্টেম অ্যান্ড্রয়েড অটোকে সমর্থন করে, তাহলে ড্রাইভাররা তাদের ফোনটিকে ইউনিটের সাথে সংযুক্ত করে সরাসরি তাদের গাড়ির ডিসপ্লেতে আপনার অ্যাপটি ব্যবহার করতে পারে। ভয়েস গাইডেন্স গাড়ির স্পীকারেও চলে।

একটি ইন-ড্যাশ হেড ইউনিট যা Android Auto এর জন্য সক্ষম একটি অ্যাপ ব্যবহার করে নির্দেশিত নেভিগেশন প্রদর্শন করে।

অ্যান্ড্রয়েড ফর কার অ্যাপ লাইব্রেরি ড্রাইভার নিরাপত্তার জন্য অনুমোদিত ভিজ্যুয়াল টেমপ্লেটের একটি সেট প্রদান করে অ্যান্ড্রয়েড অ্যাপ্লিকেশানগুলিকে অ্যান্ড্রয়েড অটোতে চালানোর জন্য সক্ষম করে৷ ড্রাইভারের বিভ্রান্তি কমাতে এই টেমপ্লেটগুলি উদ্দেশ্যমূলকভাবে ফোনের ইন-ড্যাশবোর্ড UI নিয়ন্ত্রণগুলিকে সীমাবদ্ধ করে।

আপনি যখন আপনার নেভিগেশন SDK-চালিত অ্যাপটিকে Android Auto-এর সাথে কাজ করতে সক্ষম করেন, তখন আপনি নেভিগেশন অভিজ্ঞতার জন্য একটি অতিরিক্ত ভিউ প্রদান করেন। এটি দুটি মানচিত্র দর্শনের অনুমতি দেয় - একটি ফোনের জন্য এবং একটি হেড ইউনিটের জন্য৷ উভয় ডিসপ্লে Navigator.java থেকে নির্দেশিকা পায়, যা একটি সিঙ্গেলটন।

ইন-ড্যাশবোর্ড সিস্টেম নিরাপত্তা অনুমোদিত ইন্টারেক্টিভ উপাদানগুলি প্রদর্শন করে যাতে ড্রাইভার অযথা বিভ্রান্তি ছাড়াই নিরাপদে তাদের গন্তব্যে নেভিগেট করতে পারে। ড্রাইভার আপনার অ্যাপ-নির্দিষ্ট কার্যকারিতার সাথেও ইন্টারঅ্যাক্ট করতে পারে, যেমন অর্ডার গ্রহণ করা বা প্রত্যাখ্যান করা বা ম্যাপে গ্রাহকের অবস্থান দেখা। অর্ডার স্ট্যাটাস আপডেট ইন-ড্যাশবোর্ড ইউনিটেও প্রদর্শিত হতে পারে।

একটি ইন-ড্যাশ হেড ইউনিট যা Android Auto-এর সাথে পালাক্রমে নির্দেশিকা প্রদর্শন করে।একটি অ্যান্ড্রয়েড ফোন যা একটি ওভারভিউ হিসাবে একই রুট প্রদর্শন করে।

সংযুক্ত ফোনটি আপনার অ্যাপ্লিকেশানে স্ট্যান্ডার্ড নেভিগেশন SDK অভিজ্ঞতা বা অন্য কোনো দৃশ্য বা কর্মপ্রবাহ প্রদর্শন করা চালিয়ে যেতে পারে । এটি আপনাকে কাস্টম কার্যকারিতা প্রদান চালিয়ে যেতে দেয় যা একটি গাড়ির স্ক্রিনে ভাল কাজ নাও করতে পারে।

সেট আপ করুন

Android Auto-এর সাথে আপনার অ্যাপটি কাজ করার প্রথম অংশে Android Auto-এর সাথে একটি গাড়ি পরিষেবা সেট আপ করা এবং তারপরে আপনার নেভিগেশন SDK অ্যাপে TurnByTurn লাইব্রেরি সক্ষম করা অন্তর্ভুক্ত।

Android Auto দিয়ে শুরু করুন

Android Auto-এর সাথে কাজ করার জন্য ডিজাইন করা নেভিগেশন SDK বৈশিষ্ট্যগুলির সাথে কাজ শুরু করার আগে, আপনাকে অবশ্যই আপনার অ্যাপের জন্য একটি গাড়ি পরিষেবা সেট আপ করতে হবে যাতে Android Auto এটি আবিষ্কার করতে পারে।

এই পদক্ষেপগুলি অনুসরণ করুন, যার সবকটিই Android for Cars বিকাশকারীদের ডকুমেন্টেশনে পাওয়া যাবে:

  1. মৌলিক Android Auto বৈশিষ্ট্যগুলির সাথে নিজেকে পরিচিত করুন
  2. Android for Cars অ্যাপ লাইব্রেরি ইনস্টল করুন
  3. Android Auto অন্তর্ভুক্ত করতে আপনার অ্যাপের ম্যানিফেস্ট ফাইল কনফিগার করুন
  4. আপনার ম্যানিফেস্টে ন্যূনতম কার-অ্যাপ লেভেল 1 ঘোষণা করুন
  5. আপনার CarAppService এবং সেশন তৈরি করুন

নেভিগেশন SDK সেট আপ করুন৷

একবার আপনি আপনার গাড়ি অ্যাপ পরিষেবা প্রতিষ্ঠা করলে, আপনি নেভিগেশন SDK-এর সাথে কাজ করতে প্রস্তুত৷

  1. আপনার প্রজেক্ট সেট আপ করুন , যদি আপনি ইতিমধ্যে আপনার অ্যাপে নেভিগেশন SDK সংহত না করে থাকেন।
  2. আপনার অ্যাপের জন্য TurnbyTurn নির্দেশিকা ফিড সক্ষম করুন
  3. ঐচ্ছিক। নেভিগেশন SDK থেকে জেনারেট করা আইকন ব্যবহার করুন
  4. Screen ক্লাসে প্রদত্ত অ্যান্ড্রয়েড অটো সারফেসে NavigationViewForAuto ক্লাস ব্যবহার করে মানচিত্র আঁকুন
  5. TurnbyTurn লাইব্রেরি থেকে ডেটা দিয়ে Android Auto নেভিগেশন টেমপ্লেট তৈরি করুন

এখন যেহেতু আপনার অ্যাপে নেভিগেশন তথ্য প্রদানের জন্য আপনার কাছে একটি নিবন্ধিত পরিষেবা রয়েছে এবং আপনার অ্যাপ Android Auto-এর সাথে সংযুক্ত হতে পারে, আপনি আপনার অ্যাপের জন্য Android Auto-এর সাথে সঠিকভাবে কাজ করার জন্য প্রয়োজনীয় বাকি নেভিগেশন উপাদানগুলি তৈরি করতে প্রস্তুত:

মানচিত্র এবং নেভিগেশন UI আঁকুন

NavigationViewForAuto ক্লাস অ্যান্ড্রয়েড অটো স্ক্রীনে একটি মানচিত্র এবং নেভিগেশন UI রেন্ডার করে। এটি ফোনের জন্য NavigationView মতো একই কার্যকারিতা প্রদান করে, তবে সীমিত ইন্টারঅ্যাক্টিভিটি সহ। Android Auto দ্বারা প্রদত্ত সারফেসে আঁকার জন্য NavigationViewForAuto ব্যবহার করুন:

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 সারফেসকলব্যাক পদ্ধতির একটি সিরিজে স্ক্রীন পৃষ্ঠের মিথস্ক্রিয়াকে সীমাবদ্ধ করে। একটি ইন-ড্যাশ স্ক্রিনে মানচিত্রের সাথে সীমিত ড্রাইভার মিথস্ক্রিয়া সমর্থন করতে এই কলব্যাকগুলি ব্যবহার করুন৷ উদাহরণস্বরূপ, 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 নির্দেশিকা জন্য একটি টার্ন কার্ড।

অ্যান্ড্রয়েড অটো নেভিগেশন টেমপ্লেট একটি টার্ন কার্ড প্রদান করে যা বর্তমান ট্রিপের সাথে সম্পর্কিত নেভিগেশন তথ্য প্রদর্শন করে। নেভিগেশন SDK-এর TurnByTurn লাইব্রেরি এই নেভিগেশন তথ্য প্রদান করে, যা আপনার কোড Android Auto নেভিগেশন টেমপ্লেট তৈরি করতে ব্যবহার করে।

একটি পর্যবেক্ষক সেট আপ করুন

নিম্নলিখিত উদাহরণে, SampleApplication হল একটি কাস্টম অ্যাপ্লিকেশন ক্লাস যা একটি MutableLiveData<NavInfo> অবজেক্ট বজায় রাখে। যখন পর্যবেক্ষক নেভিগেটর অবজেক্ট থেকে একটি আপডেট পায়, তখন সে এই NavInfo অবজেক্টটিকে SampleApplication ক্লাস দ্বারা রক্ষণাবেক্ষণ করা NavInfoMutableLiveData এ পোস্ট করে।

নিম্নলিখিত উদাহরণটি অ্যান্ড্রয়েড অটো স্ক্রীনের বাস্তবায়নে এই বস্তুর জন্য একজন পর্যবেক্ষক নিবন্ধন করে।

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

সঠিক কৌশলের ধরন নিশ্চিত করুন

অ্যান্ড্রয়েড অটো কার লাইব্রেরিতে যে ধরনের কৌশল ব্যবহার করা হয় তা TurnByTurn লাইব্রেরি দ্বারা প্রদত্ত কৌশলগুলির সাথে একের পর এক মিলে যায়৷ যাইহোক, আপনাকে অবশ্যই নেভিগেশন 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;
  }
}