diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index 9167327a9c..b3b955f59c 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -38,6 +38,8 @@
   //-->
   <uses-permission android:name="android.permission.WAKE_LOCK"/>
   <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+  <uses-permission android:name="androidx.car.app.NAVIGATION_TEMPLATES"/>
+  <uses-permission android:name="androidx.car.app.ACCESS_SURFACE"/>
 
   <queries>
     <intent>
@@ -723,6 +725,14 @@
       android:name="app.organicmaps.background.OsmUploadService"
       android:permission="android.permission.BIND_JOB_SERVICE"
       android:exported="false"/>
+    <service
+        android:name="app.organicmaps.car.NavigationCarAppService"
+        android:exported="true">
+      <intent-filter>
+        <action android:name="androidx.car.app.CarAppService" />
+        <category android:name="androidx.car.app.category.NAVIGATION" />
+      </intent-filter>
+    </service>
 
     <!-- Catches app upgraded intent -->
     <receiver
@@ -748,5 +758,13 @@
     <!-- Disable Google's anonymous stats collection -->
     <meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" />
 
+    <!-- For android Auto -->
+    <meta-data android:name="com.google.android.gms.car.application"
+        android:resource="@xml/automotive_app_desc"/>
+
+    <meta-data
+        android:name="androidx.car.app.minCarApiLevel"
+        android:value="1"/>
+
   </application>
 </manifest>
diff --git a/android/build.gradle b/android/build.gradle
index 96c989afd2..8f23e41966 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -84,6 +84,7 @@ dependencies {
 
   implementation 'androidx.annotation:annotation:1.5.0'
   implementation 'androidx.appcompat:appcompat:1.7.0-alpha01'
+  implementation 'androidx.car.app:app:1.3.0-rc01'
   implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
   implementation 'androidx.fragment:fragment:1.5.4'
   // Lifecycle is added as a workaround for duplicate classes error caused by some outdated dependency:
@@ -97,6 +98,8 @@ dependencies {
   implementation 'androidx.work:work-runtime:2.7.1'
   implementation 'com.google.android.material:material:1.8.0-alpha02'
   implementation 'com.google.code.gson:gson:2.10'
+  // Fix for app/organicmaps/util/FileUploadWorker.java:14: error: cannot access ListenableFuture
+  implementation 'com.google.guava:guava:29.0-android'
   implementation 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar'
   implementation 'com.github.devnullorthrow:MPAndroidChart:3.2.0-alpha'
   implementation 'net.jcip:jcip-annotations:1.0'
diff --git a/android/gradle.properties b/android/gradle.properties
index c9cf2e5f8f..5608450632 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,4 +1,4 @@
-propMinSdkVersion=21
+propMinSdkVersion=23
 propTargetSdkVersion=33
 propCompileSdkVersion=33
 propBuildToolsVersion=33.0.0
diff --git a/android/res/drawable/ic_check_box.xml b/android/res/drawable/ic_check_box.xml
new file mode 100644
index 0000000000..50101a198b
--- /dev/null
+++ b/android/res/drawable/ic_check_box.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
+</vector>
diff --git a/android/res/drawable/ic_check_box_checked.xml b/android/res/drawable/ic_check_box_checked.xml
new file mode 100644
index 0000000000..5c0babf540
--- /dev/null
+++ b/android/res/drawable/ic_check_box_checked.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+    android:viewportHeight="24" android:viewportWidth="24"
+    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@android:color/white" android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
+</vector>
diff --git a/android/res/xml/automotive_app_desc.xml b/android/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000000..83b683397c
--- /dev/null
+++ b/android/res/xml/automotive_app_desc.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<automotiveApp>
+  <uses name="template" />
+</automotiveApp>
diff --git a/android/src/app/organicmaps/car/NavigationCarAppService.java b/android/src/app/organicmaps/car/NavigationCarAppService.java
new file mode 100644
index 0000000000..3bcadafb78
--- /dev/null
+++ b/android/src/app/organicmaps/car/NavigationCarAppService.java
@@ -0,0 +1,30 @@
+package app.organicmaps.car;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarAppService;
+import androidx.car.app.Session;
+import androidx.car.app.validation.HostValidator;
+
+import app.organicmaps.BuildConfig;
+
+public final class NavigationCarAppService extends CarAppService
+{
+  @NonNull
+  @Override
+  public HostValidator createHostValidator()
+  {
+    if (BuildConfig.DEBUG)
+      return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR;
+
+    return new HostValidator.Builder(getApplicationContext())
+        .addAllowedHosts(androidx.car.app.R.array.hosts_allowlist_sample)
+        .build();
+  }
+
+  @NonNull
+  @Override
+  public Session onCreateSession()
+  {
+    return new NavigationSession();
+  }
+}
diff --git a/android/src/app/organicmaps/car/NavigationSession.java b/android/src/app/organicmaps/car/NavigationSession.java
new file mode 100644
index 0000000000..e500aa5f16
--- /dev/null
+++ b/android/src/app/organicmaps/car/NavigationSession.java
@@ -0,0 +1,69 @@
+package app.organicmaps.car;
+
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.Screen;
+import androidx.car.app.Session;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import app.organicmaps.MwmApplication;
+import app.organicmaps.car.screens.ErrorScreen;
+import app.organicmaps.car.screens.NavigationScreen;
+
+import java.io.IOException;
+
+public final class NavigationSession extends Session implements DefaultLifecycleObserver
+{
+  private static final String TAG = NavigationSession.class.getSimpleName();
+
+  private final SurfaceRenderer mSurfaceRenderer;
+  private boolean mInitFailed = false;
+
+  public NavigationSession()
+  {
+    getLifecycle().addObserver(this);
+    mSurfaceRenderer = new SurfaceRenderer(getCarContext(), getLifecycle());
+  }
+
+  @NonNull
+  @Override
+  public Screen onCreateScreen(@NonNull Intent intent)
+  {
+    Log.d(TAG, "onCreateScreen()");
+    if (mInitFailed)
+      return new ErrorScreen(getCarContext());
+
+    return new NavigationScreen(getCarContext(), mSurfaceRenderer);
+  }
+
+  @Override
+  public void onCreate(@NonNull LifecycleOwner owner)
+  {
+    Log.d(TAG, "onCreate()");
+    init();
+  }
+
+  @Override
+  public void onResume(@NonNull LifecycleOwner owner)
+  {
+    Log.d(TAG, "onResume()");
+    init();
+  }
+
+  private void init()
+  {
+    mInitFailed = false;
+    MwmApplication app = MwmApplication.from(getCarContext());
+    try
+    {
+      app.init();
+    } catch (IOException e)
+    {
+      mInitFailed = true;
+      Log.e(TAG, "Failed to initialize the app.");
+    }
+  }
+}
diff --git a/android/src/app/organicmaps/car/SurfaceRenderer.java b/android/src/app/organicmaps/car/SurfaceRenderer.java
new file mode 100644
index 0000000000..7db089925e
--- /dev/null
+++ b/android/src/app/organicmaps/car/SurfaceRenderer.java
@@ -0,0 +1,172 @@
+package app.organicmaps.car;
+
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.AppManager;
+import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
+import androidx.car.app.SurfaceCallback;
+import androidx.car.app.SurfaceContainer;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+
+import app.organicmaps.Map;
+import app.organicmaps.R;
+
+public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallback
+{
+  private static final String TAG = SurfaceRenderer.class.getSimpleName();
+
+  private final CarContext mCarContext;
+  private final Map mMap;
+
+  @Nullable
+  private Rect mVisibleArea;
+  @Nullable
+  private Rect mStableArea;
+
+  public SurfaceRenderer(@NonNull CarContext carContext, @NonNull Lifecycle lifecycle)
+  {
+    Log.d(TAG, "SurfaceRenderer()");
+    mCarContext = carContext;
+    mMap = new Map();
+    lifecycle.addObserver(this);
+  }
+
+  @Override
+  public void onSurfaceAvailable(@NonNull SurfaceContainer surfaceContainer)
+  {
+    Log.d(TAG, "Surface available " + surfaceContainer);
+    mMap.onSurfaceCreated(
+        mCarContext,
+        surfaceContainer.getSurface(),
+        new Rect(0, 0, surfaceContainer.getWidth(), surfaceContainer.getHeight()),
+        surfaceContainer.getDpi()
+    );
+  }
+
+  @Override
+  public void onVisibleAreaChanged(@NonNull Rect visibleArea)
+  {
+    Log.d(TAG, "Visible area changed. stableArea: " + mStableArea + " visibleArea:" + visibleArea);
+    mVisibleArea = visibleArea;
+  }
+
+  @Override
+  public void onStableAreaChanged(@NonNull Rect stableArea)
+  {
+    Log.d(TAG, "Stable area changed. stableArea: " + mStableArea + " visibleArea:" + mVisibleArea);
+    mStableArea = stableArea;
+  }
+
+  @Override
+  public void onSurfaceDestroyed(@NonNull SurfaceContainer surfaceContainer)
+  {
+    Log.d(TAG, "Surface destroyed");
+    mMap.onSurfaceDestroyed(false, true);
+  }
+
+  @Override
+  public void onCreate(@NonNull LifecycleOwner owner)
+  {
+    Log.d(TAG, "onCreate");
+    mCarContext.getCarService(AppManager.class).setSurfaceCallback(this);
+
+    // TODO: Properly process deep links from other apps on AA.
+    boolean launchByDeepLink = false;
+    mMap.onCreate(launchByDeepLink);
+  }
+
+  @Override
+  public void onStart(@NonNull LifecycleOwner owner)
+  {
+    Log.d(TAG, "onStart");
+    mMap.onStart();
+    mMap.setCallbackUnsupported(this::reportUnsupported);
+  }
+
+  @Override
+  public void onStop(@NonNull LifecycleOwner owner)
+  {
+    Log.d(TAG, "onStop");
+    mMap.onStop();
+    mMap.setCallbackUnsupported(null);
+  }
+
+  @Override
+  public void onPause(@NonNull LifecycleOwner owner)
+  {
+    Log.d(TAG, "onPause");
+    mMap.onPause(mCarContext);
+  }
+
+  @Override
+  public void onResume(@NonNull LifecycleOwner owner)
+  {
+    Log.d(TAG, "onResume");
+    mMap.onResume();
+  }
+
+  @Override
+  public void onScroll(float distanceX, float distanceY)
+  {
+    Log.d(TAG, "onScroll: distanceX: " + distanceX + ", distanceY: " + distanceY);
+    mMap.onScroll(distanceX, distanceY);
+  }
+
+  @Override
+  public void onFling(float velocityX, float velocityY)
+  {
+    Log.d(TAG, "onFling: velocityX: " + velocityX + ", velocityY: " + velocityY);
+  }
+
+  public void onZoomIn()
+  {
+    Map.zoomIn();
+  }
+
+  public void onZoomOut()
+  {
+    Map.zoomOut();
+  }
+
+  @Override
+  public void onScale(float focusX, float focusY, float scaleFactor)
+  {
+    Log.d(TAG, "onScale: focusX: " + focusX + ", focusY: " + focusY + ", scaleFactor: " + scaleFactor);
+    float x = focusX;
+    float y = focusY;
+
+    Rect visibleArea = mVisibleArea;
+    if (visibleArea != null)
+    {
+      // If a focal point value is negative, use the center point of the visible area.
+      if (x < 0)
+        x = visibleArea.centerX();
+      if (y < 0)
+        y = visibleArea.centerY();
+    }
+
+    final boolean animated = Float.compare(scaleFactor, 2f) == 0;
+
+    Map.onScale(scaleFactor, x, y, animated);
+  }
+
+  @Override
+  public void onClick(float x, float y)
+  {
+    Log.d(TAG, "onClick: x: " + x + ", y: " + y);
+    Map.onTouch(x, y);
+  }
+
+  private void reportUnsupported()
+  {
+    String message = mCarContext.getString(R.string.unsupported_phone);
+    Log.e(TAG, mCarContext.getString(R.string.unsupported_phone));
+    CarToast.makeText(mCarContext, message, CarToast.LENGTH_LONG).show();
+  }
+}
diff --git a/android/src/app/organicmaps/car/UiHelpers.java b/android/src/app/organicmaps/car/UiHelpers.java
new file mode 100644
index 0000000000..c7980b89c6
--- /dev/null
+++ b/android/src/app/organicmaps/car/UiHelpers.java
@@ -0,0 +1,47 @@
+package app.organicmaps.car;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
+import androidx.car.app.ScreenManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.ActionStrip;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.navigation.model.MapController;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.screens.settings.SettingsScreen;
+
+public final class UiHelpers
+{
+  public static ActionStrip createSettingsActionStrip(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    final CarIcon iconSettings = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_settings)).build();
+    final Action settings = new Action.Builder().setIcon(iconSettings).setOnClickListener(
+        () -> context.getCarService(ScreenManager.class).push(new SettingsScreen(context, surfaceRenderer))
+    ).build();
+    return new ActionStrip.Builder().addAction(settings).build();
+  }
+
+  public static MapController createMapController(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    final CarIcon iconPlus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_plus)).build();
+    final CarIcon iconMinus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_minus)).build();
+    final CarIcon iconLocation = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_not_follow)).build();
+
+    final Action panAction = new Action.Builder(Action.PAN).build();
+    final Action location = new Action.Builder().setIcon(iconLocation).setOnClickListener(
+        () -> CarToast.makeText(context, "Location", CarToast.LENGTH_LONG).show()
+    ).build();
+    final Action zoomIn = new Action.Builder().setIcon(iconPlus).setOnClickListener(surfaceRenderer::onZoomIn).build();
+    final Action zoomOut = new Action.Builder().setIcon(iconMinus).setOnClickListener(surfaceRenderer::onZoomOut).build();
+    final ActionStrip mapActionStrip = new ActionStrip.Builder()
+        .addAction(location)
+        .addAction(zoomIn)
+        .addAction(zoomOut)
+        .addAction(panAction)
+        .build();
+    return new MapController.Builder().setMapActionStrip(mapActionStrip).build();
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/BookmarksScreen.java b/android/src/app/organicmaps/car/screens/BookmarksScreen.java
new file mode 100644
index 0000000000..42cb9dee3b
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/BookmarksScreen.java
@@ -0,0 +1,135 @@
+package app.organicmaps.car.screens;
+
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.CarContext;
+import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.bookmarks.data.BookmarkCategory;
+import app.organicmaps.bookmarks.data.BookmarkInfo;
+import app.organicmaps.bookmarks.data.BookmarkManager;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.util.Graphics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BookmarksScreen extends MapScreen
+{
+  private final int MAX_CATEGORIES_SIZE;
+
+  @Nullable
+  private BookmarkCategory mBookmarkCategory;
+
+  public BookmarksScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    super(carContext, surfaceRenderer);
+    final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
+    MAX_CATEGORIES_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
+  }
+
+  private BookmarksScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer, @NonNull BookmarkCategory bookmarkCategory)
+  {
+    this(carContext, surfaceRenderer);
+    mBookmarkCategory = bookmarkCategory;
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    MapTemplate.Builder builder = new MapTemplate.Builder();
+    builder.setHeader(createHeader());
+    builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+    builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer()));
+    builder.setItemList(mBookmarkCategory == null ? createBookmarkCategoriesList() : createBookmarksList());
+    return builder.build();
+  }
+
+  @NonNull
+  private Header createHeader()
+  {
+    Header.Builder builder = new Header.Builder();
+    builder.setStartHeaderAction(Action.BACK);
+    builder.setTitle(mBookmarkCategory == null ? getCarContext().getString(R.string.bookmarks) : mBookmarkCategory.getName());
+    return builder.build();
+  }
+
+  @NonNull
+  private ItemList createBookmarkCategoriesList()
+  {
+    final List<BookmarkCategory> bookmarkCategories = getBookmarks();
+    final int categoriesSize = Math.min(bookmarkCategories.size(), MAX_CATEGORIES_SIZE);
+
+    ItemList.Builder builder = new ItemList.Builder();
+    for (int i = 0; i < categoriesSize; ++i)
+    {
+      final BookmarkCategory bookmarkCategory = bookmarkCategories.get(i);
+
+      Row.Builder itemBuilder = new Row.Builder();
+      itemBuilder.setTitle(bookmarkCategory.getName());
+      itemBuilder.addText(bookmarkCategory.getDescription());
+      itemBuilder.setOnClickListener(() -> getScreenManager().push(new BookmarksScreen(getCarContext(), getSurfaceRenderer(), bookmarkCategory)));
+      itemBuilder.setBrowsable(true);
+      builder.addItem(itemBuilder.build());
+    }
+    return builder.build();
+  }
+
+  @NonNull
+  private ItemList createBookmarksList()
+  {
+    final long bookmarkCategoryId = mBookmarkCategory.getId();
+    final int bookmarkCategoriesSize = Math.min(mBookmarkCategory.getBookmarksCount(), MAX_CATEGORIES_SIZE);
+
+    ItemList.Builder builder = new ItemList.Builder();
+    for (int i = 0; i < bookmarkCategoriesSize; ++i)
+    {
+      final long bookmarkId = BookmarkManager.INSTANCE.getBookmarkIdByPosition(bookmarkCategoryId, i);
+      final BookmarkInfo bookmarkInfo = new BookmarkInfo(bookmarkCategoryId, bookmarkId);
+
+      final Row.Builder itemBuilder = new Row.Builder();
+      itemBuilder.setTitle(bookmarkInfo.getName());
+      if (!bookmarkInfo.getAddress().isEmpty())
+        itemBuilder.addText(bookmarkInfo.getAddress());
+      if (!bookmarkInfo.getFeatureType().isEmpty())
+        itemBuilder.addText(bookmarkInfo.getFeatureType());
+      final Drawable icon = Graphics.drawCircleAndImage(bookmarkInfo.getIcon().argb(),
+          R.dimen.track_circle_size,
+          bookmarkInfo.getIcon().getResId(),
+          R.dimen.bookmark_icon_size,
+          getCarContext());
+      itemBuilder.setImage(new CarIcon.Builder(IconCompat.createWithBitmap(Graphics.drawableToBitmap(icon))).build());
+      builder.addItem(itemBuilder.build());
+    }
+    return builder.build();
+  }
+
+  @NonNull
+  private static List<BookmarkCategory> getBookmarks()
+  {
+    final List<BookmarkCategory> bookmarkCategories = new ArrayList<>(BookmarkManager.INSTANCE.getCategories());
+
+    final List<BookmarkCategory> toRemove = new ArrayList<>();
+    for (BookmarkCategory bookmarkCategory : bookmarkCategories)
+    {
+      if (bookmarkCategory.getBookmarksCount() == 0)
+        toRemove.add(bookmarkCategory);
+    }
+    bookmarkCategories.removeAll(toRemove);
+
+    return bookmarkCategories;
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/CategoriesScreen.java b/android/src/app/organicmaps/car/screens/CategoriesScreen.java
new file mode 100644
index 0000000000..4595273821
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/CategoriesScreen.java
@@ -0,0 +1,95 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import androidx.car.app.CarContext;
+import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CategoriesScreen extends MapScreen
+{
+  private static class CategoryData
+  {
+    @StringRes
+    public final int nameResId;
+
+    @DrawableRes
+    public final int iconResId;
+
+    public CategoryData(int nameResId, int iconResId)
+    {
+      this.nameResId = nameResId;
+      this.iconResId = iconResId;
+    }
+  }
+
+  private static final List<CategoryData> CATEGORIES = Arrays.asList(
+      new CategoryData(R.string.fuel, R.drawable.ic_category_fuel),
+      new CategoryData(R.string.parking, R.drawable.ic_category_parking),
+      new CategoryData(R.string.eat, R.drawable.ic_category_eat),
+      new CategoryData(R.string.food, R.drawable.ic_category_food),
+      new CategoryData(R.string.hotel, R.drawable.ic_category_hotel),
+      new CategoryData(R.string.toilet, R.drawable.ic_category_toilet),
+      new CategoryData(R.string.rv, R.drawable.ic_category_rv)
+  );
+
+  private final int MAX_CATEGORIES_SIZE;
+
+  public CategoriesScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    super(carContext, surfaceRenderer);
+    final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
+    MAX_CATEGORIES_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    MapTemplate.Builder builder = new MapTemplate.Builder();
+    builder.setHeader(createHeader());
+    builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+    builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer()));
+    builder.setItemList(createCategoriesList());
+    return builder.build();
+  }
+
+  @NonNull
+  private Header createHeader()
+  {
+    Header.Builder builder = new Header.Builder();
+    builder.setStartHeaderAction(Action.BACK);
+    builder.setTitle(getCarContext().getString(R.string.categories));
+    return builder.build();
+  }
+
+  @NonNull
+  private ItemList createCategoriesList()
+  {
+    ItemList.Builder builder = new ItemList.Builder();
+    int categoriesSize = Math.min(CATEGORIES.size(), MAX_CATEGORIES_SIZE);
+    for (int i = 0; i < categoriesSize; ++i)
+    {
+      Row.Builder itemBuilder = new Row.Builder();
+      itemBuilder.setTitle(getCarContext().getString(CATEGORIES.get(i).nameResId));
+      itemBuilder.setImage(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), CATEGORIES.get(i).iconResId)).build());
+      builder.addItem(itemBuilder.build());
+    }
+    return builder.build();
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/ErrorScreen.java b/android/src/app/organicmaps/car/screens/ErrorScreen.java
new file mode 100644
index 0000000000..bcdaf8525f
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/ErrorScreen.java
@@ -0,0 +1,32 @@
+package app.organicmaps.car.screens;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.model.LongMessageTemplate;
+import androidx.car.app.model.Template;
+
+import app.organicmaps.R;
+
+public class ErrorScreen extends Screen
+{
+  private static final String TAG = ErrorScreen.class.getSimpleName();
+
+  public ErrorScreen(@NonNull CarContext carContext)
+  {
+    super(carContext);
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    Log.d(TAG, "onGetTemplate");
+    LongMessageTemplate.Builder builder = new LongMessageTemplate.Builder(getCarContext().getString(R.string.dialog_error_storage_message));
+    builder.setTitle(getCarContext().getString(R.string.dialog_error_storage_title));
+
+    return builder.build();
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/MapScreen.java b/android/src/app/organicmaps/car/screens/MapScreen.java
new file mode 100644
index 0000000000..38ab3de45d
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/MapScreen.java
@@ -0,0 +1,25 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+
+import app.organicmaps.car.SurfaceRenderer;
+
+public abstract class MapScreen extends Screen
+{
+  @NonNull
+  private final SurfaceRenderer mSurfaceRenderer;
+
+  public MapScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    super(carContext);
+    mSurfaceRenderer = surfaceRenderer;
+  }
+
+  @NonNull
+  public SurfaceRenderer getSurfaceRenderer()
+  {
+    return mSurfaceRenderer;
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/NavigationScreen.java b/android/src/app/organicmaps/car/screens/NavigationScreen.java
new file mode 100644
index 0000000000..477113f8d0
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/NavigationScreen.java
@@ -0,0 +1,104 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.Item;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+
+public class NavigationScreen extends MapScreen
+{
+  public NavigationScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    super(carContext, surfaceRenderer);
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    MapTemplate.Builder builder = new MapTemplate.Builder();
+    builder.setHeader(createHeader());
+    builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+    builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer()));
+    builder.setItemList(createList());
+    return builder.build();
+  }
+
+  @NonNull
+  private Header createHeader()
+  {
+    Header.Builder builder = new Header.Builder();
+    builder.setStartHeaderAction(new Action.Builder(Action.APP_ICON).build());
+    builder.setTitle(getCarContext().getString(R.string.app_name));
+    return builder.build();
+  }
+
+  @NonNull
+  private ItemList createList()
+  {
+    ItemList.Builder builder = new ItemList.Builder();
+    builder.addItem(createSearchItem());
+    builder.addItem(createCategoriesItem());
+    builder.addItem(createBookmarksItem());
+    return builder.build();
+  }
+
+  @NonNull
+  private Item createSearchItem()
+  {
+    final CarIcon iconSearch = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_search)).build();
+
+    Row.Builder builder = new Row.Builder();
+    builder.setTitle(getCarContext().getString(R.string.search));
+    builder.setImage(iconSearch);
+    builder.setBrowsable(true);
+    builder.setOnClickListener(this::openSearch);
+    return builder.build();
+  }
+
+  @NonNull
+  private Item createCategoriesItem()
+  {
+    Row.Builder builder = new Row.Builder();
+    builder.setTitle(getCarContext().getString(R.string.categories));
+    builder.setBrowsable(true);
+    builder.setOnClickListener(this::openCategories);
+    return builder.build();
+  }
+
+  @NonNull
+  private Item createBookmarksItem()
+  {
+    Row.Builder builder = new Row.Builder();
+    builder.setTitle(getCarContext().getString(R.string.bookmarks));
+    builder.setBrowsable(true);
+    builder.setOnClickListener(this::openBookmarks);
+    return builder.build();
+  }
+
+  private void openSearch()
+  {
+    getScreenManager().push(new SearchScreen(getCarContext()));
+  }
+
+  private void openCategories()
+  {
+    getScreenManager().push(new CategoriesScreen(getCarContext(), getSurfaceRenderer()));
+  }
+
+  private void openBookmarks()
+  {
+    getScreenManager().push(new BookmarksScreen(getCarContext(), getSurfaceRenderer()));
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/SearchScreen.java b/android/src/app/organicmaps/car/screens/SearchScreen.java
new file mode 100644
index 0000000000..f6464aa869
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/SearchScreen.java
@@ -0,0 +1,62 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.SearchTemplate;
+import androidx.car.app.model.Template;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.search.SearchRecents;
+
+public class SearchScreen extends Screen implements SearchTemplate.SearchCallback
+{
+  private final int MAX_RESULTS_SIZE;
+  private ItemList mResults;
+  private String mSearchText = "";
+
+  public SearchScreen(@NonNull CarContext carContext)
+  {
+    super(carContext);
+    final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
+    MAX_RESULTS_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    SearchTemplate.Builder builder = new SearchTemplate.Builder(this);
+    builder.setHeaderAction(Action.BACK);
+    builder.setShowKeyboardByDefault(false);
+    if (mSearchText.isEmpty() || mResults == null)
+      loadRecents();
+    builder.setItemList(mResults);
+    builder.setInitialSearchText(mSearchText);
+    return builder.build();
+  }
+
+  private void loadRecents()
+  {
+    final CarIcon iconRecent = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_search_recent)).build();
+
+    ItemList.Builder builder = new ItemList.Builder();
+    builder.setNoItemsMessage(getCarContext().getString(R.string.search_history_text));
+    SearchRecents.refresh();
+    int recentsSize = Math.min(SearchRecents.getSize(), MAX_RESULTS_SIZE);
+    for (int i = 0; i < recentsSize; ++i)
+    {
+      Row.Builder itemBuilder = new Row.Builder();
+      itemBuilder.setTitle(SearchRecents.get(i));
+      itemBuilder.setImage(iconRecent);
+      builder.addItem(itemBuilder.build());
+    }
+    mResults = builder.build();
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java
new file mode 100644
index 0000000000..3142500653
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java
@@ -0,0 +1,82 @@
+package app.organicmaps.car.screens.settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.car.screens.MapScreen;
+import app.organicmaps.routing.RoutingOptions;
+import app.organicmaps.settings.RoadType;
+
+public class DrivingOptionsScreen extends MapScreen
+{
+  @NonNull
+  private final CarIcon mCheckboxIcon;
+  @NonNull
+  private final CarIcon mCheckboxSelectedIcon;
+
+  public DrivingOptionsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    super(carContext, surfaceRenderer);
+    mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box)).build();
+    mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box_checked)).build();
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    MapTemplate.Builder builder = new MapTemplate.Builder();
+    builder.setHeader(createHeader());
+    builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+    builder.setItemList(createDrivingOptionsList());
+    return builder.build();
+  }
+
+  @NonNull
+  private Header createHeader()
+  {
+    Header.Builder builder = new Header.Builder();
+    builder.setStartHeaderAction(Action.BACK);
+    builder.setTitle(getCarContext().getString(R.string.driving_options_subheader));
+    return builder.build();
+  }
+
+  @NonNull
+  private ItemList createDrivingOptionsList()
+  {
+    ItemList.Builder builder = new ItemList.Builder();
+    builder.addItem(createDrivingOptionCheckbox(RoadType.Toll, R.string.avoid_tolls));
+    builder.addItem(createDrivingOptionCheckbox(RoadType.Dirty, R.string.avoid_unpaved));
+    builder.addItem(createDrivingOptionCheckbox(RoadType.Ferry, R.string.avoid_ferry));
+    builder.addItem(createDrivingOptionCheckbox(RoadType.Motorway, R.string.avoid_motorways));
+    return builder.build();
+  }
+
+  @NonNull
+  private Row createDrivingOptionCheckbox(RoadType roadType, @StringRes int titleRes)
+  {
+    Row.Builder builder = new Row.Builder();
+    builder.setTitle(getCarContext().getString(titleRes));
+    builder.setOnClickListener(() -> {
+      if (RoutingOptions.hasOption(roadType))
+        RoutingOptions.removeOption(roadType);
+      else
+        RoutingOptions.addOption(roadType);
+      DrivingOptionsScreen.this.invalidate();
+    });
+    builder.setImage(RoutingOptions.hasOption(roadType) ? mCheckboxSelectedIcon : mCheckboxIcon);
+    return builder.build();
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/settings/HelpScreen.java b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java
new file mode 100644
index 0000000000..aa1ffab2f9
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java
@@ -0,0 +1,74 @@
+package app.organicmaps.car.screens.settings;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.Item;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+
+import app.organicmaps.BuildConfig;
+import app.organicmaps.Framework;
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.car.screens.MapScreen;
+import app.organicmaps.util.DateUtils;
+
+public class HelpScreen extends MapScreen
+{
+  public HelpScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    super(carContext, surfaceRenderer);
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    MapTemplate.Builder builder = new MapTemplate.Builder();
+    builder.setHeader(createHeader());
+    builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+    builder.setItemList(createSettingsList());
+    return builder.build();
+  }
+
+  @NonNull
+  private Header createHeader()
+  {
+    Header.Builder builder = new Header.Builder();
+    builder.setStartHeaderAction(Action.BACK);
+    builder.setTitle(getCarContext().getString(R.string.help));
+    return builder.build();
+  }
+
+  @NonNull
+  private ItemList createSettingsList()
+  {
+    ItemList.Builder builder = new ItemList.Builder();
+    builder.addItem(createVersionInfo());
+    builder.addItem(createDataVersionInfo());
+    return builder.build();
+  }
+
+  @NonNull
+  private Item createVersionInfo()
+  {
+    return new Row.Builder()
+        .setTitle(getCarContext().getString(R.string.app_name))
+        .addText(BuildConfig.VERSION_NAME)
+        .build();
+  }
+
+  @NonNull
+  private Item createDataVersionInfo()
+  {
+    return new Row.Builder()
+        .setTitle(getCarContext().getString(R.string.data_version, ""))
+        .addText(DateUtils.getLocalDate(Framework.nativeGetDataVersion()))
+        .build();
+  }
+}
diff --git a/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java
new file mode 100644
index 0000000000..58acd4942f
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java
@@ -0,0 +1,111 @@
+package app.organicmaps.car.screens.settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.Item;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.car.screens.MapScreen;
+import app.organicmaps.util.Config;
+
+public class SettingsScreen extends MapScreen
+{
+  private interface PrefsGetter
+  {
+    boolean get();
+  }
+
+  private interface PrefsSetter
+  {
+    void set(boolean newValue);
+  }
+
+  @NonNull
+  private final CarIcon mCheckboxIcon;
+  @NonNull
+  private final CarIcon mCheckboxSelectedIcon;
+
+  public SettingsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+  {
+    super(carContext, surfaceRenderer);
+    mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box)).build();
+    mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box_checked)).build();
+  }
+
+  @NonNull
+  @Override
+  public Template onGetTemplate()
+  {
+    MapTemplate.Builder builder = new MapTemplate.Builder();
+    builder.setHeader(createHeader());
+    builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+    builder.setItemList(createSettingsList());
+    return builder.build();
+  }
+
+  @NonNull
+  private Header createHeader()
+  {
+    Header.Builder builder = new Header.Builder();
+    builder.setStartHeaderAction(Action.BACK);
+    builder.setTitle(getCarContext().getString(R.string.settings));
+    return builder.build();
+  }
+
+  @NonNull
+  private ItemList createSettingsList()
+  {
+    ItemList.Builder builder = new ItemList.Builder();
+    builder.addItem(createRoutingOptionsItem());
+    builder.addItem(createSharedPrefsCheckbox(R.string.big_font, Config::isLargeFontsSize, Config::setLargeFontsSize));
+    builder.addItem(createSharedPrefsCheckbox(R.string.transliteration_title, Config::isTransliteration, Config::setTransliteration));
+    builder.addItem(createHelpItem());
+    return builder.build();
+  }
+
+  @NonNull
+  private Item createRoutingOptionsItem()
+  {
+    Row.Builder builder = new Row.Builder();
+    builder.setTitle(getCarContext().getString(R.string.driving_options_title));
+    builder.setOnClickListener(() -> getScreenManager().push(new DrivingOptionsScreen(getCarContext(), getSurfaceRenderer())));
+    builder.setBrowsable(true);
+    return builder.build();
+  }
+
+  @NonNull
+  private Item createHelpItem()
+  {
+    Row.Builder builder = new Row.Builder();
+    builder.setTitle(getCarContext().getString(R.string.help));
+    builder.setOnClickListener(() -> getScreenManager().push(new HelpScreen(getCarContext(), getSurfaceRenderer())));
+    builder.setBrowsable(true);
+    return builder.build();
+  }
+
+  @NonNull
+  private Row createSharedPrefsCheckbox(@StringRes int titleRes, PrefsGetter getter, PrefsSetter setter)
+  {
+    final boolean getterValue = getter.get();
+
+    Row.Builder builder = new Row.Builder();
+    builder.setTitle(getCarContext().getString(titleRes));
+    builder.setOnClickListener(() -> {
+      setter.set(!getterValue);
+      SettingsScreen.this.invalidate();
+    });
+    builder.setImage(getterValue ? mCheckboxSelectedIcon : mCheckboxIcon);
+    return builder.build();
+  }
+}