From 3975f6799aea02313d8bfa28c0a163a99d4b80ee Mon Sep 17 00:00:00 2001
From: 0xRe1nk0 <0xre1nk0@gmail.com>
Date: Wed, 25 Dec 2024 18:16:25 +0200
Subject: [PATCH 01/10] add route legend card

---
 OsmAnd/res/layout/fragment_mtb_routes.xml     | 10 ++
 OsmAnd/res/layout/route_legend_card.xml       | 45 +++++++++
 OsmAnd/res/layout/route_legend_item.xml       | 78 +++++++++++++++
 .../plus/configmap/MtbRoutesFragment.java     | 20 +++-
 .../plus/configmap/RouteLegendCard.java       | 99 +++++++++++++++++++
 5 files changed, 251 insertions(+), 1 deletion(-)
 create mode 100644 OsmAnd/res/layout/route_legend_card.xml
 create mode 100644 OsmAnd/res/layout/route_legend_item.xml
 create mode 100644 OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java

diff --git a/OsmAnd/res/layout/fragment_mtb_routes.xml b/OsmAnd/res/layout/fragment_mtb_routes.xml
index eb19e5e4622..1dddc9e261a 100644
--- a/OsmAnd/res/layout/fragment_mtb_routes.xml
+++ b/OsmAnd/res/layout/fragment_mtb_routes.xml
@@ -64,6 +64,16 @@
 
 			</LinearLayout>
 
+			<include layout="@layout/list_item_divider" />
+
+			<LinearLayout
+				android:id="@+id/legend_container"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:orientation="vertical">
+
+			</LinearLayout>
+
 			<include
 				android:id="@+id/bottom_divider"
 				layout="@layout/card_bottom_divider" />
diff --git a/OsmAnd/res/layout/route_legend_card.xml b/OsmAnd/res/layout/route_legend_card.xml
new file mode 100644
index 00000000000..56cf2023571
--- /dev/null
+++ b/OsmAnd/res/layout/route_legend_card.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:tools="http://schemas.android.com/tools"
+	android:id="@+id/route_legend_card"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:orientation="vertical">
+
+	<LinearLayout
+		android:id="@+id/properties_container"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:background="?attr/list_background_color"
+		android:orientation="vertical">
+
+		<net.osmand.plus.widgets.TextViewEx
+			android:id="@+id/card_title"
+			style="@style/TitleStyle.Medium"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:layout_marginHorizontal="@dimen/content_padding"
+			android:layout_marginVertical="@dimen/content_padding_small"
+			android:textColor="?android:textColorPrimary"
+			tools:text="Legend" />
+
+		<LinearLayout
+			android:id="@+id/classification_properties"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:orientation="vertical">
+
+		</LinearLayout>
+
+	</LinearLayout>
+
+	<LinearLayout
+		android:id="@+id/main_container"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:background="?attr/list_background_color"
+		android:orientation="vertical">
+
+	</LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/OsmAnd/res/layout/route_legend_item.xml b/OsmAnd/res/layout/route_legend_item.xml
new file mode 100644
index 00000000000..0351d0d4313
--- /dev/null
+++ b/OsmAnd/res/layout/route_legend_item.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:tools="http://schemas.android.com/tools"
+	android:id="@+id/main_view"
+	android:layout_width="match_parent"
+	android:layout_height="wrap_content"
+	android:background="?attr/selectableItemBackground"
+	android:descendantFocusability="blocksDescendants"
+	android:gravity="center"
+	android:orientation="horizontal">
+
+	<androidx.appcompat.widget.AppCompatImageView
+		android:id="@+id/icon"
+		android:layout_width="@dimen/standard_icon_size"
+		android:layout_height="@dimen/standard_icon_size"
+		android:layout_marginStart="@dimen/content_padding"
+		android:layout_marginEnd="@dimen/favorites_icon_right_margin"
+		android:src="@drawable/ic_action_route_direction_here" />
+
+	<LinearLayout
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:orientation="vertical">
+
+		<LinearLayout
+			android:layout_width="match_parent"
+			android:layout_height="@dimen/setting_profile_item_height"
+			android:orientation="horizontal">
+
+			<LinearLayout
+				android:layout_width="0dp"
+				android:layout_height="match_parent"
+				android:layout_weight="1"
+				android:gravity="center"
+				android:orientation="vertical">
+
+				<TextView
+					android:id="@+id/title"
+					android:layout_width="match_parent"
+					android:layout_height="wrap_content"
+					android:ellipsize="end"
+					android:maxLines="2"
+					android:textColor="?android:textColorPrimary"
+					android:textSize="@dimen/default_list_text_size"
+					tools:text="Title" />
+
+				<TextView
+					android:id="@+id/description"
+					android:layout_width="match_parent"
+					android:layout_height="wrap_content"
+					android:ellipsize="end"
+					android:maxLines="1"
+					android:textColor="?android:textColorSecondary"
+					android:textSize="@dimen/default_desc_text_size"
+					tools:text="Description" />
+
+			</LinearLayout>
+
+			<androidx.appcompat.widget.SwitchCompat
+				android:id="@+id/compound_button"
+				android:layout_width="wrap_content"
+				android:layout_height="match_parent"
+				android:layout_gravity="center_vertical|end"
+				android:background="@null"
+				android:focusableInTouchMode="true"
+				android:padding="@dimen/content_padding" />
+
+		</LinearLayout>
+
+		<View
+			android:id="@+id/divider_bottom"
+			android:layout_width="match_parent"
+			android:layout_height="1dp"
+			android:background="?attr/list_divider" />
+
+	</LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java
index 8d168156a54..bbba0646405 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java
@@ -15,6 +15,7 @@
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
 import net.osmand.plus.base.BaseOsmAndFragment;
+import net.osmand.plus.configmap.RouteLegendCard.DataClass;
 import net.osmand.plus.configmap.routes.MtbClassification;
 import net.osmand.plus.configmap.routes.RouteLayersHelper;
 import net.osmand.plus.helpers.AndroidUiHelper;
@@ -25,6 +26,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.IntStream;
 
 public class MtbRoutesFragment extends BaseOsmAndFragment {
 
@@ -55,6 +57,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
 		setupClassifications(view);
 		updateClassificationPreferences();
 
+		setupLegendCard(view);
 		return view;
 	}
 
@@ -85,6 +88,22 @@ private void setupClassifications(@NonNull View view) {
 		}
 	}
 
+	@NonNull
+	private List<DataClass> getDataClasses() {
+		return IntStream.rangeClosed(1, 9)
+				.mapToObj(i -> new DataClass("Legend item " + i, "Description"))
+				.toList();
+	}
+
+	private void setupLegendCard(@NonNull View view) {
+		List<DataClass> items = getDataClasses();
+
+		RouteLegendCard card = new RouteLegendCard(requireActivity(), items, app.getString(R.string.shared_string_legend));
+		ViewGroup group = view.findViewById(R.id.legend_container);
+		View cardView = card.build();
+		group.addView(cardView);
+	}
+
 	private View createRadioButton(@NonNull MtbClassification classification, @NonNull LayoutInflater inflater, @Nullable ViewGroup container, boolean hasDivider) {
 		View view = inflater.inflate(R.layout.item_with_radiobutton_and_descr, container, false);
 		view.setTag(classification);
@@ -142,5 +161,4 @@ public static void showInstance(@NonNull FragmentManager manager) {
 					.commitAllowingStateLoss();
 		}
 	}
-
 }
diff --git a/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java b/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
new file mode 100644
index 00000000000..be8c0127060
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
@@ -0,0 +1,99 @@
+package net.osmand.plus.configmap;
+
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.FragmentActivity;
+
+import net.osmand.plus.R;
+import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.routepreparationmenu.cards.BaseCard;
+import net.osmand.plus.settings.backend.OsmandSettings;
+import net.osmand.plus.utils.AndroidUtils;
+import net.osmand.plus.utils.UiUtilities;
+
+import java.util.List;
+
+public class RouteLegendCard extends BaseCard {
+	private final LayoutInflater themedInflater;
+
+	private final List<DataClass> items;
+	private final String cardTitle;
+
+	public RouteLegendCard(@NonNull FragmentActivity activity, @NonNull List<DataClass> items, @NonNull String cardTitle) {
+		super(activity, true);
+		this.items = items;
+		this.cardTitle = cardTitle;
+		themedInflater = UiUtilities.getInflater(activity, nightMode);
+	}
+
+	@Override
+	public int getCardLayoutId() {
+		return R.layout.route_legend_card;
+	}
+
+	@Override
+	protected void updateContent() {
+		setupCardTitle();
+		setupLegendItems();
+	}
+
+	private void setupLegendItems() {
+		ViewGroup mainContainer = view.findViewById(R.id.main_container);
+		mainContainer.removeAllViews();
+		for (int i = 0; i < items.size(); i++) {
+			mainContainer.addView(createView(i));
+		}
+	}
+
+	@NonNull
+	private View createView(int position) {
+		DataClass dataClass = items.get(position);
+		View itemView = themedInflater.inflate(R.layout.route_legend_item, null, false);
+		CompoundButton compoundButton = itemView.findViewById(R.id.compound_button);
+		TextView title = itemView.findViewById(R.id.title);
+		TextView description = itemView.findViewById(R.id.description);
+		View divider = itemView.findViewById(R.id.divider_bottom);
+
+		title.setText(dataClass.title());
+		description.setText(dataClass.description());
+
+		compoundButton.setChecked(isClassEnabled(dataClass));
+
+		AndroidUiHelper.updateVisibility(divider, position != items.size() - 1);
+		itemView.setOnClickListener(view -> {
+			compoundButton.performClick();
+			onClassSelected(dataClass, compoundButton.isChecked());
+		});
+
+		compoundButton.setFocusable(false);
+		compoundButton.setClickable(false);
+
+		OsmandSettings settings = app.getSettings();
+		Drawable background = UiUtilities.getColoredSelectableDrawable(app, settings.getApplicationMode().getProfileColor(nightMode), 0.3f);
+		AndroidUtils.setBackground(itemView, background);
+
+		return itemView;
+	}
+
+	private void setupCardTitle() {
+		TextView title = view.findViewById(R.id.card_title);
+		title.setText(cardTitle);
+	}
+
+	public boolean isClassEnabled(@NonNull DataClass dataClass) {
+		return false;
+	}
+
+	public void onClassSelected(@NonNull DataClass dataClass, boolean checked) {
+
+	}
+
+	public record DataClass(String title, String description) {
+	}
+}

From a9be1650d30752b6ad1fc4018238e2437d4e7819 Mon Sep 17 00:00:00 2001
From: 0xRe1nk0 <0xre1nk0@gmail.com>
Date: Wed, 8 Jan 2025 10:22:32 +0200
Subject: [PATCH 02/10] add rendering class support/link with colors attributes

---
 .../net/osmand/render/RenderingClass.java     | 59 +++++++++++
 .../osmand/render/RenderingRulesStorage.java  | 25 +++++
 OsmAnd/res/layout/fragment_mtb_routes.xml     | 14 ---
 .../res/layout/map_route_types_fragment.xml   | 14 +++
 OsmAnd/res/layout/route_legend_item.xml       |  6 +-
 .../plus/configmap/HikingRoutesFragment.java  | 24 +++++
 .../plus/configmap/MtbRoutesFragment.java     | 19 ----
 .../plus/configmap/RouteLegendCard.java       | 10 +-
 .../plus/configmap/routes/RClassUtils.java    | 99 +++++++++++++++++++
 9 files changed, 233 insertions(+), 37 deletions(-)
 create mode 100644 OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
 create mode 100644 OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java

diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
new file mode 100644
index 00000000000..ca94585995e
--- /dev/null
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
@@ -0,0 +1,59 @@
+package net.osmand.render;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class RenderingClass {
+    private final String name;
+    private final String title;
+    private final boolean enable;
+    private String path;
+    private final List<RenderingClass> children = new ArrayList<>();
+
+    public RenderingClass(String name, String title, boolean enable) {
+        this.name = name;
+        this.title = title;
+        this.enable = enable;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public List<RenderingClass> getChildren() {
+        return children;
+    }
+
+    public void addChild(RenderingClass child) {
+        children.add(child);
+    }
+
+    public RenderingClass findByPath(String path) {
+        if (this.path.equals(path)) {
+            return this;
+        }
+        for (RenderingClass child : children) {
+            RenderingClass result = child.findByPath(path);
+            if (result != null) {
+                return result;
+            }
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
index 2b639e050c9..f56e7942272 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
@@ -55,6 +55,7 @@ public class RenderingRulesStorage {
 	
 	protected Map<String, RenderingRule> renderingAttributes = new LinkedHashMap<String, RenderingRule>();
 	protected Map<String, RenderingRule> renderingAssociations = new LinkedHashMap<String, RenderingRule>();
+	protected Map<String, RenderingClass> renderingClasses = new LinkedHashMap<String, RenderingClass>();
 	protected Map<String, String> renderingConstants = new LinkedHashMap<String, String>();
 	
 	protected String renderingName;
@@ -271,6 +272,7 @@ private class RenderingRulesHandler {
 		private final XmlPullParser parser;
 		private int state;
 		Stack<RenderingRule> stack = new Stack<RenderingRule>();
+		private final Stack<RenderingClass> renderingClassStack = new Stack<>();
 		private final RenderingRulesStorageResolver resolver;
 		private final boolean addon;
 		private RenderingRulesStorage dependsStorage;
@@ -431,6 +433,23 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 				}
 			} else if ("renderer".equals(name)) { //$NON-NLS-1$
 				throw new XmlPullParserException("Rendering style is deprecated and no longer supported.");
+			}else if ("renderingClass".equals(name)) {
+				String className = parser.getAttributeValue(null, "name");
+				String title = parser.getAttributeValue(null, "title");
+				boolean enable = Boolean.parseBoolean(parser.getAttributeValue(null, "enable"));
+
+				RenderingClass newClass = new RenderingClass(className, title, enable);
+
+				if (!renderingClassStack.isEmpty()) {
+					RenderingClass parent = renderingClassStack.peek();
+					parent.addChild(newClass);
+					newClass.setPath(parent.getPath() + className);
+				} else {
+					newClass.setPath(className);
+				}
+
+				renderingClasses.put(newClass.getPath(), newClass);
+				renderingClassStack.push(newClass);
 			} else {
 				log.warn("Unknown tag : " + name); //$NON-NLS-1$
 			}
@@ -483,6 +502,8 @@ public void endElement(String name) throws XmlPullParserException {
 				stack.pop();
 			} else if ("renderingAssociation".equals(name)) { //$NON-NLS-1$
 				stack.pop();
+			} else if ("renderingClass".equals(name)) {
+				renderingClassStack.pop();
 			}
 		}
 	}
@@ -559,6 +580,10 @@ public RenderingRule getRule(int state, int key) {
 	public RenderingRule getRenderingAttributeRule(String attribute) {
 		return renderingAttributes.get(attribute);
 	}
+
+	public RenderingClass getRenderingClass(String path) {
+		return renderingClasses.get(path);
+	}
 	
 	public String[] getRenderingAttributeNames() {
 		return renderingAttributes.keySet().toArray(new String[0]);
diff --git a/OsmAnd/res/layout/fragment_mtb_routes.xml b/OsmAnd/res/layout/fragment_mtb_routes.xml
index 1dddc9e261a..bdf1533a17e 100644
--- a/OsmAnd/res/layout/fragment_mtb_routes.xml
+++ b/OsmAnd/res/layout/fragment_mtb_routes.xml
@@ -64,20 +64,6 @@
 
 			</LinearLayout>
 
-			<include layout="@layout/list_item_divider" />
-
-			<LinearLayout
-				android:id="@+id/legend_container"
-				android:layout_width="match_parent"
-				android:layout_height="wrap_content"
-				android:orientation="vertical">
-
-			</LinearLayout>
-
-			<include
-				android:id="@+id/bottom_divider"
-				layout="@layout/card_bottom_divider" />
-
 			<View
 				android:id="@+id/bottom_empty_space"
 				android:layout_width="match_parent"
diff --git a/OsmAnd/res/layout/map_route_types_fragment.xml b/OsmAnd/res/layout/map_route_types_fragment.xml
index ec808a87e37..b9cabbf7ee6 100644
--- a/OsmAnd/res/layout/map_route_types_fragment.xml
+++ b/OsmAnd/res/layout/map_route_types_fragment.xml
@@ -57,6 +57,20 @@
 			android:id="@+id/card_bottom_divider"
 			layout="@layout/card_bottom_divider" />
 
+		<include layout="@layout/list_item_divider" />
+
+		<LinearLayout
+			android:id="@+id/legend_container"
+			android:layout_width="match_parent"
+			android:layout_height="wrap_content"
+			android:orientation="vertical">
+
+		</LinearLayout>
+
+		<include
+			android:id="@+id/bottom_divider"
+			layout="@layout/card_bottom_divider" />
+
 		<View
 			android:id="@+id/bottom_empty_space"
 			android:layout_width="match_parent"
diff --git a/OsmAnd/res/layout/route_legend_item.xml b/OsmAnd/res/layout/route_legend_item.xml
index 0351d0d4313..3083179037e 100644
--- a/OsmAnd/res/layout/route_legend_item.xml
+++ b/OsmAnd/res/layout/route_legend_item.xml
@@ -9,13 +9,15 @@
 	android:gravity="center"
 	android:orientation="horizontal">
 
-	<androidx.appcompat.widget.AppCompatImageView
+	<ImageView
 		android:id="@+id/icon"
 		android:layout_width="@dimen/standard_icon_size"
 		android:layout_height="@dimen/standard_icon_size"
 		android:layout_marginStart="@dimen/content_padding"
 		android:layout_marginEnd="@dimen/favorites_icon_right_margin"
-		android:src="@drawable/ic_action_route_direction_here" />
+		android:layout_gravity="center_vertical"
+		tools:src="@drawable/ic_action_track_line_bold_color"
+		tools:tint="?attr/default_icon_color" />
 
 	<LinearLayout
 		android:layout_width="match_parent"
diff --git a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
index 70d3fc61710..7ae47997832 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
@@ -1,6 +1,8 @@
 package net.osmand.plus.configmap;
 
 import static net.osmand.osm.OsmRouteType.HIKING;
+import static net.osmand.plus.configmap.routes.RClassUtils.RClassType.HIKING_OSMC_NODES;
+import static net.osmand.plus.configmap.routes.RClassUtils.getDataClasses;
 
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -18,6 +20,7 @@
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
 import net.osmand.plus.base.BaseOsmAndFragment;
+import net.osmand.plus.configmap.RouteLegendCard.DataClass;
 import net.osmand.plus.configmap.routes.RouteLayersHelper;
 import net.osmand.plus.helpers.AndroidUiHelper;
 import net.osmand.plus.utils.AndroidUtils;
@@ -61,6 +64,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
 
 		showHideTopShadow(view);
 		setupHeader(view);
+		setupLegendCard(view);
 		setupTypesCard(view);
 
 		return view;
@@ -144,6 +148,7 @@ private TextRadioItem createRadioButton(@NonNull String value) {
 			if (view != null) {
 				setupHeader(view);
 				setupTypesCard(view);
+				setupLegendCard(view);
 			}
 			refreshMap();
 			return true;
@@ -151,6 +156,25 @@ private TextRadioItem createRadioButton(@NonNull String value) {
 		return item;
 	}
 
+	private void setupLegendCard(View view) {
+		ViewGroup group = view.findViewById(R.id.legend_container);
+		String propertyValue = routeLayersHelper.getSelectedHikingRoutesValue();
+
+		if (HIKING_OSMC_NODES.getPropertyValue().equals(propertyValue)) {
+			List<DataClass> dataClasses = getDataClasses(app, HIKING_OSMC_NODES);
+			if (!Algorithms.isEmpty(dataClasses)) {
+				RouteLegendCard card = new RouteLegendCard(requireActivity(), dataClasses, app.getString(R.string.shared_string_legend));
+				View cardView = card.build();
+				group.addView(cardView);
+				AndroidUiHelper.updateVisibility(group, true);
+			} else {
+				AndroidUiHelper.updateVisibility(group, false);
+			}
+		} else {
+			AndroidUiHelper.updateVisibility(group, false);
+		}
+	}
+
 	private void refreshMap() {
 		MapActivity mapActivity = (MapActivity) getMyActivity();
 		if (mapActivity != null) {
diff --git a/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java
index bbba0646405..fafab71d485 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/MtbRoutesFragment.java
@@ -15,7 +15,6 @@
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
 import net.osmand.plus.base.BaseOsmAndFragment;
-import net.osmand.plus.configmap.RouteLegendCard.DataClass;
 import net.osmand.plus.configmap.routes.MtbClassification;
 import net.osmand.plus.configmap.routes.RouteLayersHelper;
 import net.osmand.plus.helpers.AndroidUiHelper;
@@ -26,7 +25,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
-import java.util.stream.IntStream;
 
 public class MtbRoutesFragment extends BaseOsmAndFragment {
 
@@ -57,7 +55,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
 		setupClassifications(view);
 		updateClassificationPreferences();
 
-		setupLegendCard(view);
 		return view;
 	}
 
@@ -88,22 +85,6 @@ private void setupClassifications(@NonNull View view) {
 		}
 	}
 
-	@NonNull
-	private List<DataClass> getDataClasses() {
-		return IntStream.rangeClosed(1, 9)
-				.mapToObj(i -> new DataClass("Legend item " + i, "Description"))
-				.toList();
-	}
-
-	private void setupLegendCard(@NonNull View view) {
-		List<DataClass> items = getDataClasses();
-
-		RouteLegendCard card = new RouteLegendCard(requireActivity(), items, app.getString(R.string.shared_string_legend));
-		ViewGroup group = view.findViewById(R.id.legend_container);
-		View cardView = card.build();
-		group.addView(cardView);
-	}
-
 	private View createRadioButton(@NonNull MtbClassification classification, @NonNull LayoutInflater inflater, @Nullable ViewGroup container, boolean hasDivider) {
 		View view = inflater.inflate(R.layout.item_with_radiobutton_and_descr, container, false);
 		view.setTag(classification);
diff --git a/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java b/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
index be8c0127060..f2305a22679 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
@@ -5,6 +5,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CompoundButton;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -14,6 +15,7 @@
 import net.osmand.plus.helpers.AndroidUiHelper;
 import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.settings.backend.OsmandSettings;
+import net.osmand.plus.track.fragments.TrackAppearanceFragment;
 import net.osmand.plus.utils.AndroidUtils;
 import net.osmand.plus.utils.UiUtilities;
 
@@ -58,10 +60,14 @@ private View createView(int position) {
 		CompoundButton compoundButton = itemView.findViewById(R.id.compound_button);
 		TextView title = itemView.findViewById(R.id.title);
 		TextView description = itemView.findViewById(R.id.description);
+		AndroidUiHelper.updateVisibility(description, false);
 		View divider = itemView.findViewById(R.id.divider_bottom);
+		ImageView icon = itemView.findViewById(R.id.icon);
+
+		Drawable iconDrawable = TrackAppearanceFragment.getTrackIcon(app, null, false, dataClass.color);
+		icon.setImageDrawable(iconDrawable);
 
 		title.setText(dataClass.title());
-		description.setText(dataClass.description());
 
 		compoundButton.setChecked(isClassEnabled(dataClass));
 
@@ -94,6 +100,6 @@ public void onClassSelected(@NonNull DataClass dataClass, boolean checked) {
 
 	}
 
-	public record DataClass(String title, String description) {
+	public record DataClass(String title, int color) {
 	}
 }
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java
new file mode 100644
index 00000000000..737681c4b15
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java
@@ -0,0 +1,99 @@
+package net.osmand.plus.configmap.routes;
+
+import static net.osmand.render.RenderingRuleStorageProperties.ATTR_COLOR_VALUE;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.plus.OsmandApplication;
+import net.osmand.plus.configmap.RouteLegendCard;
+import net.osmand.render.RenderingClass;
+import net.osmand.render.RenderingRule;
+import net.osmand.render.RenderingRulesStorage;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RClassUtils {
+    public enum RClassType {
+        HIKING_OSMC_NODES(".route.hiking.osmc_nodes", "walkingRoutesOSMCNodes");
+
+        private final String path;
+        private final String propertyValue;
+
+        RClassType(@NonNull String path, @Nullable String propertyValue) {
+            this.path = path;
+            this.propertyValue = propertyValue;
+        }
+
+        public String getPropertyValue() {
+            return propertyValue;
+        }
+
+        public String getPath() {
+            return path;
+        }
+    }
+
+    public enum RClassColors {
+        IWN(".iwn", "iwnColor"),
+        NWN(".nwn", "nwnColor"),
+        RWN(".rwn", "rwnColor"),
+        LWN(".lwn", "lwnColor");
+
+        private final String className;
+        private final String colorName;
+
+        RClassColors(String className, String colorName) {
+            this.className = className;
+            this.colorName = colorName;
+        }
+
+        @Nullable
+        public static String getColorName(String className) {
+            for (RClassColors RClassColors : values()) {
+                if (RClassColors.className.equals(className)) {
+                    return RClassColors.colorName;
+                }
+            }
+            return null;
+        }
+    }
+
+    public static List<RouteLegendCard.DataClass> getDataClasses(@NonNull OsmandApplication app,
+                                                                 @NonNull RClassType rClassType){
+        List<RouteLegendCard.DataClass> dataClasses = new ArrayList<>();
+
+        RenderingRulesStorage routeRender = app.getRendererRegistry().getCurrentSelectedRenderer();
+        if (routeRender != null) {
+            RenderingClass rClass = routeRender.getRenderingClass(rClassType.getPath());
+            if (rClass != null) {
+                for (RenderingClass children : rClass.getChildren()) {
+                    String colorName = RClassColors.getColorName(children.getName());
+                    Integer color = parseColor(routeRender, colorName);
+                    if (color != null) {
+                        dataClasses.add(new RouteLegendCard.DataClass(children.getTitle(), color));
+                    }
+                }
+            }
+        }
+        return dataClasses;
+    }
+
+    public static Integer parseColor(@NonNull RenderingRulesStorage routeRender, @Nullable String colorName) {
+        if (colorName == null) {
+            return null;
+        }
+        Integer color = null;
+
+        RenderingRule colorRule = routeRender.getRenderingAttributeRule(colorName);
+        List<RenderingRule> rules = colorRule.getIfElseChildren();
+        for (RenderingRule rule : rules) {
+            int colorValue = rule.getIntPropertyValue(ATTR_COLOR_VALUE);
+            if (colorValue != -1) {
+                color = colorValue;
+            }
+        }
+        return color;
+    }
+}

From 5ba95d577ba78d110a6d38bc19ea1ed62509e004 Mon Sep 17 00:00:00 2001
From: 0xRe1nk0 <0xre1nk0@gmail.com>
Date: Wed, 8 Jan 2025 10:33:40 +0200
Subject: [PATCH 03/10] move value check to fragment

---
 .../osmand/plus/configmap/HikingRoutesFragment.java    |  3 ++-
 .../net/osmand/plus/configmap/routes/RClassUtils.java  | 10 ++--------
 2 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
index 7ae47997832..092fb6b9474 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
@@ -36,6 +36,7 @@
 public class HikingRoutesFragment extends BaseOsmAndFragment {
 
 	public static final String TAG = HikingRoutesFragment.class.getSimpleName();
+	public static final String NODE_NETWORKS_VALUE = "walkingRoutesOSMCNodes";
 
 	private RouteLayersHelper routeLayersHelper;
 	@Nullable
@@ -160,7 +161,7 @@ private void setupLegendCard(View view) {
 		ViewGroup group = view.findViewById(R.id.legend_container);
 		String propertyValue = routeLayersHelper.getSelectedHikingRoutesValue();
 
-		if (HIKING_OSMC_NODES.getPropertyValue().equals(propertyValue)) {
+		if (NODE_NETWORKS_VALUE.equals(propertyValue)) {
 			List<DataClass> dataClasses = getDataClasses(app, HIKING_OSMC_NODES);
 			if (!Algorithms.isEmpty(dataClasses)) {
 				RouteLegendCard card = new RouteLegendCard(requireActivity(), dataClasses, app.getString(R.string.shared_string_legend));
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java
index 737681c4b15..76d6ec5782e 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java
@@ -16,18 +16,12 @@
 
 public class RClassUtils {
     public enum RClassType {
-        HIKING_OSMC_NODES(".route.hiking.osmc_nodes", "walkingRoutesOSMCNodes");
+        HIKING_OSMC_NODES(".route.hiking.osmc_nodes");
 
         private final String path;
-        private final String propertyValue;
 
-        RClassType(@NonNull String path, @Nullable String propertyValue) {
+        RClassType(@NonNull String path) {
             this.path = path;
-            this.propertyValue = propertyValue;
-        }
-
-        public String getPropertyValue() {
-            return propertyValue;
         }
 
         public String getPath() {

From 83dea3306da4fe47ef6a9e8eded4578b34f39e69 Mon Sep 17 00:00:00 2001
From: 0xRe1nk0 <0xre1nk0@gmail.com>
Date: Wed, 8 Jan 2025 10:41:39 +0200
Subject: [PATCH 04/10] show/hide legend card UI fix

---
 .../res/layout/map_route_types_fragment.xml   | 20 +++++++++++++------
 .../plus/configmap/HikingRoutesFragment.java  | 12 ++++++-----
 2 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/OsmAnd/res/layout/map_route_types_fragment.xml b/OsmAnd/res/layout/map_route_types_fragment.xml
index b9cabbf7ee6..4c28d3d0d77 100644
--- a/OsmAnd/res/layout/map_route_types_fragment.xml
+++ b/OsmAnd/res/layout/map_route_types_fragment.xml
@@ -57,19 +57,27 @@
 			android:id="@+id/card_bottom_divider"
 			layout="@layout/card_bottom_divider" />
 
-		<include layout="@layout/list_item_divider" />
-
 		<LinearLayout
 			android:id="@+id/legend_container"
 			android:layout_width="match_parent"
 			android:layout_height="wrap_content"
 			android:orientation="vertical">
 
-		</LinearLayout>
+			<include layout="@layout/list_item_divider" />
 
-		<include
-			android:id="@+id/bottom_divider"
-			layout="@layout/card_bottom_divider" />
+			<LinearLayout
+				android:id="@+id/legend_content"
+				android:layout_width="match_parent"
+				android:layout_height="wrap_content"
+				android:orientation="vertical">
+
+			</LinearLayout>
+
+			<include
+				android:id="@+id/bottom_divider"
+				layout="@layout/card_bottom_divider" />
+
+		</LinearLayout>
 
 		<View
 			android:id="@+id/bottom_empty_space"
diff --git a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
index 092fb6b9474..f8483de9fe4 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
@@ -158,7 +158,9 @@ private TextRadioItem createRadioButton(@NonNull String value) {
 	}
 
 	private void setupLegendCard(View view) {
-		ViewGroup group = view.findViewById(R.id.legend_container);
+		ViewGroup container = view.findViewById(R.id.legend_container);
+		ViewGroup contentGroup = view.findViewById(R.id.legend_content);
+		contentGroup.removeAllViews();
 		String propertyValue = routeLayersHelper.getSelectedHikingRoutesValue();
 
 		if (NODE_NETWORKS_VALUE.equals(propertyValue)) {
@@ -166,13 +168,13 @@ private void setupLegendCard(View view) {
 			if (!Algorithms.isEmpty(dataClasses)) {
 				RouteLegendCard card = new RouteLegendCard(requireActivity(), dataClasses, app.getString(R.string.shared_string_legend));
 				View cardView = card.build();
-				group.addView(cardView);
-				AndroidUiHelper.updateVisibility(group, true);
+				contentGroup.addView(cardView);
+				AndroidUiHelper.updateVisibility(container, true);
 			} else {
-				AndroidUiHelper.updateVisibility(group, false);
+				AndroidUiHelper.updateVisibility(container, false);
 			}
 		} else {
-			AndroidUiHelper.updateVisibility(group, false);
+			AndroidUiHelper.updateVisibility(container, false);
 		}
 	}
 

From 9bda498855d2eb462fcb5058e4845db5331ea19e Mon Sep 17 00:00:00 2001
From: chumv <chumva.forever@gmail.com>
Date: Wed, 15 Jan 2025 17:31:02 +0200
Subject: [PATCH 05/10] Add customBooleanRenderClassProps

---
 .../net/osmand/render/RenderingClass.java     | 112 +++++++++---------
 .../osmand/render/RenderingRulesStorage.java  |  34 ++++--
 .../core/android/MapRendererContext.java      |  12 +-
 .../plus/configmap/HikingRoutesFragment.java  |  26 +++-
 .../plus/configmap/RouteLegendCard.java       |  56 +++++++--
 .../plus/configmap/routes/RClassUtils.java    |  93 ---------------
 .../plus/settings/backend/OsmandSettings.java |  11 ++
 7 files changed, 168 insertions(+), 176 deletions(-)
 delete mode 100644 OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java

diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
index ca94585995e..d5a37520724 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
@@ -1,59 +1,65 @@
 package net.osmand.render;
 
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 
 public class RenderingClass {
-    private final String name;
-    private final String title;
-    private final boolean enable;
-    private String path;
-    private final List<RenderingClass> children = new ArrayList<>();
-
-    public RenderingClass(String name, String title, boolean enable) {
-        this.name = name;
-        this.title = title;
-        this.enable = enable;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public boolean isEnable() {
-        return enable;
-    }
-
-    public String getPath() {
-        return path;
-    }
-
-    public void setPath(String path) {
-        this.path = path;
-    }
-
-    public List<RenderingClass> getChildren() {
-        return children;
-    }
-
-    public void addChild(RenderingClass child) {
-        children.add(child);
-    }
-
-    public RenderingClass findByPath(String path) {
-        if (this.path.equals(path)) {
-            return this;
-        }
-        for (RenderingClass child : children) {
-            RenderingClass result = child.findByPath(path);
-            if (result != null) {
-                return result;
-            }
-        }
-        return null;
-    }
+
+	private final String name;
+	private final String title;
+	private final List<RenderingClass> children = new ArrayList<>();
+	private final boolean enable;
+
+	private String path;
+
+	public RenderingClass(String name, String title, boolean enable) {
+		this.name = name;
+		this.title = title;
+		this.enable = enable;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public String getColorName() {
+		return name.replace(".", "") + "Color";
+	}
+
+	public String getTitle() {
+		return title;
+	}
+
+	public boolean isEnable() {
+		return enable;
+	}
+
+	public String getPath() {
+		return path;
+	}
+
+	public void setPath(String path) {
+		this.path = path;
+	}
+
+	public List<RenderingClass> getChildren() {
+		return children;
+	}
+
+	public void addChild(RenderingClass child) {
+		children.add(child);
+	}
+
+	public RenderingClass findByPath(String path) {
+		if (this.path.equals(path)) {
+			return this;
+		}
+		for (RenderingClass child : children) {
+			RenderingClass result = child.findByPath(path);
+			if (result != null) {
+				return result;
+			}
+		}
+		return null;
+	}
 }
\ No newline at end of file
diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
index f56e7942272..4242073f42a 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
@@ -269,14 +269,15 @@ private void process(RenderingRulesHandler handler, int el) throws XmlPullParser
 	}
 	
 	private class RenderingRulesHandler {
+
 		private final XmlPullParser parser;
 		private int state;
-		Stack<RenderingRule> stack = new Stack<RenderingRule>();
+		private final Stack<RenderingRule> stack = new Stack<RenderingRule>();
 		private final Stack<RenderingClass> renderingClassStack = new Stack<>();
 		private final RenderingRulesStorageResolver resolver;
 		private final boolean addon;
 		private RenderingRulesStorage dependsStorage;
-		
+
 		public RenderingRulesHandler(XmlPullParser parser, RenderingRulesStorageResolver resolver,
 									 boolean addon){
 			this.parser = parser;
@@ -433,23 +434,22 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 				}
 			} else if ("renderer".equals(name)) { //$NON-NLS-1$
 				throw new XmlPullParserException("Rendering style is deprecated and no longer supported.");
-			}else if ("renderingClass".equals(name)) {
-				String className = parser.getAttributeValue(null, "name");
-				String title = parser.getAttributeValue(null, "title");
-				boolean enable = Boolean.parseBoolean(parser.getAttributeValue(null, "enable"));
+			} else if ("renderingClass".equals(name)) {
+				String title = attrsMap.get("title");
+				String className = attrsMap.get("name");
+				boolean enable = Boolean.parseBoolean(attrsMap.get("enable"));
 
-				RenderingClass newClass = new RenderingClass(className, title, enable);
+				RenderingClass renderingClass = new RenderingClass(className, title, enable);
 
 				if (!renderingClassStack.isEmpty()) {
 					RenderingClass parent = renderingClassStack.peek();
-					parent.addChild(newClass);
-					newClass.setPath(parent.getPath() + className);
+					parent.addChild(renderingClass);
+					renderingClass.setPath(parent.getPath() + className);
 				} else {
-					newClass.setPath(className);
+					renderingClass.setPath(className);
 				}
-
-				renderingClasses.put(newClass.getPath(), newClass);
-				renderingClassStack.push(newClass);
+				renderingClasses.put(renderingClass.getPath(), renderingClass);
+				renderingClassStack.push(renderingClass);
 			} else {
 				log.warn("Unknown tag : " + name); //$NON-NLS-1$
 			}
@@ -600,6 +600,14 @@ public String[] getRenderingAssociationNames() {
 		return renderingAssociations.keySet().toArray(new String[0]);
 	}
 
+	public Map<String, RenderingClass> getRenderingClasses() {
+		return renderingClasses;
+	}
+
+	public Map<String, RenderingRule> getRenderingAssociations() {
+		return renderingAssociations;
+	}
+
 	public RenderingRule[] getRules(int state){
 		if(state >= tagValueGlobalRules.length ||  tagValueGlobalRules[state] == null) {
 			return new RenderingRule[0];
diff --git a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java
index 2132c8cc953..8dac0bfbcfd 100644
--- a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java
+++ b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java
@@ -29,7 +29,10 @@
 import net.osmand.plus.render.MapRenderRepositories;
 import net.osmand.plus.render.RendererRegistry;
 import net.osmand.plus.settings.backend.OsmandSettings;
+import net.osmand.plus.settings.backend.preferences.CommonPreference;
 import net.osmand.plus.utils.NativeUtilities;
+import net.osmand.render.RenderingClass;
+import net.osmand.render.RenderingRule;
 import net.osmand.render.RenderingRuleProperty;
 import net.osmand.render.RenderingRuleSearchRequest;
 import net.osmand.render.RenderingRuleStorageProperties;
@@ -325,9 +328,14 @@ protected QStringStringHash getMapStyleSettings() {
 				}
 			}
 		}
-
+		for (Map.Entry<String, RenderingClass> entry : storage.getRenderingClasses().entrySet()) {
+			RenderingClass renderingClass = entry.getValue();
+			String name = renderingClass.getName();
+			CommonPreference<Boolean> preference = settings.getСustomBooleanRenderClassProperty(name, renderingClass.isEnable());
+			properties.put(name, String.valueOf(preference.get()));
+		}
 		QStringStringHash styleSettings = new QStringStringHash();
-		for (Entry<String, String> setting : properties.entrySet()) {
+		for (Map.Entry<String, String> setting : properties.entrySet()) {
 			styleSettings.set(setting.getKey(), setting.getValue());
 		}
 		if (nightMode) {
diff --git a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
index f8483de9fe4..deccb821cc5 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/HikingRoutesFragment.java
@@ -1,8 +1,6 @@
 package net.osmand.plus.configmap;
 
 import static net.osmand.osm.OsmRouteType.HIKING;
-import static net.osmand.plus.configmap.routes.RClassUtils.RClassType.HIKING_OSMC_NODES;
-import static net.osmand.plus.configmap.routes.RClassUtils.getDataClasses;
 
 import android.os.Bundle;
 import android.view.LayoutInflater;
@@ -17,17 +15,19 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentManager;
 
+import net.osmand.plus.OsmandApplication;
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
 import net.osmand.plus.base.BaseOsmAndFragment;
-import net.osmand.plus.configmap.RouteLegendCard.DataClass;
 import net.osmand.plus.configmap.routes.RouteLayersHelper;
 import net.osmand.plus.helpers.AndroidUiHelper;
 import net.osmand.plus.utils.AndroidUtils;
 import net.osmand.plus.utils.UiUtilities;
 import net.osmand.plus.widgets.multistatetoggle.TextToggleButton;
 import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem;
+import net.osmand.render.RenderingClass;
 import net.osmand.render.RenderingRuleProperty;
+import net.osmand.render.RenderingRulesStorage;
 import net.osmand.util.Algorithms;
 
 import java.util.ArrayList;
@@ -37,6 +37,7 @@ public class HikingRoutesFragment extends BaseOsmAndFragment {
 
 	public static final String TAG = HikingRoutesFragment.class.getSimpleName();
 	public static final String NODE_NETWORKS_VALUE = "walkingRoutesOSMCNodes";
+	public static final String OSMC_NODES_KEY = ".route.hiking.osmc_nodes";
 
 	private RouteLayersHelper routeLayersHelper;
 	@Nullable
@@ -59,7 +60,8 @@ private boolean isEnabled() {
 	}
 
 	@Override
-	public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+	public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
+			Bundle savedInstanceState) {
 		updateNightMode();
 		View view = themedInflater.inflate(R.layout.map_route_types_fragment, container, false);
 
@@ -164,7 +166,7 @@ private void setupLegendCard(View view) {
 		String propertyValue = routeLayersHelper.getSelectedHikingRoutesValue();
 
 		if (NODE_NETWORKS_VALUE.equals(propertyValue)) {
-			List<DataClass> dataClasses = getDataClasses(app, HIKING_OSMC_NODES);
+			List<RenderingClass> dataClasses = getDataClasses(app);
 			if (!Algorithms.isEmpty(dataClasses)) {
 				RouteLegendCard card = new RouteLegendCard(requireActivity(), dataClasses, app.getString(R.string.shared_string_legend));
 				View cardView = card.build();
@@ -178,6 +180,20 @@ private void setupLegendCard(View view) {
 		}
 	}
 
+	@NonNull
+	public List<RenderingClass> getDataClasses(@NonNull OsmandApplication app) {
+		List<RenderingClass> renderingClasses = new ArrayList<>();
+
+		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
+		if (renderer != null) {
+			RenderingClass renderingClass = renderer.getRenderingClass(OSMC_NODES_KEY);
+			if (renderingClass != null) {
+				renderingClasses.addAll(renderingClass.getChildren());
+			}
+		}
+		return renderingClasses;
+	}
+
 	private void refreshMap() {
 		MapActivity mapActivity = (MapActivity) getMyActivity();
 		if (mapActivity != null) {
diff --git a/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java b/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
index f2305a22679..937388759cd 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
@@ -1,5 +1,7 @@
 package net.osmand.plus.configmap;
 
+import static net.osmand.render.RenderingRuleStorageProperties.ATTR_COLOR_VALUE;
+
 import android.graphics.drawable.Drawable;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -9,25 +11,33 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
 
 import net.osmand.plus.R;
+import net.osmand.plus.activities.MapActivity;
 import net.osmand.plus.helpers.AndroidUiHelper;
 import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.settings.backend.OsmandSettings;
 import net.osmand.plus.track.fragments.TrackAppearanceFragment;
 import net.osmand.plus.utils.AndroidUtils;
 import net.osmand.plus.utils.UiUtilities;
+import net.osmand.render.RenderingClass;
+import net.osmand.render.RenderingRule;
+import net.osmand.render.RenderingRulesStorage;
 
 import java.util.List;
 
 public class RouteLegendCard extends BaseCard {
+
+
 	private final LayoutInflater themedInflater;
 
-	private final List<DataClass> items;
+	private final List<RenderingClass> items;
 	private final String cardTitle;
 
-	public RouteLegendCard(@NonNull FragmentActivity activity, @NonNull List<DataClass> items, @NonNull String cardTitle) {
+	public RouteLegendCard(@NonNull FragmentActivity activity, @NonNull List<RenderingClass> items,
+			@NonNull String cardTitle) {
 		super(activity, true);
 		this.items = items;
 		this.cardTitle = cardTitle;
@@ -55,7 +65,7 @@ private void setupLegendItems() {
 
 	@NonNull
 	private View createView(int position) {
-		DataClass dataClass = items.get(position);
+		RenderingClass dataClass = items.get(position);
 		View itemView = themedInflater.inflate(R.layout.route_legend_item, null, false);
 		CompoundButton compoundButton = itemView.findViewById(R.id.compound_button);
 		TextView title = itemView.findViewById(R.id.title);
@@ -64,10 +74,14 @@ private View createView(int position) {
 		View divider = itemView.findViewById(R.id.divider_bottom);
 		ImageView icon = itemView.findViewById(R.id.icon);
 
-		Drawable iconDrawable = TrackAppearanceFragment.getTrackIcon(app, null, false, dataClass.color);
-		icon.setImageDrawable(iconDrawable);
+		String colorName = dataClass.getColorName();
+		Integer color = parseColor(app.getRendererRegistry().getCurrentSelectedRenderer(), colorName);
+		if (color != null) {
+			Drawable iconDrawable = TrackAppearanceFragment.getTrackIcon(app, null, false, color);
+			icon.setImageDrawable(iconDrawable);
+		}
 
-		title.setText(dataClass.title());
+		title.setText(dataClass.getTitle());
 
 		compoundButton.setChecked(isClassEnabled(dataClass));
 
@@ -92,14 +106,36 @@ private void setupCardTitle() {
 		title.setText(cardTitle);
 	}
 
-	public boolean isClassEnabled(@NonNull DataClass dataClass) {
+	public boolean isClassEnabled(@NonNull RenderingClass dataClass) {
 		return false;
 	}
 
-	public void onClassSelected(@NonNull DataClass dataClass, boolean checked) {
+	public void onClassSelected(@NonNull RenderingClass dataClass, boolean checked) {
+		settings.getСustomBooleanRenderClassProperty(dataClass.getName(), dataClass.isEnable()).set(checked);
+		refreshMap();
+	}
 
+	private void refreshMap() {
+		if (activity instanceof MapActivity mapActivity) {
+			mapActivity.refreshMapComplete();
+			mapActivity.updateLayers();
+		}
 	}
 
-	public record DataClass(String title, int color) {
+	public static Integer parseColor(@NonNull RenderingRulesStorage routeRender, @Nullable String colorName) {
+		if (colorName == null) {
+			return null;
+		}
+		Integer color = null;
+
+		RenderingRule colorRule = routeRender.getRenderingAttributeRule(colorName);
+		List<RenderingRule> rules = colorRule.getIfElseChildren();
+		for (RenderingRule rule : rules) {
+			int colorValue = rule.getIntPropertyValue(ATTR_COLOR_VALUE);
+			if (colorValue != -1) {
+				color = colorValue;
+			}
+		}
+		return color;
 	}
-}
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java
deleted file mode 100644
index 76d6ec5782e..00000000000
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/RClassUtils.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package net.osmand.plus.configmap.routes;
-
-import static net.osmand.render.RenderingRuleStorageProperties.ATTR_COLOR_VALUE;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import net.osmand.plus.OsmandApplication;
-import net.osmand.plus.configmap.RouteLegendCard;
-import net.osmand.render.RenderingClass;
-import net.osmand.render.RenderingRule;
-import net.osmand.render.RenderingRulesStorage;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class RClassUtils {
-    public enum RClassType {
-        HIKING_OSMC_NODES(".route.hiking.osmc_nodes");
-
-        private final String path;
-
-        RClassType(@NonNull String path) {
-            this.path = path;
-        }
-
-        public String getPath() {
-            return path;
-        }
-    }
-
-    public enum RClassColors {
-        IWN(".iwn", "iwnColor"),
-        NWN(".nwn", "nwnColor"),
-        RWN(".rwn", "rwnColor"),
-        LWN(".lwn", "lwnColor");
-
-        private final String className;
-        private final String colorName;
-
-        RClassColors(String className, String colorName) {
-            this.className = className;
-            this.colorName = colorName;
-        }
-
-        @Nullable
-        public static String getColorName(String className) {
-            for (RClassColors RClassColors : values()) {
-                if (RClassColors.className.equals(className)) {
-                    return RClassColors.colorName;
-                }
-            }
-            return null;
-        }
-    }
-
-    public static List<RouteLegendCard.DataClass> getDataClasses(@NonNull OsmandApplication app,
-                                                                 @NonNull RClassType rClassType){
-        List<RouteLegendCard.DataClass> dataClasses = new ArrayList<>();
-
-        RenderingRulesStorage routeRender = app.getRendererRegistry().getCurrentSelectedRenderer();
-        if (routeRender != null) {
-            RenderingClass rClass = routeRender.getRenderingClass(rClassType.getPath());
-            if (rClass != null) {
-                for (RenderingClass children : rClass.getChildren()) {
-                    String colorName = RClassColors.getColorName(children.getName());
-                    Integer color = parseColor(routeRender, colorName);
-                    if (color != null) {
-                        dataClasses.add(new RouteLegendCard.DataClass(children.getTitle(), color));
-                    }
-                }
-            }
-        }
-        return dataClasses;
-    }
-
-    public static Integer parseColor(@NonNull RenderingRulesStorage routeRender, @Nullable String colorName) {
-        if (colorName == null) {
-            return null;
-        }
-        Integer color = null;
-
-        RenderingRule colorRule = routeRender.getRenderingAttributeRule(colorName);
-        List<RenderingRule> rules = colorRule.getIfElseChildren();
-        for (RenderingRule rule : rules) {
-            int colorValue = rule.getIntPropertyValue(ATTR_COLOR_VALUE);
-            if (colorValue != -1) {
-                color = colorValue;
-            }
-        }
-        return color;
-    }
-}
diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java
index 4ee64f2a873..1258b94a83f 100644
--- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java
+++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java
@@ -143,6 +143,7 @@ public class OsmandSettings {
 	private final Map<String, CommonPreference<String>> customRendersProps = new LinkedHashMap<>();
 	private final Map<String, CommonPreference<Boolean>> customBooleanRoutingProps = new LinkedHashMap<>();
 	private final Map<String, CommonPreference<Boolean>> customBooleanRendersProps = new LinkedHashMap<>();
+	private final Map<String, CommonPreference<Boolean>> customBooleanRenderClassProps = new LinkedHashMap<>();
 
 	private final ImpassableRoadsStorage impassableRoadsStorage = new ImpassableRoadsStorage(this);
 	private final IntermediatePointsStorage intermediatePointsStorage = new IntermediatePointsStorage(this);
@@ -3118,6 +3119,16 @@ public CommonPreference<Boolean> getCustomRenderBooleanProperty(@NonNull String
 		return customBooleanRendersProps.get(attrName);
 	}
 
+	@NonNull
+	public CommonPreference<Boolean> getСustomBooleanRenderClassProperty(@NonNull String className, boolean defaultValue) {
+		if (!customBooleanRenderClassProps.containsKey(className)) {
+			CommonPreference<Boolean> preference = new BooleanPreference(this, className, defaultValue).makeProfile();
+			customBooleanRenderClassProps.put(className, preference);
+			return preference;
+		}
+		return customBooleanRenderClassProps.get(className);
+	}
+
 	@NonNull
 	public CommonPreference<Boolean> registerCustomRenderBooleanProperty(@NonNull String attrName, boolean defaultValue) {
 		CommonPreference<Boolean> preference = new BooleanPreference(this, RENDERER_PREFERENCE_PREFIX + attrName, defaultValue).makeProfile();

From 56d1c1eeaef445cdb9087c681fbc4aedc2e1b1a3 Mon Sep 17 00:00:00 2001
From: chumv <chumva.forever@gmail.com>
Date: Tue, 28 Jan 2025 16:39:53 +0200
Subject: [PATCH 06/10] Route legend [WIP]

---
 .../net/osmand/render/RenderingClass.java     |  50 +------
 .../osmand/render/RenderingRulesStorage.java  |  33 +---
 OsmAnd/res/layout/rendering_classes_card.xml  |  28 ++++
 OsmAnd/res/layout/route_legend_card.xml       |  45 ------
 OsmAnd/res/layout/route_legend_item.xml       |  80 ----------
 .../core/android/MapRendererContext.java      |   6 +-
 .../plus/configmap/ConfigureMapMenu.java      |   8 +-
 .../plus/configmap/ConfigureMapUtils.java     |  49 +++++-
 .../plus/configmap/RouteLegendCard.java       | 141 ------------------
 .../routes/AlpineHikingScaleFragment.java     |  25 +++-
 .../routes/CustomRoutesFragment.java          |  80 ++++++++++
 .../configmap/routes/CycleRoutesFragment.java |  21 ++-
 .../routes/HikingRoutesFragment.java          |  21 ++-
 .../configmap/routes/MapRoutesFragment.java   |  51 +++++--
 .../configmap/routes/MtbRoutesFragment.java   |  24 ++-
 .../routes/RenderingClassesCard.java          |  92 ++++++++++++
 .../configmap/routes/RouteLayersHelper.java   |  11 ++
 .../plus/settings/backend/OsmandSettings.java |  20 +--
 18 files changed, 397 insertions(+), 388 deletions(-)
 create mode 100644 OsmAnd/res/layout/rendering_classes_card.xml
 delete mode 100644 OsmAnd/res/layout/route_legend_card.xml
 delete mode 100644 OsmAnd/res/layout/route_legend_item.xml
 delete mode 100644 OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
 create mode 100644 OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java
 create mode 100644 OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java

diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
index d5a37520724..ee995b45af2 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
@@ -1,65 +1,31 @@
 package net.osmand.render;
 
-import java.util.ArrayList;
-import java.util.List;
-
 public class RenderingClass {
 
 	private final String name;
 	private final String title;
-	private final List<RenderingClass> children = new ArrayList<>();
-	private final boolean enable;
-
-	private String path;
+	private final boolean enabledByDefault;
 
-	public RenderingClass(String name, String title, boolean enable) {
+	public RenderingClass(String name, String title, boolean enabledByDefault) {
 		this.name = name;
 		this.title = title;
-		this.enable = enable;
+		this.enabledByDefault = enabledByDefault;
 	}
 
 	public String getName() {
 		return name;
 	}
 
-	public String getColorName() {
-		return name.replace(".", "") + "Color";
-	}
-
 	public String getTitle() {
 		return title;
 	}
 
-	public boolean isEnable() {
-		return enable;
+	public boolean isEnabledByDefault() {
+		return enabledByDefault;
 	}
 
-	public String getPath() {
-		return path;
-	}
-
-	public void setPath(String path) {
-		this.path = path;
-	}
-
-	public List<RenderingClass> getChildren() {
-		return children;
-	}
-
-	public void addChild(RenderingClass child) {
-		children.add(child);
-	}
-
-	public RenderingClass findByPath(String path) {
-		if (this.path.equals(path)) {
-			return this;
-		}
-		for (RenderingClass child : children) {
-			RenderingClass result = child.findByPath(path);
-			if (result != null) {
-				return result;
-			}
-		}
-		return null;
+	@Override
+	public String toString() {
+		return name;
 	}
 }
\ No newline at end of file
diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
index 90ab524b8e8..dda11e451ca 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
@@ -100,6 +100,7 @@ public RenderingRulesStorage copy() {
 		}
 		storage.renderingAttributes.putAll(renderingAttributes);
 		storage.renderingAssociations.putAll(renderingAssociations);
+		storage.renderingClasses.putAll(renderingClasses);
 		return storage;
 	}
 
@@ -269,15 +270,13 @@ private void process(RenderingRulesHandler handler, int el) throws XmlPullParser
 	}
 	
 	private class RenderingRulesHandler {
-
 		private final XmlPullParser parser;
 		private int state;
-		private final Stack<RenderingRule> stack = new Stack<RenderingRule>();
-		private final Stack<RenderingClass> renderingClassStack = new Stack<>();
+		Stack<RenderingRule> stack = new Stack<RenderingRule>();
 		private final RenderingRulesStorageResolver resolver;
 		private final boolean addon;
 		private RenderingRulesStorage dependsStorage;
-
+		
 		public RenderingRulesHandler(XmlPullParser parser, RenderingRulesStorageResolver resolver,
 									 boolean addon){
 			this.parser = parser;
@@ -438,18 +437,7 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 				String title = attrsMap.get("title");
 				String className = attrsMap.get("name");
 				boolean enable = Boolean.parseBoolean(attrsMap.get("enable"));
-
-				RenderingClass renderingClass = new RenderingClass(className, title, enable);
-
-				if (!renderingClassStack.isEmpty()) {
-					RenderingClass parent = renderingClassStack.peek();
-					parent.addChild(renderingClass);
-					renderingClass.setPath(parent.getPath() + className);
-				} else {
-					renderingClass.setPath(className);
-				}
-				renderingClasses.put(renderingClass.getPath(), renderingClass);
-				renderingClassStack.push(renderingClass);
+				renderingClasses.put(className, new RenderingClass(className, title, enable));
 			} else {
 				log.warn("Unknown tag : " + name); //$NON-NLS-1$
 			}
@@ -477,7 +465,8 @@ private Map<String, String> parseAttributes(XmlPullParser parser, Map<String, St
 				String vl = parser.getAttributeValue(i);
 				if (vl != null && vl.startsWith("$")) {
 					String cv = vl.substring(1);
-					if (!renderingConstants.containsKey(cv) && !renderingAttributes.containsKey(cv) && !renderingAssociations.containsKey(cv)) {
+					if (!renderingConstants.containsKey(cv) && !renderingAttributes.containsKey(cv)
+							&& !renderingAssociations.containsKey(cv) && !renderingClasses.containsKey(cv)) {
 						throw new IllegalStateException("Rendering constant or attribute '" + cv + "' was not specified.");
 					}
 					if (renderingConstants.containsKey(cv)) {
@@ -502,8 +491,6 @@ public void endElement(String name) throws XmlPullParserException {
 				stack.pop();
 			} else if ("renderingAssociation".equals(name)) { //$NON-NLS-1$
 				stack.pop();
-			} else if ("renderingClass".equals(name)) {
-				renderingClassStack.pop();
 			}
 		}
 	}
@@ -581,8 +568,8 @@ public RenderingRule getRenderingAttributeRule(String attribute) {
 		return renderingAttributes.get(attribute);
 	}
 
-	public RenderingClass getRenderingClass(String path) {
-		return renderingClasses.get(path);
+	public RenderingClass getRenderingClass(String name) {
+		return renderingClasses.get(name);
 	}
 	
 	public String[] getRenderingAttributeNames() {
@@ -604,10 +591,6 @@ public Map<String, RenderingClass> getRenderingClasses() {
 		return renderingClasses;
 	}
 
-	public Map<String, RenderingRule> getRenderingAssociations() {
-		return renderingAssociations;
-	}
-
 	public RenderingRule[] getRules(int state){
 		if(state >= tagValueGlobalRules.length ||  tagValueGlobalRules[state] == null) {
 			return new RenderingRule[0];
diff --git a/OsmAnd/res/layout/rendering_classes_card.xml b/OsmAnd/res/layout/rendering_classes_card.xml
new file mode 100644
index 00000000000..ee033b2031f
--- /dev/null
+++ b/OsmAnd/res/layout/rendering_classes_card.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	xmlns:tools="http://schemas.android.com/tools"
+	android:id="@+id/rendering_classes_card"
+	android:layout_width="match_parent"
+	android:layout_height="wrap_content"
+	android:background="?attr/list_background_color"
+	android:orientation="vertical">
+
+	<net.osmand.plus.widgets.TextViewEx
+		android:id="@+id/header"
+		style="@style/TitleStyle.Medium"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:layout_marginHorizontal="@dimen/content_padding"
+		android:layout_marginVertical="@dimen/content_padding_small"
+		android:textColor="?android:textColorPrimary"
+		tools:text="@string/mtb_scale" />
+
+	<LinearLayout
+		android:id="@+id/container"
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:orientation="vertical">
+
+	</LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/OsmAnd/res/layout/route_legend_card.xml b/OsmAnd/res/layout/route_legend_card.xml
deleted file mode 100644
index 56cf2023571..00000000000
--- a/OsmAnd/res/layout/route_legend_card.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-	xmlns:tools="http://schemas.android.com/tools"
-	android:id="@+id/route_legend_card"
-	android:layout_width="match_parent"
-	android:layout_height="match_parent"
-	android:orientation="vertical">
-
-	<LinearLayout
-		android:id="@+id/properties_container"
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:background="?attr/list_background_color"
-		android:orientation="vertical">
-
-		<net.osmand.plus.widgets.TextViewEx
-			android:id="@+id/card_title"
-			style="@style/TitleStyle.Medium"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:layout_marginHorizontal="@dimen/content_padding"
-			android:layout_marginVertical="@dimen/content_padding_small"
-			android:textColor="?android:textColorPrimary"
-			tools:text="Legend" />
-
-		<LinearLayout
-			android:id="@+id/classification_properties"
-			android:layout_width="match_parent"
-			android:layout_height="wrap_content"
-			android:orientation="vertical">
-
-		</LinearLayout>
-
-	</LinearLayout>
-
-	<LinearLayout
-		android:id="@+id/main_container"
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:background="?attr/list_background_color"
-		android:orientation="vertical">
-
-	</LinearLayout>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/OsmAnd/res/layout/route_legend_item.xml b/OsmAnd/res/layout/route_legend_item.xml
deleted file mode 100644
index 3083179037e..00000000000
--- a/OsmAnd/res/layout/route_legend_item.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-	xmlns:tools="http://schemas.android.com/tools"
-	android:id="@+id/main_view"
-	android:layout_width="match_parent"
-	android:layout_height="wrap_content"
-	android:background="?attr/selectableItemBackground"
-	android:descendantFocusability="blocksDescendants"
-	android:gravity="center"
-	android:orientation="horizontal">
-
-	<ImageView
-		android:id="@+id/icon"
-		android:layout_width="@dimen/standard_icon_size"
-		android:layout_height="@dimen/standard_icon_size"
-		android:layout_marginStart="@dimen/content_padding"
-		android:layout_marginEnd="@dimen/favorites_icon_right_margin"
-		android:layout_gravity="center_vertical"
-		tools:src="@drawable/ic_action_track_line_bold_color"
-		tools:tint="?attr/default_icon_color" />
-
-	<LinearLayout
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:orientation="vertical">
-
-		<LinearLayout
-			android:layout_width="match_parent"
-			android:layout_height="@dimen/setting_profile_item_height"
-			android:orientation="horizontal">
-
-			<LinearLayout
-				android:layout_width="0dp"
-				android:layout_height="match_parent"
-				android:layout_weight="1"
-				android:gravity="center"
-				android:orientation="vertical">
-
-				<TextView
-					android:id="@+id/title"
-					android:layout_width="match_parent"
-					android:layout_height="wrap_content"
-					android:ellipsize="end"
-					android:maxLines="2"
-					android:textColor="?android:textColorPrimary"
-					android:textSize="@dimen/default_list_text_size"
-					tools:text="Title" />
-
-				<TextView
-					android:id="@+id/description"
-					android:layout_width="match_parent"
-					android:layout_height="wrap_content"
-					android:ellipsize="end"
-					android:maxLines="1"
-					android:textColor="?android:textColorSecondary"
-					android:textSize="@dimen/default_desc_text_size"
-					tools:text="Description" />
-
-			</LinearLayout>
-
-			<androidx.appcompat.widget.SwitchCompat
-				android:id="@+id/compound_button"
-				android:layout_width="wrap_content"
-				android:layout_height="match_parent"
-				android:layout_gravity="center_vertical|end"
-				android:background="@null"
-				android:focusableInTouchMode="true"
-				android:padding="@dimen/content_padding" />
-
-		</LinearLayout>
-
-		<View
-			android:id="@+id/divider_bottom"
-			android:layout_width="match_parent"
-			android:layout_height="1dp"
-			android:background="?attr/list_divider" />
-
-	</LinearLayout>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java
index 8dac0bfbcfd..f1b1597a47d 100644
--- a/OsmAnd/src/net/osmand/core/android/MapRendererContext.java
+++ b/OsmAnd/src/net/osmand/core/android/MapRendererContext.java
@@ -320,7 +320,7 @@ protected QStringStringHash getMapStyleSettings() {
 		for (RenderingRuleProperty property : storage.PROPS.getCustomRules()) {
 			String attrName = property.getAttrName();
 			if (property.isBoolean()) {
-				properties.put(attrName, settings.getRenderBooleanPropertyValue(attrName) + "");
+				properties.put(attrName, String.valueOf(settings.getRenderBooleanPropertyValue(attrName)));
 			} else {
 				String value = settings.getRenderPropertyValue(attrName);
 				if (!Algorithms.isEmpty(value)) {
@@ -330,8 +330,10 @@ protected QStringStringHash getMapStyleSettings() {
 		}
 		for (Map.Entry<String, RenderingClass> entry : storage.getRenderingClasses().entrySet()) {
 			RenderingClass renderingClass = entry.getValue();
+
 			String name = renderingClass.getName();
-			CommonPreference<Boolean> preference = settings.getСustomBooleanRenderClassProperty(name, renderingClass.isEnable());
+			boolean enabled = renderingClass.isEnabledByDefault();
+			CommonPreference<Boolean> preference = settings.getСustomBooleanRenderClassProperty(name, enabled);
 			properties.put(name, String.valueOf(preference.get()));
 		}
 		QStringStringHash styleSettings = new QStringStringHash();
diff --git a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapMenu.java b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapMenu.java
index dcdbee5108f..d1775d24994 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapMenu.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapMenu.java
@@ -59,6 +59,7 @@
 import net.osmand.plus.widgets.ctxmenu.callback.OnDataChangeUiAdapter;
 import net.osmand.plus.widgets.ctxmenu.callback.OnRowItemClick;
 import net.osmand.plus.widgets.ctxmenu.data.ContextMenuItem;
+import net.osmand.render.RenderingClass;
 import net.osmand.render.RenderingRuleProperty;
 import net.osmand.util.Algorithms;
 import net.osmand.util.SunriseSunset;
@@ -71,6 +72,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 public class ConfigureMapMenu {
 
@@ -104,10 +106,12 @@ public ContextMenuAdapter createListAdapter(@NonNull MapActivity mapActivity) {
 
 		List<RenderingRuleProperty> customRules = ConfigureMapUtils.getCustomRules(app,
 				UI_CATEGORY_HIDDEN, RENDERING_CATEGORY_TRANSPORT);
+
 		createLayersItems(customRules, adapter, mapActivity, nightMode);
 		PluginsHelper.registerConfigureMapCategory(adapter, mapActivity, customRules);
 		createRouteAttributeItems(customRules, adapter, mapActivity, nightMode);
 		createRenderingAttributeItems(customRules, adapter, mapActivity, nightMode);
+
 		return adapter;
 	}
 
@@ -210,9 +214,7 @@ private void createLayersItems(@NonNull List<RenderingRuleProperty> customRules,
 	}
 
 	private void createRouteAttributeItems(@NonNull List<RenderingRuleProperty> customRules,
-	                                       @NonNull ContextMenuAdapter adapter,
-	                                       @NonNull MapActivity activity,
-	                                       boolean nightMode) {
+			@NonNull ContextMenuAdapter adapter, @NonNull MapActivity activity, boolean nightMode) {
 		adapter.addItem(new ContextMenuItem(ROUTES_CATEGORY_ID)
 				.setCategory(true)
 				.setTitleId(R.string.rendering_category_routes, activity)
diff --git a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
index 9ce1a6a0562..f82ac50856d 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
@@ -4,6 +4,8 @@
 import static net.osmand.plus.dialogs.DetailsBottomSheet.STREET_LIGHTING_NIGHT;
 import static net.osmand.plus.settings.backend.OsmandSettings.RENDERER_PREFERENCE_PREFIX;
 
+import android.util.Pair;
+
 import androidx.annotation.DrawableRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -15,21 +17,19 @@
 import net.osmand.plus.settings.backend.OsmandSettings;
 import net.osmand.plus.settings.backend.preferences.CommonPreference;
 import net.osmand.plus.utils.AndroidUtils;
+import net.osmand.render.RenderingClass;
 import net.osmand.render.RenderingRuleProperty;
 import net.osmand.render.RenderingRulesStorage;
 import net.osmand.util.Algorithms;
 
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
+import java.util.*;
 
 public class ConfigureMapUtils {
 
 	public static final String[] MAP_LANGUAGES_IDS = {"", "en", "af", "als", "ar", "az", "be", "ber", "bg", "bn", "bpy", "br", "bs", "ca", "ceb", "ckb", "cs", "cy", "da", "de", "el", "eo", "es", "et", "eu", "fa", "fi", "fr", "fy", "ga", "gl", "he", "hi", "hsb", "hr", "ht", "hu", "hy", "id", "is", "it", "ja", "ka", "kab", "kk", "kn", "ko", "ku", "la", "lb", "lo", "lt", "lv", "mk", "ml", "mr", "ms", "nds", "new", "nl", "nn", "no", "nv", "oc", "os", "pl", "pms", "pt", "ro", "ru", "sat", "sc", "sh", "sk", "sl", "sq", "sr", "sr-latn", "sv", "sw", "ta", "te", "th", "tl", "tr", "uk", "vi", "vo", "zh", "zh-Hans", "zh-Hant"};
 
+	public static final String ROUTE_CLASS_PREFIX = ".route.";
+
 	@NonNull
 	public static Map<String, String> getSorterMapLanguages(@NonNull OsmandApplication app) {
 		Map<String, String> mapLanguages = new HashMap<>();
@@ -71,7 +71,8 @@ public static RenderingRuleProperty getPropertyForAttr(@NonNull OsmandApplicatio
 	}
 
 	@Nullable
-	public static RenderingRuleProperty getPropertyForAttr(@NonNull List<RenderingRuleProperty> customRules, @NonNull String attrName) {
+	public static RenderingRuleProperty getPropertyForAttr(
+			@NonNull List<RenderingRuleProperty> customRules, @NonNull String attrName) {
 		for (RenderingRuleProperty property : customRules) {
 			if (Algorithms.stringsEqual(property.getAttrName(), attrName)) {
 				return property;
@@ -103,7 +104,39 @@ public static List<RenderingRuleProperty> getCustomRules(@NonNull OsmandApplicat
 		return customRules;
 	}
 
-	protected static String[] getRenderingPropertyPossibleValues(OsmandApplication app, RenderingRuleProperty p) {
+	@NonNull
+	public static Map<String, RenderingClass> getRenderingClasses(@NonNull OsmandApplication app) {
+		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
+		if (renderer == null) {
+			return new LinkedHashMap<>();
+		}
+		return renderer.getRenderingClasses();
+	}
+
+	@NonNull
+	public static Pair<RenderingClass, List<RenderingClass>> getRenderingClassesForKey(
+			@NonNull OsmandApplication app, @NonNull String attrName) {
+		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
+		if (renderer == null) {
+			return null;
+		}
+		String key = ROUTE_CLASS_PREFIX + attrName.replace("show", "");
+		RenderingClass mainClass = renderer.getRenderingClass(key);
+		if (mainClass != null) {
+			List<RenderingClass> list = new ArrayList<>();
+			for (Map.Entry<String, RenderingClass> entry : renderer.getRenderingClasses().entrySet()) {
+				RenderingClass renderingClass = entry.getValue();
+				if (renderingClass.getName().startsWith(key + ".")) {
+					list.add(renderingClass);
+				}
+			}
+			return Pair.create(mainClass, list);
+		}
+		return null;
+	}
+
+	protected static String[] getRenderingPropertyPossibleValues(OsmandApplication app,
+			RenderingRuleProperty p) {
 		String[] possibleValuesString = new String[p.getPossibleValues().length + 1];
 		possibleValuesString[0] = AndroidUtils.getRenderingStringPropertyValue(app, p.getDefaultValueDescription());
 
diff --git a/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java b/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
deleted file mode 100644
index 937388759cd..00000000000
--- a/OsmAnd/src/net/osmand/plus/configmap/RouteLegendCard.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package net.osmand.plus.configmap;
-
-import static net.osmand.render.RenderingRuleStorageProperties.ATTR_COLOR_VALUE;
-
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.FragmentActivity;
-
-import net.osmand.plus.R;
-import net.osmand.plus.activities.MapActivity;
-import net.osmand.plus.helpers.AndroidUiHelper;
-import net.osmand.plus.routepreparationmenu.cards.BaseCard;
-import net.osmand.plus.settings.backend.OsmandSettings;
-import net.osmand.plus.track.fragments.TrackAppearanceFragment;
-import net.osmand.plus.utils.AndroidUtils;
-import net.osmand.plus.utils.UiUtilities;
-import net.osmand.render.RenderingClass;
-import net.osmand.render.RenderingRule;
-import net.osmand.render.RenderingRulesStorage;
-
-import java.util.List;
-
-public class RouteLegendCard extends BaseCard {
-
-
-	private final LayoutInflater themedInflater;
-
-	private final List<RenderingClass> items;
-	private final String cardTitle;
-
-	public RouteLegendCard(@NonNull FragmentActivity activity, @NonNull List<RenderingClass> items,
-			@NonNull String cardTitle) {
-		super(activity, true);
-		this.items = items;
-		this.cardTitle = cardTitle;
-		themedInflater = UiUtilities.getInflater(activity, nightMode);
-	}
-
-	@Override
-	public int getCardLayoutId() {
-		return R.layout.route_legend_card;
-	}
-
-	@Override
-	protected void updateContent() {
-		setupCardTitle();
-		setupLegendItems();
-	}
-
-	private void setupLegendItems() {
-		ViewGroup mainContainer = view.findViewById(R.id.main_container);
-		mainContainer.removeAllViews();
-		for (int i = 0; i < items.size(); i++) {
-			mainContainer.addView(createView(i));
-		}
-	}
-
-	@NonNull
-	private View createView(int position) {
-		RenderingClass dataClass = items.get(position);
-		View itemView = themedInflater.inflate(R.layout.route_legend_item, null, false);
-		CompoundButton compoundButton = itemView.findViewById(R.id.compound_button);
-		TextView title = itemView.findViewById(R.id.title);
-		TextView description = itemView.findViewById(R.id.description);
-		AndroidUiHelper.updateVisibility(description, false);
-		View divider = itemView.findViewById(R.id.divider_bottom);
-		ImageView icon = itemView.findViewById(R.id.icon);
-
-		String colorName = dataClass.getColorName();
-		Integer color = parseColor(app.getRendererRegistry().getCurrentSelectedRenderer(), colorName);
-		if (color != null) {
-			Drawable iconDrawable = TrackAppearanceFragment.getTrackIcon(app, null, false, color);
-			icon.setImageDrawable(iconDrawable);
-		}
-
-		title.setText(dataClass.getTitle());
-
-		compoundButton.setChecked(isClassEnabled(dataClass));
-
-		AndroidUiHelper.updateVisibility(divider, position != items.size() - 1);
-		itemView.setOnClickListener(view -> {
-			compoundButton.performClick();
-			onClassSelected(dataClass, compoundButton.isChecked());
-		});
-
-		compoundButton.setFocusable(false);
-		compoundButton.setClickable(false);
-
-		OsmandSettings settings = app.getSettings();
-		Drawable background = UiUtilities.getColoredSelectableDrawable(app, settings.getApplicationMode().getProfileColor(nightMode), 0.3f);
-		AndroidUtils.setBackground(itemView, background);
-
-		return itemView;
-	}
-
-	private void setupCardTitle() {
-		TextView title = view.findViewById(R.id.card_title);
-		title.setText(cardTitle);
-	}
-
-	public boolean isClassEnabled(@NonNull RenderingClass dataClass) {
-		return false;
-	}
-
-	public void onClassSelected(@NonNull RenderingClass dataClass, boolean checked) {
-		settings.getСustomBooleanRenderClassProperty(dataClass.getName(), dataClass.isEnable()).set(checked);
-		refreshMap();
-	}
-
-	private void refreshMap() {
-		if (activity instanceof MapActivity mapActivity) {
-			mapActivity.refreshMapComplete();
-			mapActivity.updateLayers();
-		}
-	}
-
-	public static Integer parseColor(@NonNull RenderingRulesStorage routeRender, @Nullable String colorName) {
-		if (colorName == null) {
-			return null;
-		}
-		Integer color = null;
-
-		RenderingRule colorRule = routeRender.getRenderingAttributeRule(colorName);
-		List<RenderingRule> rules = colorRule.getIfElseChildren();
-		for (RenderingRule rule : rules) {
-			int colorValue = rule.getIntPropertyValue(ATTR_COLOR_VALUE);
-			if (colorValue != -1) {
-				color = colorValue;
-			}
-		}
-		return color;
-	}
-}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java
index 3919f0d35cb..e5e61feef62 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java
@@ -1,5 +1,7 @@
 package net.osmand.plus.configmap.routes;
 
+import static net.osmand.osm.OsmRouteType.ALPINE;
+
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -8,6 +10,7 @@
 
 import net.osmand.plus.R;
 import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 
 public class AlpineHikingScaleFragment extends MapRoutesFragment {
@@ -24,6 +27,12 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleAlpineHikingRoutes();
 	}
 
+	@NonNull
+	@Override
+	protected String getSelectedAttrName() {
+		return ALPINE.getRenderingPropertyAttr();
+	}
+
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -34,21 +43,25 @@ protected void setupHeader(@NonNull View view) {
 		title.setText(R.string.rendering_attr_alpineHiking_name);
 
 		TextView description = container.findViewById(R.id.description);
-		description.setText(AlpineHikingCard.getDifficultyClassificationDescription(app));
+		description.setText(routeLayersHelper.getRoutesTypeDescription(getSelectedAttrName()));
 		AndroidUiHelper.updateVisibility(description, enabled);
 
 		int selectedColor = settings.getApplicationMode().getProfileColor(nightMode);
 		int disabledColor = AndroidUtils.getColorFromAttr(app, R.attr.default_icon_color);
 		ImageView icon = container.findViewById(R.id.icon);
-		icon.setImageDrawable(getPaintedContentIcon(R.drawable.ic_action_trekking_dark, enabled ? selectedColor : disabledColor));
+		icon.setImageDrawable(getPaintedContentIcon(RouteUtils.getIconIdForAttr(getSelectedAttrName()),
+				enabled ? selectedColor : disabledColor));
 	}
 
 	@Override
-	protected void setupCards(@NonNull View view) {
-		super.setupCards(view);
+	protected void createCards(@NonNull View view) {
+		super.createCards(view);
 
-		cardsContainer.addView(createDivider(cardsContainer, true, true));
 		addCard(new AlpineHikingCard(getMapActivity()));
-		cardsContainer.addView(createDivider(cardsContainer, false, true));
+
+		BaseCard card = createRenderingClassCard(getSelectedAttrName());
+		if (card != null) {
+			addCard(card);
+		}
 	}
 }
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java
new file mode 100644
index 00000000000..7650bd3b3a4
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java
@@ -0,0 +1,80 @@
+package net.osmand.plus.configmap.routes;
+
+import android.os.Bundle;
+import android.util.Pair;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import net.osmand.plus.R;
+import net.osmand.plus.configmap.ConfigureMapUtils;
+import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.routepreparationmenu.cards.BaseCard;
+import net.osmand.plus.utils.AndroidUtils;
+import net.osmand.render.RenderingClass;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class CustomRoutesFragment extends MapRoutesFragment {
+
+	public static final String TAG = CustomRoutesFragment.class.getSimpleName();
+
+	private String attrName;
+
+	@Override
+	protected boolean isEnabled() {
+		return routeLayersHelper.isRoutesTypeEnabled(attrName);
+	}
+
+	@Override
+	protected void toggleMainPreference(@NonNull View view) {
+		routeLayersHelper.toggleRoutesType(attrName);
+	}
+
+	@NonNull
+	@Override
+	protected @NotNull String getSelectedAttrName() {
+		return attrName;
+	}
+
+	@Override
+	public void onCreate(@Nullable Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		attrName = app.getRouteLayersHelper().getSelectedAttrName();
+	}
+
+	protected void setupHeader(@NonNull View view) {
+		super.setupHeader(view);
+
+		boolean enabled = isEnabled();
+		View container = view.findViewById(R.id.preference_container);
+
+		TextView title = container.findViewById(R.id.title);
+		title.setText(routeLayersHelper.getRoutesTypeName(routeLayersHelper.getSelectedAttrName()));
+
+		int selectedColor = settings.getApplicationMode().getProfileColor(nightMode);
+		int disabledColor = AndroidUtils.getColorFromAttr(app, R.attr.default_icon_color);
+		ImageView icon = container.findViewById(R.id.icon);
+		int iconId = RouteUtils.getIconIdForAttr(attrName);
+		if (iconId > 0) {
+			icon.setImageDrawable(getPaintedContentIcon(iconId, enabled ? selectedColor : disabledColor));
+		}
+
+		AndroidUiHelper.updateVisibility(container.findViewById(R.id.description), false);
+	}
+
+	@Override
+	protected void createCards(@NonNull View view) {
+		super.createCards(view);
+
+		BaseCard card = createRenderingClassCard(getSelectedAttrName());
+		if (card != null) {
+			addCard(card);
+		}
+	}
+}
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java
index deba0d94df5..7b5400d088a 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java
@@ -9,9 +9,12 @@
 import androidx.annotation.NonNull;
 
 import net.osmand.plus.R;
+import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 import net.osmand.plus.utils.ColorUtilities;
 
+import org.jetbrains.annotations.NotNull;
+
 public class CycleRoutesFragment extends MapRoutesFragment {
 
 	public static final String TAG = CycleRoutesFragment.class.getSimpleName();
@@ -26,6 +29,12 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleCycleRoutes();
 	}
 
+	@NonNull
+	@Override
+	protected String getSelectedAttrName() {
+		return BICYCLE.getRenderingPropertyAttr();
+	}
+
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -46,11 +55,13 @@ protected void setupHeader(@NonNull View view) {
 	}
 
 	@Override
-	protected void setupCards(@NonNull View view) {
-		super.setupCards(view);
-
-		cardsContainer.addView(createDivider(cardsContainer, true, true));
+	protected void createCards(@NonNull View view) {
+		super.createCards(view);
 		addCard(new CycleRouteTypesCard(getMapActivity()));
-		cardsContainer.addView(createDivider(cardsContainer, false, true));
+
+		BaseCard card = createRenderingClassCard(getSelectedAttrName());
+		if (card != null) {
+			addCard(card);
+		}
 	}
 }
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java
index 9a9a04d5f68..655c2716417 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java
@@ -1,5 +1,7 @@
 package net.osmand.plus.configmap.routes;
 
+import static net.osmand.osm.OsmRouteType.HIKING;
+
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -7,6 +9,7 @@
 import androidx.annotation.NonNull;
 
 import net.osmand.plus.R;
+import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 
 public class HikingRoutesFragment extends MapRoutesFragment {
@@ -25,6 +28,12 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleHikingRoutes();
 	}
 
+	@NonNull
+	@Override
+	protected String getSelectedAttrName() {
+		return HIKING.getRenderingPropertyAttr();
+	}
+
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -44,11 +53,13 @@ protected void setupHeader(@NonNull View view) {
 	}
 
 	@Override
-	protected void setupCards(@NonNull View view) {
-		super.setupCards(view);
-
-		cardsContainer.addView(createDivider(cardsContainer, true, true));
+	protected void createCards(@NonNull View view) {
+		super.createCards(view);
 		addCard(new HikingRoutesCard(getMapActivity()));
-		cardsContainer.addView(createDivider(cardsContainer, false, true));
+
+		BaseCard card = createRenderingClassCard(getSelectedAttrName());
+		if (card != null) {
+			addCard(card);
+		}
 	}
 }
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java
index 90252960fca..ea96e000f14 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java
@@ -6,6 +6,7 @@
 import static net.osmand.osm.OsmRouteType.MTB;
 
 import android.os.Bundle;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -22,11 +23,13 @@
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
 import net.osmand.plus.base.BaseOsmAndFragment;
+import net.osmand.plus.configmap.ConfigureMapUtils;
 import net.osmand.plus.helpers.AndroidUiHelper;
 import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
 import net.osmand.plus.utils.AndroidUtils;
 import net.osmand.plus.utils.UiUtilities;
+import net.osmand.render.RenderingClass;
 import net.osmand.util.Algorithms;
 import net.osmand.util.CollectionUtils;
 
@@ -56,6 +59,9 @@ protected boolean isUsedOnMap() {
 
 	protected abstract void toggleMainPreference(@NonNull View view);
 
+	@NonNull
+	protected abstract String getSelectedAttrName();
+
 	@Override
 	public void onCreate(@Nullable Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
@@ -70,8 +76,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c
 		View view = themedInflater.inflate(R.layout.map_routes_fragment, container, false);
 
 		setupHeader(view);
-		setupCards(view);
-		updateContent();
+		setupContent(view);
 
 		return view;
 	}
@@ -83,7 +88,7 @@ protected void setupHeader(@NonNull View view) {
 		preferenceContainer.setOnClickListener(v -> {
 			toggleMainPreference(view);
 			setupHeader(view);
-			updateContent();
+			setupContent(view);
 			refreshMap();
 		});
 
@@ -106,19 +111,41 @@ protected void setupHeader(@NonNull View view) {
 	protected void addCard(@NonNull BaseCard card) {
 		cards.add(card);
 		card.setListener(this);
-		cardsContainer.addView(card.build(cardsContainer.getContext()));
 	}
 
-	protected void setupCards(@NonNull View view) {
+	protected void createCards(@NonNull View view) {
 		cards.clear();
+	}
+
+	protected void inflateCards(@NonNull View view) {
 		cardsContainer = view.findViewById(R.id.cards_container);
 		cardsContainer.removeAllViews();
+
+		for (int i = 0; i < cards.size(); i++) {
+			BaseCard card = cards.get(i);
+
+			if (i == 0) {
+				cardsContainer.addView(createDivider(cardsContainer, true, true));
+			}
+			cardsContainer.addView(card.build(cardsContainer.getContext()));
+
+			boolean lastItem = i == cards.size() - 1;
+			cardsContainer.addView(createDivider(cardsContainer, !lastItem, true));
+		}
 	}
 
-	protected void updateContent() {
-		for (BaseCard card : cards) {
-			card.update();
+	@Nullable
+	protected BaseCard createRenderingClassCard(@NonNull String attrName) {
+		Pair<RenderingClass, List<RenderingClass>> pair = ConfigureMapUtils.getRenderingClassesForKey(app, attrName);
+		if (pair != null) {
+			addCard(new RenderingClassesCard(getMapActivity(), pair.first, pair.second));
 		}
+		return null;
+	}
+
+	protected void setupContent(@NonNull View view) {
+		createCards(view);
+		inflateCards(view);
 		AndroidUiHelper.updateVisibility(cardsContainer, isEnabled());
 	}
 
@@ -145,6 +172,7 @@ public void onCardPressed(@NonNull BaseCard card) {
 		View view = getView();
 		if (view != null) {
 			setupHeader(view);
+			setupContent(view);
 		}
 	}
 
@@ -167,9 +195,12 @@ protected MapActivity requireMapActivity() {
 	}
 
 	public static boolean shouldShow(@NonNull OsmandApplication app, @NonNull String attrName) {
-		return CollectionUtils.equalsToAny(attrName,
+		boolean defaultScreens = CollectionUtils.equalsToAny(attrName,
 				BICYCLE.getRenderingPropertyAttr(), MTB.getRenderingPropertyAttr(),
 				HIKING.getRenderingPropertyAttr(), ALPINE.getRenderingPropertyAttr());
+
+		Pair<RenderingClass, List<RenderingClass>> pair = ConfigureMapUtils.getRenderingClassesForKey(app, attrName);
+		return defaultScreens || pair != null;
 	}
 
 	@Nullable
@@ -183,7 +214,7 @@ public static String getFragmentName(@Nullable String attrName) {
 		} else if (Algorithms.stringsEqual(ALPINE.getRenderingPropertyAttr(), attrName)) {
 			return AlpineHikingScaleFragment.class.getName();
 		}
-		return null;
+		return CustomRoutesFragment.class.getName();
 	}
 
 	public static void showInstance(@NonNull FragmentActivity activity, @NonNull String attrName) {
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java
index d8506411185..4b203d2d5f9 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java
@@ -1,5 +1,7 @@
 package net.osmand.plus.configmap.routes;
 
+import static net.osmand.osm.OsmRouteType.MTB;
+
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -8,6 +10,7 @@
 
 import net.osmand.plus.R;
 import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 
 public class MtbRoutesFragment extends MapRoutesFragment {
@@ -24,6 +27,13 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleMtbRoutes();
 	}
 
+	@NonNull
+	@Override
+	protected String getSelectedAttrName() {
+		MtbClassification classification = routeLayersHelper.getSelectedMtbClassification();
+		return classification != null ? classification.attrName : MTB.getRenderingPropertyAttr();
+	}
+
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -31,7 +41,7 @@ protected void setupHeader(@NonNull View view) {
 		View container = view.findViewById(R.id.preference_container);
 
 		TextView title = container.findViewById(R.id.title);
-		title.setText(R.string.rendering_attr_showMtbRoutes_name);
+		title.setText(routeLayersHelper.getRoutesTypeName(MTB.getRenderingPropertyAttr()));
 
 		int selectedColor = settings.getApplicationMode().getProfileColor(nightMode);
 		int disabledColor = AndroidUtils.getColorFromAttr(app, R.attr.default_icon_color);
@@ -42,11 +52,13 @@ protected void setupHeader(@NonNull View view) {
 	}
 
 	@Override
-	protected void setupCards(@NonNull View view) {
-		super.setupCards(view);
-
-		cardsContainer.addView(createDivider(cardsContainer, true, true));
+	protected void createCards(@NonNull View view) {
+		super.createCards(view);
 		addCard(new MtbRoutesCard(getMapActivity()));
-		cardsContainer.addView(createDivider(cardsContainer, false, true));
+
+		BaseCard card = createRenderingClassCard(getSelectedAttrName());
+		if (card != null) {
+			addCard(card);
+		}
 	}
 }
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java
new file mode 100644
index 00000000000..c583bafac62
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java
@@ -0,0 +1,92 @@
+package net.osmand.plus.configmap.routes;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import net.osmand.plus.R;
+import net.osmand.plus.activities.MapActivity;
+import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.routepreparationmenu.cards.MapBaseCard;
+import net.osmand.plus.settings.backend.preferences.CommonPreference;
+import net.osmand.plus.utils.AndroidUtils;
+import net.osmand.plus.utils.ColorUtilities;
+import net.osmand.plus.utils.UiUtilities;
+import net.osmand.render.RenderingClass;
+
+import java.util.List;
+
+public class RenderingClassesCard extends MapBaseCard {
+
+	private final RenderingClass mainClass;
+	private final List<RenderingClass> list;
+
+	private ViewGroup container;
+
+	@Override
+	public int getCardLayoutId() {
+		return R.layout.rendering_classes_card;
+	}
+
+	public RenderingClassesCard(@NonNull MapActivity activity, @NonNull RenderingClass mainClass,
+			@NonNull List<RenderingClass> list) {
+		super(activity, true);
+		this.mainClass = mainClass;
+		this.list = list;
+	}
+
+	@Override
+	protected void updateContent() {
+		setupHeader();
+		setupRenderingClasses();
+	}
+
+	private void setupHeader() {
+		TextView header = view.findViewById(R.id.header);
+		header.setText(mainClass.getTitle());
+	}
+
+	private void setupRenderingClasses() {
+		container = view.findViewById(R.id.container);
+		container.removeAllViews();
+
+		for (int i = 0; i < list.size(); i++) {
+			RenderingClass renderingClass = list.get(i);
+			container.addView(createRadioButton(renderingClass, i == list.size() - 1));
+		}
+	}
+
+	@NonNull
+	private View createRadioButton(@NonNull RenderingClass renderingClass, boolean lastItem) {
+		CommonPreference<Boolean> pref = settings.getСustomBooleanRenderClassProperty(renderingClass.getName(), renderingClass.isEnabledByDefault());
+		View view = themedInflater.inflate(R.layout.bottom_sheet_item_with_switch_56dp, container, false);
+
+		ImageView icon = view.findViewById(R.id.icon);
+		AndroidUiHelper.updateVisibility(icon, false);
+
+		TextView title = view.findViewById(R.id.title);
+		title.setText(renderingClass.getTitle());
+
+		CompoundButton button = view.findViewById(R.id.compound_button);
+		UiUtilities.setupCompoundButton(nightMode, ColorUtilities.getActiveColor(app, nightMode), button);
+		button.setChecked(pref.get());
+
+		view.setOnClickListener(v -> {
+			boolean checked = !pref.get();
+			pref.set(checked);
+			button.setChecked(checked);
+			notifyCardPressed();
+		});
+
+		int profileColor = settings.getApplicationMode().getProfileColor(nightMode);
+		Drawable background = UiUtilities.getColoredSelectableDrawable(app, profileColor, 0.3f);
+		AndroidUtils.setBackground(view, background);
+
+		return view;
+	}
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java
index 4870d2c7f84..40045f52109 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java
@@ -195,6 +195,17 @@ public String getSelectedMtbClassificationName(@NonNull Context context) {
 		return "";
 	}
 
+	@NonNull
+	public MtbClassification getSelectedMtbClassification() {
+		String selectedId = getSelectedMtbClassificationId();
+		for (MtbClassification classification : MtbClassification.values()) {
+			if (Objects.equals(classification.attrName, selectedId)) {
+				return classification;
+			}
+		}
+		return null;
+	}
+
 	@Nullable
 	public String getSelectedMtbClassificationId() {
 		return isMtbRoutesEnabled() ? mtbRoutesLastClassification.get() : null;
diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java
index afa85d70da9..f43156d718b 100644
--- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java
+++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java
@@ -3084,16 +3084,6 @@ public CommonPreference<Boolean> getCustomRenderBooleanProperty(@NonNull String
 		return customBooleanRendersProps.get(attrName);
 	}
 
-	@NonNull
-	public CommonPreference<Boolean> getСustomBooleanRenderClassProperty(@NonNull String className, boolean defaultValue) {
-		if (!customBooleanRenderClassProps.containsKey(className)) {
-			CommonPreference<Boolean> preference = new BooleanPreference(this, className, defaultValue).makeProfile();
-			customBooleanRenderClassProps.put(className, preference);
-			return preference;
-		}
-		return customBooleanRenderClassProps.get(className);
-	}
-
 	@NonNull
 	public CommonPreference<Boolean> registerCustomRenderBooleanProperty(@NonNull String attrName, boolean defaultValue) {
 		String id = attrName.startsWith(RENDERER_PREFERENCE_PREFIX) ? attrName : RENDERER_PREFERENCE_PREFIX + attrName;
@@ -3120,6 +3110,16 @@ public CommonPreference<Boolean> getCustomRoutingBooleanProperty(@NonNull String
 		return customBooleanRoutingProps.get(attrName);
 	}
 
+	@NonNull
+	public CommonPreference<Boolean> getСustomBooleanRenderClassProperty(@NonNull String name, boolean defaultValue) {
+		if (!customBooleanRenderClassProps.containsKey(name)) {
+			CommonPreference<Boolean> preference = new BooleanPreference(this, name, defaultValue).makeProfile();
+			customBooleanRenderClassProps.put(name, preference);
+			return preference;
+		}
+		return customBooleanRenderClassProps.get(name);
+	}
+
 	public final OsmandPreference<Boolean> SHOW_TRAVEL = new BooleanPreference(this, "show_travel_routes", false).makeProfile().cache();
 
 	public final CommonPreference<Float> ROUTE_RECALCULATION_DISTANCE = new FloatPreference(this, "routing_recalc_distance", 0.f).makeProfile();

From d8b7afcaf492343466dbe1936fb5282b2484e28d Mon Sep 17 00:00:00 2001
From: chumv <chumva.forever@gmail.com>
Date: Tue, 4 Feb 2025 19:23:26 +0200
Subject: [PATCH 07/10] Remove deprecated renderingAssociation

---
 .../java/net/osmand/render/RenderingRule.java |  3 -
 .../render/RenderingRuleSearchRequest.java    | 10 ---
 .../osmand/render/RenderingRulesStorage.java  | 78 ++++---------------
 3 files changed, 16 insertions(+), 75 deletions(-)

diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRule.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRule.java
index adb59f9c767..2eaae8a9a96 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRule.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRule.java
@@ -64,9 +64,6 @@ public void init(Map<String, String> attributes) {
 						attributesRef = new RenderingRule[attributes.size()];
 					}
 					attributesRef[i] = storage.getRenderingAttributeRule(vl.substring(1));
-					if (attributesRef[i] == null) {
-						attributesRef[i] = storage.getRenderingAssociationRule(vl.substring(1));
-					}
 				} else if (property.isString()) {
 					intProperties[i] = storage.getDictionaryValue(vl);
 				} else {
diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRuleSearchRequest.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRuleSearchRequest.java
index c4c911cc764..3d3168fb96e 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRuleSearchRequest.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRuleSearchRequest.java
@@ -161,16 +161,6 @@ public boolean searchRenderingAttribute(String attribute) {
 		return searchResult;
 	}
 
-	public boolean searchRenderingAssociation(String association) {
-		searchResult = false;
-		RenderingRule rule = storage.getRenderingAssociationRule(association);
-		if (rule == null) {
-			return false;
-		}
-		searchResult = visitRule(rule, true);
-		return searchResult;
-	}
-
 	public boolean search(int state) {
 		return search(state, true);
 	}
diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
index dda11e451ca..d6881497c9a 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
@@ -12,15 +12,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.Map.Entry;
-import java.util.Stack;
 
 import gnu.trove.map.hash.TIntObjectHashMap;
 
@@ -54,7 +47,6 @@ public class RenderingRulesStorage {
 	public TIntObjectHashMap<RenderingRule>[] tagValueGlobalRules = new TIntObjectHashMap[LENGTH_RULES];
 	
 	protected Map<String, RenderingRule> renderingAttributes = new LinkedHashMap<String, RenderingRule>();
-	protected Map<String, RenderingRule> renderingAssociations = new LinkedHashMap<String, RenderingRule>();
 	protected Map<String, RenderingClass> renderingClasses = new LinkedHashMap<String, RenderingClass>();
 	protected Map<String, String> renderingConstants = new LinkedHashMap<String, String>();
 	
@@ -99,7 +91,6 @@ public RenderingRulesStorage copy() {
 			}
 		}
 		storage.renderingAttributes.putAll(renderingAttributes);
-		storage.renderingAssociations.putAll(renderingAssociations);
 		storage.renderingClasses.putAll(renderingClasses);
 		return storage;
 	}
@@ -163,20 +154,6 @@ public void mergeDependsOrAddon(RenderingRulesStorage depends) {
 				renderingAttributes.put(e.getKey(), e.getValue());
 			}
 		}
-		it = depends.renderingAssociations.entrySet().iterator();
-		while (it.hasNext()) {
-			Entry<String, RenderingRule> e = it.next();
-			if (renderingAssociations.containsKey(e.getKey())) {
-				RenderingRule root = renderingAssociations.get(e.getKey());
-				List<RenderingRule> list = e.getValue().getIfElseChildren();
-				for (RenderingRule every : list) {
-					root.addIfElseChildren(every);
-				}
-				e.getValue().addToBeginIfElseChildren(root);
-			} else {
-				renderingAssociations.put(e.getKey(), e.getValue());
-			}
-		}
 		for (int i = 0; i < LENGTH_RULES; i++) {
 			if (depends.tagValueGlobalRules[i] == null || depends.tagValueGlobalRules[i].isEmpty()) {
 				continue;
@@ -272,7 +249,7 @@ private void process(RenderingRulesHandler handler, int el) throws XmlPullParser
 	private class RenderingRulesHandler {
 		private final XmlPullParser parser;
 		private int state;
-		Stack<RenderingRule> stack = new Stack<RenderingRule>();
+		private Stack<RenderingRule> stack = new Stack<RenderingRule>();
 		private final RenderingRulesStorageResolver resolver;
 		private final boolean addon;
 		private RenderingRulesStorage dependsStorage;
@@ -376,7 +353,7 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 				state = LINE_RULES;
 			} else if("polygon".equals(name)){ //$NON-NLS-1$
 				state = POLYGON_RULES;
-			} else if("renderingAttribute".equals(name)){ //$NON-NLS-1$
+			} else if ("renderingAttribute".equals(name)) { //$NON-NLS-1$
 				String attr = attrsMap.get("name");
 				RenderingRule root = new RenderingRule(new HashMap<String, String>(), false, RenderingRulesStorage.this);
 				if (renderingAttributes.containsKey(attr)) {
@@ -385,22 +362,13 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 					renderingAttributes.put(attr, root);
 				}
 				stack.push(root);
-			} else if("renderingAssociation".equals(name)){ //$NON-NLS-1$
-				String attr = attrsMap.get("name");
-				RenderingRule root = new RenderingRule(new HashMap<String, String>(), false, RenderingRulesStorage.this);
-				if (renderingAssociations.containsKey(attr)) {
-					renderingAssociations.get(attr).addIfElseChildren(root);
-				} else {
-					renderingAssociations.put(attr, root);
-				}
-				stack.push(root);
-			} else if("renderingProperty".equals(name)){ //$NON-NLS-1$
+			} else if ("renderingProperty".equals(name)) { //$NON-NLS-1$
 				String attr = attrsMap.get("attr");
 				RenderingRuleProperty prop;
 				String type = attrsMap.get("type");
-				if("boolean".equalsIgnoreCase(type)){
+				if ("boolean".equalsIgnoreCase(type)) {
 					prop = RenderingRuleProperty.createInputBooleanProperty(attr);
-				} else if("string".equalsIgnoreCase(type)){
+				} else if ("string".equalsIgnoreCase(type)) {
 					prop = RenderingRuleProperty.createInputStringProperty(attr);
 				} else {
 					prop = RenderingRuleProperty.createInputIntProperty(attr);
@@ -413,11 +381,11 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 					prop.setPossibleValues(attrsMap.get("possibleValues").split(","));
 				}
 				PROPS.registerRule(prop, !addon);
-			} else if("renderingConstant".equals(name)){ //$NON-NLS-1$
-				if(!renderingConstants.containsKey(attrsMap.get("name"))){
+			} else if ("renderingConstant".equals(name)) {
+				if (!renderingConstants.containsKey(attrsMap.get("name"))) {
 					renderingConstants.put(attrsMap.get("name"), attrsMap.get("value"));
 				}
-			} else if ("renderingStyle".equals(name)) { //$NON-NLS-1$
+			} else if ("renderingStyle".equals(name)) {
 				if (!addon) {
 					String depends = attrsMap.get("depends");
 					if (depends != null && depends.length() > 0) {
@@ -466,8 +434,8 @@ private Map<String, String> parseAttributes(XmlPullParser parser, Map<String, St
 				if (vl != null && vl.startsWith("$")) {
 					String cv = vl.substring(1);
 					if (!renderingConstants.containsKey(cv) && !renderingAttributes.containsKey(cv)
-							&& !renderingAssociations.containsKey(cv) && !renderingClasses.containsKey(cv)) {
-						throw new IllegalStateException("Rendering constant or attribute '" + cv + "' was not specified.");
+							&& !renderingClasses.containsKey(cv)) {
+						throw new IllegalStateException("Rendering constant, attribute or class '" + cv + "' was not specified.");
 					}
 					if (renderingConstants.containsKey(cv)) {
 						vl = renderingConstants.get(cv);
@@ -478,7 +446,6 @@ private Map<String, String> parseAttributes(XmlPullParser parser, Map<String, St
 			return m;
 		}
 
-
 		public void endElement(String name) throws XmlPullParserException {
 			if (isCase(name) || isSwitch(name)) {
 				RenderingRule renderingRule = (RenderingRule) stack.pop();
@@ -487,9 +454,7 @@ public void endElement(String name) throws XmlPullParserException {
 				}
 			} else if (isApply(name)) {
 				stack.pop();
-			} else if ("renderingAttribute".equals(name)) { //$NON-NLS-1$
-				stack.pop();
-			} else if ("renderingAssociation".equals(name)) { //$NON-NLS-1$
+			} else if ("renderingAttribute".equals(name)) {
 				stack.pop();
 			}
 		}
@@ -539,15 +504,15 @@ public void registerTopLevel(RenderingRule renderingRule,
 	public int getTagValueKey(String tag, String value){
 		int itag = getDictionaryValue(tag);
 		int ivalue = getDictionaryValue(value);
-		return (itag << SHIFT_TAG_VAL) | ivalue; 
+		return (itag << SHIFT_TAG_VAL) | ivalue;
 	}
 	
 	public String getValueString(int tagValueKey){
-		return getStringValue(tagValueKey & ((1 << SHIFT_TAG_VAL) - 1)); 
+		return getStringValue(tagValueKey & ((1 << SHIFT_TAG_VAL) - 1));
 	}
 	
 	public String getTagString(int tagValueKey){
-		return getStringValue(tagValueKey >> SHIFT_TAG_VAL); 
+		return getStringValue(tagValueKey >> SHIFT_TAG_VAL);
 	}
 	
 	protected RenderingRule getRule(int state, int itag, int ivalue){
@@ -567,26 +532,15 @@ public RenderingRule getRule(int state, int key) {
 	public RenderingRule getRenderingAttributeRule(String attribute) {
 		return renderingAttributes.get(attribute);
 	}
-
-	public RenderingClass getRenderingClass(String name) {
-		return renderingClasses.get(name);
-	}
 	
 	public String[] getRenderingAttributeNames() {
 		return renderingAttributes.keySet().toArray(new String[0]);
 	}
+
 	public RenderingRule[] getRenderingAttributeValues() {
 		return renderingAttributes.values().toArray(new RenderingRule[0]);
 	}
 
-	public RenderingRule getRenderingAssociationRule(String association) {
-		return renderingAssociations.get(association);
-	}
-
-	public String[] getRenderingAssociationNames() {
-		return renderingAssociations.keySet().toArray(new String[0]);
-	}
-
 	public Map<String, RenderingClass> getRenderingClasses() {
 		return renderingClasses;
 	}

From d4d9111bad154eae6d0fd37446b36f3e45fa5c9c Mon Sep 17 00:00:00 2001
From: chumv <chumva.forever@gmail.com>
Date: Thu, 6 Feb 2025 16:55:11 +0200
Subject: [PATCH 08/10] Add support for inner rendering classes & update UI

---
 .../java/net/osmand/osm/OsmRouteType.java     |  16 +--
 .../net/osmand/osm/RenderingPropertyAttr.java |  15 +++
 .../net/osmand/render/RenderingClass.java     |  64 +++++++++-
 .../osmand/render/RenderingRulesStorage.java  | 117 ++++++++++++++++-
 OsmAnd/res/layout/rendering_classes_card.xml  |  14 +-
 .../plus/configmap/ConfigureMapUtils.java     | 107 +++++++++++-----
 .../routes/AlpineHikingScaleFragment.java     |  19 +--
 .../routes/CustomRoutesFragment.java          |  13 +-
 .../configmap/routes/CycleRoutesFragment.java |  16 +--
 .../routes/HikingRoutesFragment.java          |  14 +-
 .../configmap/routes/MapRoutesFragment.java   |  50 ++++----
 .../configmap/routes/MtbRoutesFragment.java   |  15 +--
 .../routes/RenderingClassFragment.java        |  81 ++++++++++++
 .../routes/RenderingClassesCard.java          | 120 ++++++++++++------
 .../configmap/routes/RouteLayersHelper.java   |  21 ++-
 .../osmand/plus/dashboard/DashboardOnMap.java |  30 ++++-
 .../osmand/plus/dashboard/DashboardType.java  |   1 +
 17 files changed, 500 insertions(+), 213 deletions(-)
 create mode 100644 OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
 create mode 100644 OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassFragment.java

diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java b/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java
index c22a7334777..79ce575116b 100644
--- a/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java
+++ b/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java
@@ -1,6 +1,6 @@
 package net.osmand.osm;
 
-import static net.osmand.osm.OsmRouteType.RenderingPropertyAttr.*;
+import static net.osmand.osm.RenderingPropertyAttr.*;
 
 import net.osmand.binary.BinaryMapDataObject;
 import net.osmand.binary.BinaryMapIndexReader;
@@ -448,18 +448,4 @@ private OsmRouteType reg() {
 			return osmRouteType;
 		}
 	}
-
-	protected static class RenderingPropertyAttr {
-		static final String HIKING_ROUTES = "hikingRoutesOSMC";
-		static final String CYCLE_ROUTES = "showCycleRoutes";
-		static final String MTB_ROUTES = "showMtbRoutes";
-		static final String ALPINE_HIKING = "alpineHiking";
-		static final String HORSE_ROUTES = "horseRoutes";
-		static final String PISTE_ROUTES = "pisteRoutes";
-		static final String WHITE_WATER_SPORTS = "whiteWaterSports";
-		static final String RUNNING_ROUTES = "showRunningRoutes";
-		static final String FITNESS_TRAILS = "showFitnessTrails";
-		static final String DIRTBIKE_ROUTES = "showDirtbikeTrails";
-		static final String CLIMBING_ROUTES = "showClimbingRoutes";
-	}
 }
diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java b/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
new file mode 100644
index 00000000000..0ee99e1255a
--- /dev/null
+++ b/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
@@ -0,0 +1,15 @@
+package net.osmand.osm;
+
+public class RenderingPropertyAttr {
+	public static final String HIKING_ROUTES = "hikingRoutesOSMC";
+	public static final String CYCLE_ROUTES = "showCycleRoutes";
+	public static final String MTB_ROUTES = "showMtbRoutes";
+	public static final String ALPINE_HIKING = "alpineHiking";
+	public static final String HORSE_ROUTES = "horseRoutes";
+	public static final String PISTE_ROUTES = "pisteRoutes";
+	public static final String WHITE_WATER_SPORTS = "whiteWaterSports";
+	public static final String RUNNING_ROUTES = "showRunningRoutes";
+	public static final String FITNESS_TRAILS = "showFitnessTrails";
+	public static final String DIRTBIKE_ROUTES = "showDirtbikeTrails";
+	public static final String CLIMBING_ROUTES = "showClimbingRoutes";
+}
diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
index ee995b45af2..13fbf03e101 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingClass.java
@@ -2,28 +2,78 @@
 
 public class RenderingClass {
 
-	private final String name;
 	private final String title;
+	private final String description;
+	private final String category;
+	private final String legendObject;
+	private final String innerLegendObject;
+	private final String innerTitle;
+	private final String innerDescription;
+	private final String innerCategory;
+	private final String innerNames;
 	private final boolean enabledByDefault;
+	private final String name;
 
-	public RenderingClass(String name, String title, boolean enabledByDefault) {
-		this.name = name;
+	public RenderingClass(String title, String description, String category, String legendObject,
+			String innerLegendObject, String innerTitle, String innerDescription,
+			String innerCategory, String innerNames, boolean enabledByDefault, String name) {
 		this.title = title;
+		this.description = description;
+		this.category = category;
+		this.legendObject = legendObject;
+		this.innerLegendObject = innerLegendObject;
+		this.innerTitle = innerTitle;
+		this.innerDescription = innerDescription;
+		this.innerCategory = innerCategory;
+		this.innerNames = innerNames;
 		this.enabledByDefault = enabledByDefault;
-	}
-
-	public String getName() {
-		return name;
+		this.name = name;
 	}
 
 	public String getTitle() {
 		return title;
 	}
 
+	public String getDescription() {
+		return description;
+	}
+
+	public String getCategory() {
+		return category;
+	}
+
+	public String getLegendObject() {
+		return legendObject;
+	}
+
+	public String getInnerLegendObject() {
+		return innerLegendObject;
+	}
+
+	public String getInnerTitle() {
+		return innerTitle;
+	}
+
+	public String getInnerDescription() {
+		return innerDescription;
+	}
+
+	public String getInnerCategory() {
+		return innerCategory;
+	}
+
+	public String getInnerNames() {
+		return innerNames;
+	}
+
 	public boolean isEnabledByDefault() {
 		return enabledByDefault;
 	}
 
+	public String getName() {
+		return name;
+	}
+
 	@Override
 	public String toString() {
 		return name;
diff --git a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
index d6881497c9a..1c06f3828c2 100644
--- a/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
+++ b/OsmAnd-java/src/main/java/net/osmand/render/RenderingRulesStorage.java
@@ -177,6 +177,34 @@ public void mergeDependsOrAddon(RenderingRulesStorage depends) {
 				tagValueGlobalRules[i] = depends.tagValueGlobalRules[i];
 			}
 		}
+		for (Entry<String, RenderingClass> entry : depends.renderingClasses.entrySet()) {
+			String className = entry.getKey();
+			RenderingClass renderingClass = entry.getValue();
+
+			if (renderingClasses.containsKey(className)) {
+				RenderingClass existingClass = renderingClasses.get(className);
+				RenderingClass mergedClass = mergeRenderingClasses(existingClass, renderingClass);
+				renderingClasses.put(className, mergedClass);
+			} else {
+				renderingClasses.put(className, renderingClass);
+			}
+		}
+	}
+
+	private RenderingClass mergeRenderingClasses(RenderingClass existing, RenderingClass depends) {
+		return new RenderingClass(
+				existing.getTitle() != null ? existing.getTitle() : depends.getTitle(),
+				existing.getDescription() != null ? existing.getDescription() : depends.getDescription(),
+				existing.getCategory() != null ? existing.getCategory() : depends.getCategory(),
+				existing.getLegendObject() != null ? existing.getLegendObject() : depends.getLegendObject(),
+				existing.getInnerLegendObject() != null ? existing.getInnerLegendObject() : depends.getInnerLegendObject(),
+				existing.getInnerTitle() != null ? existing.getInnerTitle() : depends.getInnerTitle(),
+				existing.getInnerDescription() != null ? existing.getInnerDescription() : depends.getInnerDescription(),
+				existing.getInnerCategory() != null ? existing.getInnerCategory() : depends.getInnerCategory(),
+				existing.getInnerNames() != null ? existing.getInnerNames() : depends.getInnerNames(),
+				existing.isEnabledByDefault() || depends.isEnabledByDefault(),
+				existing.getName() != null ? existing.getName() : depends.getName()
+		);
 	}
 
 	public static String colorToString(int color) {
@@ -250,6 +278,7 @@ private class RenderingRulesHandler {
 		private final XmlPullParser parser;
 		private int state;
 		private Stack<RenderingRule> stack = new Stack<RenderingRule>();
+		private Stack<String> nodeNamesStack = new Stack<String>();
 		private final RenderingRulesStorageResolver resolver;
 		private final boolean addon;
 		private RenderingRulesStorage dependsStorage;
@@ -402,10 +431,7 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 			} else if ("renderer".equals(name)) { //$NON-NLS-1$
 				throw new XmlPullParserException("Rendering style is deprecated and no longer supported.");
 			} else if ("renderingClass".equals(name)) {
-				String title = attrsMap.get("title");
-				String className = attrsMap.get("name");
-				boolean enable = Boolean.parseBoolean(attrsMap.get("enable"));
-				renderingClasses.put(className, new RenderingClass(className, title, enable));
+				parseRenderingClass(attrsMap);
 			} else {
 				log.warn("Unknown tag : " + name); //$NON-NLS-1$
 			}
@@ -415,6 +441,83 @@ public void startElement(Map<String, String> attrsMap, String name) throws XmlPu
 			}
 		}
 
+		private void parseRenderingClass(Map<String, String> attrsMap) {
+			StringBuilder path = new StringBuilder();
+			if (!nodeNamesStack.isEmpty()) {
+				for (String nodeName : nodeNamesStack) {
+					path.append(nodeName);
+				}
+			}
+
+			String name = attrsMap.get("name");
+			String title = attrsMap.get("title");
+			boolean enable = Boolean.parseBoolean(attrsMap.get("enable"));
+			String description = attrsMap.get("description");
+			String category = attrsMap.get("category");
+			String legendObject = attrsMap.get("legend-object");
+			String innerLegendObject = attrsMap.get("inner-legend-object");
+			String innerTitle = attrsMap.get("inner-title");
+			String innerDescription = attrsMap.get("inner-description");
+			String innerCategory = attrsMap.get("inner-category");
+			String innerNames = attrsMap.get("inner-names");
+
+			path.append(name);
+
+			RenderingClass renderingClass = new RenderingClass(title, description, category, legendObject,
+					innerLegendObject, innerTitle, innerDescription, innerCategory, innerNames, enable, path.toString());
+
+			nodeNamesStack.push(name);
+			renderingClasses.put(renderingClass.getName(), renderingClass);
+
+			parseInnerRenderingClasses(renderingClass);
+		}
+
+		private void parseInnerRenderingClasses(RenderingClass parentClass) {
+			String innerNames = parentClass.getInnerNames();
+			if (innerNames != null && !innerNames.isEmpty()) {
+				List<String> classNames = new ArrayList<>();
+				String nextPart = innerNames;
+				int splitPosition = 1;
+
+				while (splitPosition > 0) {
+					if (!nextPart.isEmpty() && !nextPart.startsWith(",")) {
+						splitPosition = nextPart.indexOf(',');
+						String symbolClassName = (splitPosition > 0) ? nextPart.substring(0, splitPosition) : nextPart;
+						if (!symbolClassName.isEmpty()) {
+							classNames.add(symbolClassName);
+						}
+					} else {
+						log.error(String.format("'%s' contains an empty name for inner class definition", innerNames));
+						break;
+					}
+					if (splitPosition > 0) {
+						nextPart = nextPart.substring(splitPosition + 1);
+					}
+				}
+
+				String temp = "$class";
+				String path = parentClass.getName();
+				boolean enabled = parentClass.isEnabledByDefault();
+				String innerLegendObject = parentClass.getInnerLegendObject();
+				String innerTitle = parentClass.getInnerTitle();
+				String innerDescription = parentClass.getInnerDescription();
+				String innerCategory = parentClass.getInnerCategory();
+
+				for (String className : classNames) {
+					String classTitle = innerTitle != null ? innerTitle.replace(temp, className) : null;
+					String classDescription = innerDescription != null ? innerDescription.replace(temp, className) : null;
+					String classCategory = innerCategory != null ? innerCategory.replace(temp, className) : null;
+					String classLegendObject = innerLegendObject != null ? innerLegendObject.replace(temp, className) : null;
+
+					RenderingClass newClass = new RenderingClass(classTitle, classDescription,
+							classCategory, classLegendObject, "", "",
+							"", "", "", enabled,
+							path.toString() + "." + className);
+					renderingClasses.put(newClass.getName(), newClass);
+				}
+			}
+		}
+
 		protected boolean isCase(String name) {
 			return "filter".equals(name) || "case".equals(name);
 		}
@@ -456,6 +559,8 @@ public void endElement(String name) throws XmlPullParserException {
 				stack.pop();
 			} else if ("renderingAttribute".equals(name)) {
 				stack.pop();
+			} else if ("renderingClass".equals(name)) {
+				nodeNamesStack.pop();
 			}
 		}
 	}
@@ -541,6 +646,10 @@ public RenderingRule[] getRenderingAttributeValues() {
 		return renderingAttributes.values().toArray(new RenderingRule[0]);
 	}
 
+	public RenderingClass getRenderingClass(String name) {
+		return renderingClasses.get(name);
+	}
+
 	public Map<String, RenderingClass> getRenderingClasses() {
 		return renderingClasses;
 	}
diff --git a/OsmAnd/res/layout/rendering_classes_card.xml b/OsmAnd/res/layout/rendering_classes_card.xml
index ee033b2031f..166efec3209 100644
--- a/OsmAnd/res/layout/rendering_classes_card.xml
+++ b/OsmAnd/res/layout/rendering_classes_card.xml
@@ -7,22 +7,10 @@
 	android:background="?attr/list_background_color"
 	android:orientation="vertical">
 
-	<net.osmand.plus.widgets.TextViewEx
-		android:id="@+id/header"
-		style="@style/TitleStyle.Medium"
-		android:layout_width="match_parent"
-		android:layout_height="wrap_content"
-		android:layout_marginHorizontal="@dimen/content_padding"
-		android:layout_marginVertical="@dimen/content_padding_small"
-		android:textColor="?android:textColorPrimary"
-		tools:text="@string/mtb_scale" />
-
 	<LinearLayout
 		android:id="@+id/container"
 		android:layout_width="match_parent"
 		android:layout_height="wrap_content"
-		android:orientation="vertical">
-
-	</LinearLayout>
+		android:orientation="vertical" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
index f82ac50856d..249bb7ebff4 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
@@ -1,5 +1,6 @@
 package net.osmand.plus.configmap;
 
+import static net.osmand.osm.RenderingPropertyAttr.*;
 import static net.osmand.plus.dialogs.DetailsBottomSheet.STREET_LIGHTING;
 import static net.osmand.plus.dialogs.DetailsBottomSheet.STREET_LIGHTING_NIGHT;
 import static net.osmand.plus.settings.backend.OsmandSettings.RENDERER_PREFERENCE_PREFIX;
@@ -22,14 +23,18 @@
 import net.osmand.render.RenderingRulesStorage;
 import net.osmand.util.Algorithms;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
 
 public class ConfigureMapUtils {
 
 	public static final String[] MAP_LANGUAGES_IDS = {"", "en", "af", "als", "ar", "az", "be", "ber", "bg", "bn", "bpy", "br", "bs", "ca", "ceb", "ckb", "cs", "cy", "da", "de", "el", "eo", "es", "et", "eu", "fa", "fi", "fr", "fy", "ga", "gl", "he", "hi", "hsb", "hr", "ht", "hu", "hy", "id", "is", "it", "ja", "ka", "kab", "kk", "kn", "ko", "ku", "la", "lb", "lo", "lt", "lv", "mk", "ml", "mr", "ms", "nds", "new", "nl", "nn", "no", "nv", "oc", "os", "pl", "pms", "pt", "ro", "ru", "sat", "sc", "sh", "sk", "sl", "sq", "sr", "sr-latn", "sv", "sw", "ta", "te", "th", "tl", "tr", "uk", "vi", "vo", "zh", "zh-Hans", "zh-Hant"};
 
-	public static final String ROUTE_CLASS_PREFIX = ".route.";
-
 	@NonNull
 	public static Map<String, String> getSorterMapLanguages(@NonNull OsmandApplication app) {
 		Map<String, String> mapLanguages = new HashMap<>();
@@ -51,7 +56,8 @@ public static Map<String, String> getSorterMapLanguages(@NonNull OsmandApplicati
 	 * @param unsortedLanguages map of entries like en:English. Empty key stands for device locale or default locale
 	 */
 	@NonNull
-	public static Comparator<String> getLanguagesComparator(@NonNull Map<String, String> unsortedLanguages) {
+	public static Comparator<String> getLanguagesComparator(
+			@NonNull Map<String, String> unsortedLanguages) {
 		return (leftKey, rightKey) -> {
 			int i1 = Algorithms.isEmpty(leftKey) ? 0 : (leftKey.equals("en") ? 1 : 2);
 			int i2 = Algorithms.isEmpty(rightKey) ? 0 : (rightKey.equals("en") ? 1 : 2);
@@ -66,7 +72,8 @@ public static Comparator<String> getLanguagesComparator(@NonNull Map<String, Str
 	}
 
 	@Nullable
-	public static RenderingRuleProperty getPropertyForAttr(@NonNull OsmandApplication app, @NonNull String attrName) {
+	public static RenderingRuleProperty getPropertyForAttr(@NonNull OsmandApplication app,
+			@NonNull String attrName) {
 		return getPropertyForAttr(getCustomRules(app), attrName);
 	}
 
@@ -81,7 +88,8 @@ public static RenderingRuleProperty getPropertyForAttr(
 		return null;
 	}
 
-	public static List<RenderingRuleProperty> getCustomRules(@NonNull OsmandApplication app, String... skipCategories) {
+	public static List<RenderingRuleProperty> getCustomRules(@NonNull OsmandApplication app,
+			String... skipCategories) {
 		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
 		if (renderer == null) {
 			return new ArrayList<>();
@@ -104,50 +112,81 @@ public static List<RenderingRuleProperty> getCustomRules(@NonNull OsmandApplicat
 		return customRules;
 	}
 
-	@NonNull
-	public static Map<String, RenderingClass> getRenderingClasses(@NonNull OsmandApplication app) {
-		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
-		if (renderer == null) {
-			return new LinkedHashMap<>();
+	protected static String[] getRenderingPropertyPossibleValues(@NonNull OsmandApplication app,
+			@NonNull RenderingRuleProperty p) {
+		String[] possibleValuesString = new String[p.getPossibleValues().length + 1];
+		possibleValuesString[0] = AndroidUtils.getRenderingStringPropertyValue(app, p.getDefaultValueDescription());
+
+		for (int j = 0; j < p.getPossibleValues().length; j++) {
+			possibleValuesString[j + 1] = AndroidUtils.getRenderingStringPropertyValue(app, p.getPossibleValues()[j]);
 		}
-		return renderer.getRenderingClasses();
+		return possibleValuesString;
 	}
 
 	@NonNull
-	public static Pair<RenderingClass, List<RenderingClass>> getRenderingClassesForKey(
+	public static Pair<RenderingClass, List<RenderingClass>> getRenderingClassWithChildren(
 			@NonNull OsmandApplication app, @NonNull String attrName) {
 		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
-		if (renderer == null) {
-			return null;
-		}
-		String key = ROUTE_CLASS_PREFIX + attrName.replace("show", "");
-		RenderingClass mainClass = renderer.getRenderingClass(key);
-		if (mainClass != null) {
-			List<RenderingClass> list = new ArrayList<>();
-			for (Map.Entry<String, RenderingClass> entry : renderer.getRenderingClasses().entrySet()) {
-				RenderingClass renderingClass = entry.getValue();
-				if (renderingClass.getName().startsWith(key + ".")) {
-					list.add(renderingClass);
-				}
+		if (renderer != null) {
+			String key = getRenderingClassNameForAttr(app, attrName);
+			RenderingClass renderingClass = renderer.getRenderingClass(key);
+			if (renderingClass != null) {
+				List<RenderingClass> children = getChildrenRenderingClasses(app, renderingClass);
+				return Pair.create(renderingClass, children);
 			}
-			return Pair.create(mainClass, list);
 		}
 		return null;
 	}
 
-	protected static String[] getRenderingPropertyPossibleValues(OsmandApplication app,
-			RenderingRuleProperty p) {
-		String[] possibleValuesString = new String[p.getPossibleValues().length + 1];
-		possibleValuesString[0] = AndroidUtils.getRenderingStringPropertyValue(app, p.getDefaultValueDescription());
+	@NonNull
+	public static List<RenderingClass> getChildrenRenderingClasses(@NonNull OsmandApplication app,
+			@NonNull RenderingClass parentClass) {
+		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
+		if (renderer != null) {
+			String key = parentClass.getName();
+			return renderer.getRenderingClasses().values().stream()
+					.filter(renderingClass -> {
+						String name = renderingClass.getName();
+						return name.startsWith(key + ".") && name.lastIndexOf('.') == key.length();
+					})
+					.collect(Collectors.toList());
+		}
+		return null;
+	}
 
-		for (int j = 0; j < p.getPossibleValues().length; j++) {
-			possibleValuesString[j + 1] = AndroidUtils.getRenderingStringPropertyValue(app, p.getPossibleValues()[j]);
+	@NonNull
+	public static String getRenderingClassNameForAttr(@NonNull OsmandApplication app,
+			@NonNull String attrName) {
+		switch (attrName) {
+			case HIKING_ROUTES:
+				return ".route.hiking";
+			case CYCLE_ROUTES:
+				return ".route.bicycle";
+			case MTB_ROUTES:
+				return ".route.mtb";
+			case "showMtbScale":
+				return ".route.mtb.mtb_scale";
+			case "showMtbScaleIMBATrails":
+				return ".route.mtb.mtb_scale_imba";
+			case ALPINE_HIKING:
+				return ".road.alpinehiking";
+			case HORSE_ROUTES:
+				return ".route.horse";
+			case PISTE_ROUTES:
+				return ".route.piste";
+			case RUNNING_ROUTES:
+				return ".route.running";
+			case FITNESS_TRAILS:
+				return ".route.fitness_trail";
+			case DIRTBIKE_ROUTES:
+				return ".route.dirtbike";
+			default:
+				return attrName;
 		}
-		return possibleValuesString;
 	}
 
 	protected static String getDescription(@NonNull OsmandSettings settings,
-	                                       @NonNull List<CommonPreference<Boolean>> prefs) {
+			@NonNull List<CommonPreference<Boolean>> prefs) {
 		int count = 0;
 		int enabled = 0;
 
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java
index e5e61feef62..83c23cb46df 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/AlpineHikingScaleFragment.java
@@ -10,7 +10,6 @@
 
 import net.osmand.plus.R;
 import net.osmand.plus.helpers.AndroidUiHelper;
-import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 
 public class AlpineHikingScaleFragment extends MapRoutesFragment {
@@ -27,12 +26,6 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleAlpineHikingRoutes();
 	}
 
-	@NonNull
-	@Override
-	protected String getSelectedAttrName() {
-		return ALPINE.getRenderingPropertyAttr();
-	}
-
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -43,14 +36,14 @@ protected void setupHeader(@NonNull View view) {
 		title.setText(R.string.rendering_attr_alpineHiking_name);
 
 		TextView description = container.findViewById(R.id.description);
-		description.setText(routeLayersHelper.getRoutesTypeDescription(getSelectedAttrName()));
+		description.setText(routeLayersHelper.getRoutesTypeDescription(ALPINE.getRenderingPropertyAttr()));
 		AndroidUiHelper.updateVisibility(description, enabled);
 
 		int selectedColor = settings.getApplicationMode().getProfileColor(nightMode);
 		int disabledColor = AndroidUtils.getColorFromAttr(app, R.attr.default_icon_color);
 		ImageView icon = container.findViewById(R.id.icon);
-		icon.setImageDrawable(getPaintedContentIcon(RouteUtils.getIconIdForAttr(getSelectedAttrName()),
-				enabled ? selectedColor : disabledColor));
+		icon.setImageDrawable(getPaintedContentIcon(RouteUtils.getIconIdForAttr(
+				ALPINE.getRenderingPropertyAttr()), enabled ? selectedColor : disabledColor));
 	}
 
 	@Override
@@ -58,10 +51,6 @@ protected void createCards(@NonNull View view) {
 		super.createCards(view);
 
 		addCard(new AlpineHikingCard(getMapActivity()));
-
-		BaseCard card = createRenderingClassCard(getSelectedAttrName());
-		if (card != null) {
-			addCard(card);
-		}
+		addRenderingClassCard(ALPINE.getRenderingPropertyAttr());
 	}
 }
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java
index 7650bd3b3a4..d5fa3ba65f1 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/CustomRoutesFragment.java
@@ -1,5 +1,7 @@
 package net.osmand.plus.configmap.routes;
 
+import static net.osmand.osm.OsmRouteType.MTB;
+
 import android.os.Bundle;
 import android.util.Pair;
 import android.view.View;
@@ -36,12 +38,6 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleRoutesType(attrName);
 	}
 
-	@NonNull
-	@Override
-	protected @NotNull String getSelectedAttrName() {
-		return attrName;
-	}
-
 	@Override
 	public void onCreate(@Nullable Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
@@ -72,9 +68,6 @@ protected void setupHeader(@NonNull View view) {
 	protected void createCards(@NonNull View view) {
 		super.createCards(view);
 
-		BaseCard card = createRenderingClassCard(getSelectedAttrName());
-		if (card != null) {
-			addCard(card);
-		}
+		addRenderingClassCard(attrName);
 	}
 }
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java
index 7b5400d088a..fcae8c180b7 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/CycleRoutesFragment.java
@@ -9,12 +9,9 @@
 import androidx.annotation.NonNull;
 
 import net.osmand.plus.R;
-import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 import net.osmand.plus.utils.ColorUtilities;
 
-import org.jetbrains.annotations.NotNull;
-
 public class CycleRoutesFragment extends MapRoutesFragment {
 
 	public static final String TAG = CycleRoutesFragment.class.getSimpleName();
@@ -29,12 +26,6 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleCycleRoutes();
 	}
 
-	@NonNull
-	@Override
-	protected String getSelectedAttrName() {
-		return BICYCLE.getRenderingPropertyAttr();
-	}
-
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -57,11 +48,8 @@ protected void setupHeader(@NonNull View view) {
 	@Override
 	protected void createCards(@NonNull View view) {
 		super.createCards(view);
-		addCard(new CycleRouteTypesCard(getMapActivity()));
 
-		BaseCard card = createRenderingClassCard(getSelectedAttrName());
-		if (card != null) {
-			addCard(card);
-		}
+		addCard(new CycleRouteTypesCard(getMapActivity()));
+		addRenderingClassCard(BICYCLE.getRenderingPropertyAttr());
 	}
 }
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java
index 655c2716417..46d68d6f8bf 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/HikingRoutesFragment.java
@@ -9,7 +9,6 @@
 import androidx.annotation.NonNull;
 
 import net.osmand.plus.R;
-import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 
 public class HikingRoutesFragment extends MapRoutesFragment {
@@ -28,12 +27,6 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleHikingRoutes();
 	}
 
-	@NonNull
-	@Override
-	protected String getSelectedAttrName() {
-		return HIKING.getRenderingPropertyAttr();
-	}
-
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -55,11 +48,8 @@ protected void setupHeader(@NonNull View view) {
 	@Override
 	protected void createCards(@NonNull View view) {
 		super.createCards(view);
-		addCard(new HikingRoutesCard(getMapActivity()));
 
-		BaseCard card = createRenderingClassCard(getSelectedAttrName());
-		if (card != null) {
-			addCard(card);
-		}
+		addCard(new HikingRoutesCard(getMapActivity()));
+		addRenderingClassCard(HIKING.getRenderingPropertyAttr());
 	}
 }
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java
index ea96e000f14..4517a1eee48 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/MapRoutesFragment.java
@@ -59,9 +59,6 @@ protected boolean isUsedOnMap() {
 
 	protected abstract void toggleMainPreference(@NonNull View view);
 
-	@NonNull
-	protected abstract String getSelectedAttrName();
-
 	@Override
 	public void onCreate(@Nullable Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
@@ -108,9 +105,10 @@ protected void setupHeader(@NonNull View view) {
 		AndroidUiHelper.updateVisibility(view.findViewById(R.id.header_divider), !enabled);
 	}
 
-	protected void addCard(@NonNull BaseCard card) {
-		cards.add(card);
-		card.setListener(this);
+	protected void setupContent(@NonNull View view) {
+		createCards(view);
+		inflateCards(view);
+		AndroidUiHelper.updateVisibility(cardsContainer, isEnabled());
 	}
 
 	protected void createCards(@NonNull View view) {
@@ -134,27 +132,25 @@ protected void inflateCards(@NonNull View view) {
 		}
 	}
 
-	@Nullable
-	protected BaseCard createRenderingClassCard(@NonNull String attrName) {
-		Pair<RenderingClass, List<RenderingClass>> pair = ConfigureMapUtils.getRenderingClassesForKey(app, attrName);
-		if (pair != null) {
-			addCard(new RenderingClassesCard(getMapActivity(), pair.first, pair.second));
-		}
-		return null;
+	protected void addCard(@NonNull BaseCard card) {
+		cards.add(card);
+		card.setListener(this);
 	}
 
-	protected void setupContent(@NonNull View view) {
-		createCards(view);
-		inflateCards(view);
-		AndroidUiHelper.updateVisibility(cardsContainer, isEnabled());
+	protected void addRenderingClassCard(@NonNull String attrName) {
+		BaseCard card = createRenderingClassCard(attrName);
+		if (card != null) {
+			addCard(card);
+		}
 	}
 
-	protected void refreshMap() {
-		MapActivity mapActivity = (MapActivity) getMyActivity();
-		if (mapActivity != null) {
-			mapActivity.refreshMapComplete();
-			mapActivity.updateLayers();
+	@Nullable
+	protected BaseCard createRenderingClassCard(@NonNull String attrName) {
+		Pair<RenderingClass, List<RenderingClass>> pair = ConfigureMapUtils.getRenderingClassWithChildren(app, attrName);
+		if (pair != null) {
+			return new RenderingClassesCard(getMapActivity(), pair.first, pair.second);
 		}
+		return null;
 	}
 
 	@NonNull
@@ -176,6 +172,14 @@ public void onCardPressed(@NonNull BaseCard card) {
 		}
 	}
 
+	protected void refreshMap() {
+		MapActivity mapActivity = (MapActivity) getMyActivity();
+		if (mapActivity != null) {
+			mapActivity.refreshMapComplete();
+			mapActivity.updateLayers();
+		}
+	}
+
 	@Nullable
 	protected MapActivity getMapActivity() {
 		FragmentActivity activity = getActivity();
@@ -199,7 +203,7 @@ public static boolean shouldShow(@NonNull OsmandApplication app, @NonNull String
 				BICYCLE.getRenderingPropertyAttr(), MTB.getRenderingPropertyAttr(),
 				HIKING.getRenderingPropertyAttr(), ALPINE.getRenderingPropertyAttr());
 
-		Pair<RenderingClass, List<RenderingClass>> pair = ConfigureMapUtils.getRenderingClassesForKey(app, attrName);
+		Pair<RenderingClass, List<RenderingClass>> pair = ConfigureMapUtils.getRenderingClassWithChildren(app, attrName);
 		return defaultScreens || pair != null;
 	}
 
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java
index 4b203d2d5f9..9b401bcf999 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/MtbRoutesFragment.java
@@ -10,7 +10,6 @@
 
 import net.osmand.plus.R;
 import net.osmand.plus.helpers.AndroidUiHelper;
-import net.osmand.plus.routepreparationmenu.cards.BaseCard;
 import net.osmand.plus.utils.AndroidUtils;
 
 public class MtbRoutesFragment extends MapRoutesFragment {
@@ -27,13 +26,6 @@ protected void toggleMainPreference(@NonNull View view) {
 		routeLayersHelper.toggleMtbRoutes();
 	}
 
-	@NonNull
-	@Override
-	protected String getSelectedAttrName() {
-		MtbClassification classification = routeLayersHelper.getSelectedMtbClassification();
-		return classification != null ? classification.attrName : MTB.getRenderingPropertyAttr();
-	}
-
 	protected void setupHeader(@NonNull View view) {
 		super.setupHeader(view);
 
@@ -54,11 +46,8 @@ protected void setupHeader(@NonNull View view) {
 	@Override
 	protected void createCards(@NonNull View view) {
 		super.createCards(view);
-		addCard(new MtbRoutesCard(getMapActivity()));
 
-		BaseCard card = createRenderingClassCard(getSelectedAttrName());
-		if (card != null) {
-			addCard(card);
-		}
+		addCard(new MtbRoutesCard(getMapActivity()));
+		addRenderingClassCard(MTB.getRenderingPropertyAttr());
 	}
 }
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassFragment.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassFragment.java
new file mode 100644
index 00000000000..dbef2e89c8d
--- /dev/null
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassFragment.java
@@ -0,0 +1,81 @@
+package net.osmand.plus.configmap.routes;
+
+import android.os.Bundle;
+import android.util.Pair;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+
+import net.osmand.plus.R;
+import net.osmand.plus.configmap.ConfigureMapUtils;
+import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.settings.backend.preferences.CommonPreference;
+import net.osmand.plus.utils.AndroidUtils;
+import net.osmand.render.RenderingClass;
+
+import java.util.List;
+
+public class RenderingClassFragment extends MapRoutesFragment {
+
+	public static final String TAG = RenderingClassFragment.class.getSimpleName();
+
+	private RenderingClass renderingClass;
+	private CommonPreference<Boolean> preference;
+
+	@Override
+	protected boolean isEnabled() {
+		return preference.get();
+	}
+
+	@Override
+	protected void toggleMainPreference(@NonNull View view) {
+		preference.set(!isEnabled());
+	}
+
+	@Override
+	public void onCreate(@Nullable Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+
+		renderingClass = app.getRouteLayersHelper().getSelectedRenderingClass();
+		preference = settings.getСustomBooleanRenderClassProperty(renderingClass.getName(), renderingClass.isEnabledByDefault());
+	}
+
+	protected void setupHeader(@NonNull View view) {
+		super.setupHeader(view);
+
+		boolean enabled = isEnabled();
+		View container = view.findViewById(R.id.preference_container);
+
+		TextView title = container.findViewById(R.id.title);
+		title.setText(renderingClass.getTitle());
+
+		AndroidUiHelper.updateVisibility(container.findViewById(R.id.icon), false);
+		AndroidUiHelper.updateVisibility(container.findViewById(R.id.description), false);
+	}
+
+	@Override
+	protected void createCards(@NonNull View view) {
+		super.createCards(view);
+
+		Pair<RenderingClass, List<RenderingClass>> pair = ConfigureMapUtils.getRenderingClassWithChildren(app, renderingClass.getName());
+		if (pair != null) {
+			addCard(new RenderingClassesCard(getMapActivity(), null, pair.second));
+		}
+	}
+
+	public static void showInstance(@NonNull FragmentActivity activity,
+			@NonNull RenderingClass renderingClass) {
+		FragmentManager manager = activity.getSupportFragmentManager();
+		if (AndroidUtils.isFragmentCanBeAdded(manager, TAG)) {
+			RenderingClassFragment fragment = new RenderingClassFragment();
+			fragment.renderingClass = renderingClass;
+			manager.beginTransaction()
+					.replace(R.id.content, fragment, fragment.getTag())
+					.commitAllowingStateLoss();
+		}
+	}
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java
index c583bafac62..0f5216af524 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/RenderingClassesCard.java
@@ -1,31 +1,40 @@
 package net.osmand.plus.configmap.routes;
 
-import android.graphics.drawable.Drawable;
+import static net.osmand.plus.dashboard.DashboardType.RENDERING_CLASS;
+import static net.osmand.plus.widgets.ctxmenu.data.ContextMenuItem.INVALID_ID;
+
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.CompoundButton;
-import android.widget.ImageView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
-import net.osmand.plus.helpers.AndroidUiHelper;
+import net.osmand.plus.configmap.ConfigureMapUtils;
 import net.osmand.plus.routepreparationmenu.cards.MapBaseCard;
 import net.osmand.plus.settings.backend.preferences.CommonPreference;
 import net.osmand.plus.utils.AndroidUtils;
-import net.osmand.plus.utils.ColorUtilities;
+import net.osmand.plus.utils.FontCache;
 import net.osmand.plus.utils.UiUtilities;
+import net.osmand.plus.widgets.ctxmenu.ViewCreator;
+import net.osmand.plus.widgets.ctxmenu.data.ContextMenuItem;
 import net.osmand.render.RenderingClass;
+import net.osmand.util.Algorithms;
 
 import java.util.List;
 
 public class RenderingClassesCard extends MapBaseCard {
 
-	private final RenderingClass mainClass;
-	private final List<RenderingClass> list;
+	private final RouteLayersHelper routeLayersHelper;
+	private final RenderingClass renderingClass;
+	private final List<RenderingClass> subclasses;
 
+	private ViewCreator viewCreator;
 	private ViewGroup container;
 
 	@Override
@@ -33,59 +42,88 @@ public int getCardLayoutId() {
 		return R.layout.rendering_classes_card;
 	}
 
-	public RenderingClassesCard(@NonNull MapActivity activity, @NonNull RenderingClass mainClass,
-			@NonNull List<RenderingClass> list) {
+	public RenderingClassesCard(@NonNull MapActivity activity,
+			@Nullable RenderingClass renderingClass,
+			@Nullable List<RenderingClass> subclasses) {
 		super(activity, true);
-		this.mainClass = mainClass;
-		this.list = list;
-	}
+		this.renderingClass = renderingClass;
+		this.subclasses = subclasses;
 
-	@Override
-	protected void updateContent() {
-		setupHeader();
-		setupRenderingClasses();
-	}
+		routeLayersHelper = app.getRouteLayersHelper();
 
-	private void setupHeader() {
-		TextView header = view.findViewById(R.id.header);
-		header.setText(mainClass.getTitle());
+		viewCreator = new ViewCreator(activity, nightMode);
+		viewCreator.setDefaultLayoutId(R.layout.list_item_icon_and_menu);
+		viewCreator.setCustomControlsColor(settings.getApplicationMode().getProfileColor(nightMode));
 	}
 
-	private void setupRenderingClasses() {
+	@Override
+	protected void updateContent() {
 		container = view.findViewById(R.id.container);
 		container.removeAllViews();
 
-		for (int i = 0; i < list.size(); i++) {
-			RenderingClass renderingClass = list.get(i);
-			container.addView(createRadioButton(renderingClass, i == list.size() - 1));
+		if (renderingClass != null) {
+			container.addView(createHeaderRow(renderingClass));
+		}
+
+		if (!Algorithms.isEmpty(subclasses)) {
+			container.addView(themedInflater.inflate(R.layout.simple_divider_item, container, false));
+
+			for (int i = 0; i < subclasses.size(); i++) {
+				RenderingClass renderingClass = subclasses.get(i);
+				boolean lastItem = i == subclasses.size() - 1;
+				List<RenderingClass> children = ConfigureMapUtils.getChildrenRenderingClasses(app, renderingClass);
+				container.addView(createItemRow(renderingClass, !Algorithms.isEmpty(children), !lastItem));
+			}
 		}
 	}
 
 	@NonNull
-	private View createRadioButton(@NonNull RenderingClass renderingClass, boolean lastItem) {
-		CommonPreference<Boolean> pref = settings.getСustomBooleanRenderClassProperty(renderingClass.getName(), renderingClass.isEnabledByDefault());
-		View view = themedInflater.inflate(R.layout.bottom_sheet_item_with_switch_56dp, container, false);
-
-		ImageView icon = view.findViewById(R.id.icon);
-		AndroidUiHelper.updateVisibility(icon, false);
+	private View createHeaderRow(@NonNull RenderingClass renderingClass) {
+		View view = createItemRow(renderingClass, false, false);
 
 		TextView title = view.findViewById(R.id.title);
-		title.setText(renderingClass.getTitle());
+		title.setTypeface(FontCache.getMediumFont(title.getTypeface()));
 
-		CompoundButton button = view.findViewById(R.id.compound_button);
-		UiUtilities.setupCompoundButton(nightMode, ColorUtilities.getActiveColor(app, nightMode), button);
-		button.setChecked(pref.get());
+		return view;
+	}
 
-		view.setOnClickListener(v -> {
-			boolean checked = !pref.get();
-			pref.set(checked);
-			button.setChecked(checked);
-			notifyCardPressed();
+	@NonNull
+	private View createItemRow(@NonNull RenderingClass renderingClass, boolean showSubscreen, boolean showDivider) {
+		CommonPreference<Boolean> pref = settings.getСustomBooleanRenderClassProperty(
+				renderingClass.getName(), renderingClass.isEnabledByDefault());
+		boolean enabled = pref.get();
+
+		ContextMenuItem item = new ContextMenuItem(renderingClass.getName())
+				.setSelected(enabled)
+				.setHideDivider(!showDivider)
+				.setTitle(renderingClass.getTitle())
+				.setColor(enabled ? settings.getApplicationMode().getProfileColor(nightMode) : null)
+				.setSecondaryIcon(showSubscreen ? R.drawable.ic_action_additional_option : INVALID_ID);
+
+		View view = viewCreator.getView(item, null);
+
+		CompoundButton compoundButton = view.findViewById(R.id.toggle_item);
+		compoundButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+			@Override
+			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+				pref.set(isChecked);
+				notifyCardPressed();
+			}
+		});
+		view.setOnClickListener(new OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				if (showSubscreen) {
+					routeLayersHelper.setSelectedRenderingClass(renderingClass);
+					mapActivity.getDashboard().setDashboardVisibility(true,
+							RENDERING_CLASS, AndroidUtils.getCenterViewCoordinates(view));
+				} else {
+					compoundButton.toggle();
+				}
+			}
 		});
-
 		int profileColor = settings.getApplicationMode().getProfileColor(nightMode);
-		Drawable background = UiUtilities.getColoredSelectableDrawable(app, profileColor, 0.3f);
-		AndroidUtils.setBackground(view, background);
+		AndroidUtils.setBackground(view, UiUtilities.getColoredSelectableDrawable(app, profileColor, 0.3f));
 
 		return view;
 	}
diff --git a/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java b/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java
index 40045f52109..e116fbd0762 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/routes/RouteLayersHelper.java
@@ -19,6 +19,7 @@
 import net.osmand.plus.settings.backend.OsmandSettings;
 import net.osmand.plus.settings.backend.preferences.CommonPreference;
 import net.osmand.plus.utils.AndroidUtils;
+import net.osmand.render.RenderingClass;
 import net.osmand.render.RenderingRuleProperty;
 import net.osmand.util.Algorithms;
 
@@ -46,6 +47,8 @@ public class RouteLayersHelper {
 
 	@Nullable
 	private String selectedAttrName;
+	@Nullable
+	private RenderingClass selectedRenderingClass;
 
 	public RouteLayersHelper(@NonNull OsmandApplication app) {
 		this.app = app;
@@ -74,6 +77,15 @@ public void setSelectedAttrName(@Nullable String selectedAttrName) {
 		this.selectedAttrName = selectedAttrName;
 	}
 
+	@Nullable
+	public RenderingClass getSelectedRenderingClass() {
+		return selectedRenderingClass;
+	}
+
+	public void setSelectedRenderingClass(@Nullable RenderingClass renderingClass) {
+		this.selectedRenderingClass = renderingClass;
+	}
+
 	public void toggleRoutesType(@NonNull String attrName) {
 		if (BICYCLE.getRenderingPropertyAttr().equals(attrName)) {
 			toggleCycleRoutes();
@@ -186,13 +198,8 @@ public void updateSelectedMtbClassification(@Nullable String classificationId) {
 
 	@NonNull
 	public String getSelectedMtbClassificationName(@NonNull Context context) {
-		String selectedId = getSelectedMtbClassificationId();
-		for (MtbClassification classification : MtbClassification.values()) {
-			if (Objects.equals(classification.attrName, selectedId)) {
-				return context.getString(classification.nameId);
-			}
-		}
-		return "";
+		MtbClassification classification = getSelectedMtbClassification();
+		return classification != null ? context.getString(classification.nameId) : "";
 	}
 
 	@NonNull
diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java b/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java
index e314ab40911..59221b9cc7e 100644
--- a/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java
+++ b/OsmAnd/src/net/osmand/plus/dashboard/DashboardOnMap.java
@@ -43,8 +43,11 @@
 import net.osmand.plus.OsmandApplication;
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
-import net.osmand.plus.configmap.routes.*;
 import net.osmand.plus.configmap.ConfigureMapFragment;
+import net.osmand.plus.configmap.routes.MapRoutesFragment;
+import net.osmand.plus.configmap.routes.RenderingClassFragment;
+import net.osmand.plus.configmap.routes.RouteLayersHelper;
+import net.osmand.plus.configmap.routes.TravelRoutesFragment;
 import net.osmand.plus.dashboard.tools.DashFragmentData;
 import net.osmand.plus.dashboard.tools.DashboardSettingsDialogFragment;
 import net.osmand.plus.dashboard.tools.TransactionBuilder;
@@ -87,6 +90,7 @@
 import net.osmand.plus.widgets.ctxmenu.callback.OnRowItemClick;
 import net.osmand.plus.widgets.ctxmenu.data.ContextMenuItem;
 import net.osmand.plus.wikipedia.WikipediaPoiMenu;
+import net.osmand.render.RenderingClass;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -321,6 +325,12 @@ private void updateToolbarActions() {
 		} else if (isCurrentType(MAP_ROUTES)) {
 			RouteLayersHelper helper = getMyApplication().getRouteLayersHelper();
 			tv.setText(helper.getRoutesTypeName(helper.getSelectedAttrName()));
+		} else if (isCurrentType(RENDERING_CLASS)) {
+			RouteLayersHelper helper = getMyApplication().getRouteLayersHelper();
+			RenderingClass renderingClass = helper.getSelectedRenderingClass();
+			if (renderingClass != null) {
+				tv.setText(renderingClass.getTitle());
+			}
 		}
 		ImageView edit = dashboardView.findViewById(R.id.toolbar_edit);
 		edit.setVisibility(View.GONE);
@@ -559,7 +569,17 @@ public void setDashboardVisibility(boolean visible, DashboardType type, boolean
 				} else if (isCurrentType(WEATHER_CONTOURS)) {
 					WeatherContoursFragment.showInstance(fragmentManager);
 				} else if (isCurrentType(MAP_ROUTES)) {
-					MapRoutesFragment.showInstance(mapActivity, getMyApplication().getRouteLayersHelper().getSelectedAttrName());
+					RouteLayersHelper helper = getMyApplication().getRouteLayersHelper();
+					String attrName = helper.getSelectedAttrName();
+					if (attrName != null) {
+						MapRoutesFragment.showInstance(mapActivity, attrName);
+					}
+				} else if (isCurrentType(RENDERING_CLASS)) {
+					RouteLayersHelper helper = getMyApplication().getRouteLayersHelper();
+					RenderingClass renderingClass = helper.getSelectedRenderingClass();
+					if (renderingClass != null) {
+						RenderingClassFragment.showInstance(mapActivity, renderingClass);
+					}
 				}
 				scrollView.setVisibility(View.VISIBLE);
 				listViewLayout.setVisibility(View.GONE);
@@ -624,7 +644,7 @@ private void applyDayNightMode() {
 		} else {
 			listView.setBackgroundColor(backgroundColor);
 		}
-		if (isNoCurrentType(CONFIGURE_MAP, CONTOUR_LINES, TERRAIN, MAP_ROUTES, TRAVEL_ROUTES,
+		if (isNoCurrentType(CONFIGURE_MAP, CONTOUR_LINES, TERRAIN, MAP_ROUTES, RENDERING_CLASS, TRAVEL_ROUTES,
 				OSM_NOTES, WIKIPEDIA, TRANSPORT_LINES, WEATHER, WEATHER_LAYER, WEATHER_CONTOURS, NAUTICAL_DEPTH)) {
 			listView.setDivider(dividerDrawable);
 			listView.setDividerHeight(AndroidUtils.dpToPx(mapActivity, 1f));
@@ -962,8 +982,8 @@ public boolean isNoCurrentType(@NonNull DashboardType... types) {
 
 	public boolean isCurrentTypeHasIndividualFragment() {
 		return isCurrentType(
-				CONFIGURE_MAP, MAPILLARY, TERRAIN, RELIEF_3D, MAP_ROUTES, TRAVEL_ROUTES,
-				TRANSPORT_LINES, WEATHER, WEATHER_LAYER, WEATHER_CONTOURS, NAUTICAL_DEPTH
+				CONFIGURE_MAP, MAPILLARY, TERRAIN, RELIEF_3D, MAP_ROUTES, RENDERING_CLASS,
+				TRAVEL_ROUTES, TRANSPORT_LINES, WEATHER, WEATHER_LAYER, WEATHER_CONTOURS, NAUTICAL_DEPTH
 		);
 	}
 
diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashboardType.java b/OsmAnd/src/net/osmand/plus/dashboard/DashboardType.java
index 11a0661d87f..2a792eeb9f7 100644
--- a/OsmAnd/src/net/osmand/plus/dashboard/DashboardType.java
+++ b/OsmAnd/src/net/osmand/plus/dashboard/DashboardType.java
@@ -18,4 +18,5 @@ public enum DashboardType {
 	WEATHER_CONTOURS,
 	NAUTICAL_DEPTH,
 	MAP_ROUTES,
+	RENDERING_CLASS
 }

From 3283ef675d56de827146525e3e3b2844aafd6f2a Mon Sep 17 00:00:00 2001
From: chumv <chumva.forever@gmail.com>
Date: Fri, 7 Feb 2025 10:52:50 +0200
Subject: [PATCH 09/10] Move getRenderingClassNameForAttr to
 RenderingPropertyAttr

---
 .../net/osmand/osm/RenderingPropertyAttr.java | 32 +++++++++++++++++
 .../plus/configmap/ConfigureMapUtils.java     | 35 ++-----------------
 2 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java b/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
index 0ee99e1255a..0d5c6c92ca2 100644
--- a/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
+++ b/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
@@ -1,6 +1,7 @@
 package net.osmand.osm;
 
 public class RenderingPropertyAttr {
+
 	public static final String HIKING_ROUTES = "hikingRoutesOSMC";
 	public static final String CYCLE_ROUTES = "showCycleRoutes";
 	public static final String MTB_ROUTES = "showMtbRoutes";
@@ -12,4 +13,35 @@ public class RenderingPropertyAttr {
 	public static final String FITNESS_TRAILS = "showFitnessTrails";
 	public static final String DIRTBIKE_ROUTES = "showDirtbikeTrails";
 	public static final String CLIMBING_ROUTES = "showClimbingRoutes";
+	public static final String SHOW_MTB_SCALE = "showMtbScale";
+	public static final String SHOW_MTB_IMBA_SCALE = "showMtbScaleIMBATrails";
+
+	public static String getRenderingClassNameForAttr(String attrName) {
+		switch (attrName) {
+			case HIKING_ROUTES:
+				return ".route.hiking";
+			case CYCLE_ROUTES:
+				return ".route.bicycle";
+			case MTB_ROUTES:
+				return ".route.mtb";
+			case SHOW_MTB_SCALE:
+				return ".route.mtb.mtb_scale";
+			case SHOW_MTB_IMBA_SCALE:
+				return ".route.mtb.mtb_scale_imba";
+			case ALPINE_HIKING:
+				return ".road.alpinehiking";
+			case HORSE_ROUTES:
+				return ".route.horse";
+			case PISTE_ROUTES:
+				return ".route.piste";
+			case RUNNING_ROUTES:
+				return ".route.running";
+			case FITNESS_TRAILS:
+				return ".route.fitness_trail";
+			case DIRTBIKE_ROUTES:
+				return ".route.dirtbike";
+			default:
+				return attrName;
+		}
+	}
 }
diff --git a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
index 249bb7ebff4..8d95910f77f 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
@@ -1,6 +1,5 @@
 package net.osmand.plus.configmap;
 
-import static net.osmand.osm.RenderingPropertyAttr.*;
 import static net.osmand.plus.dialogs.DetailsBottomSheet.STREET_LIGHTING;
 import static net.osmand.plus.dialogs.DetailsBottomSheet.STREET_LIGHTING_NIGHT;
 import static net.osmand.plus.settings.backend.OsmandSettings.RENDERER_PREFERENCE_PREFIX;
@@ -11,6 +10,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import net.osmand.osm.RenderingPropertyAttr;
 import net.osmand.plus.OsmandApplication;
 import net.osmand.plus.R;
 import net.osmand.plus.activities.MapActivity;
@@ -128,7 +128,7 @@ public static Pair<RenderingClass, List<RenderingClass>> getRenderingClassWithCh
 			@NonNull OsmandApplication app, @NonNull String attrName) {
 		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
 		if (renderer != null) {
-			String key = getRenderingClassNameForAttr(app, attrName);
+			String key = RenderingPropertyAttr.getRenderingClassNameForAttr(attrName);
 			RenderingClass renderingClass = renderer.getRenderingClass(key);
 			if (renderingClass != null) {
 				List<RenderingClass> children = getChildrenRenderingClasses(app, renderingClass);
@@ -154,37 +154,6 @@ public static List<RenderingClass> getChildrenRenderingClasses(@NonNull OsmandAp
 		return null;
 	}
 
-	@NonNull
-	public static String getRenderingClassNameForAttr(@NonNull OsmandApplication app,
-			@NonNull String attrName) {
-		switch (attrName) {
-			case HIKING_ROUTES:
-				return ".route.hiking";
-			case CYCLE_ROUTES:
-				return ".route.bicycle";
-			case MTB_ROUTES:
-				return ".route.mtb";
-			case "showMtbScale":
-				return ".route.mtb.mtb_scale";
-			case "showMtbScaleIMBATrails":
-				return ".route.mtb.mtb_scale_imba";
-			case ALPINE_HIKING:
-				return ".road.alpinehiking";
-			case HORSE_ROUTES:
-				return ".route.horse";
-			case PISTE_ROUTES:
-				return ".route.piste";
-			case RUNNING_ROUTES:
-				return ".route.running";
-			case FITNESS_TRAILS:
-				return ".route.fitness_trail";
-			case DIRTBIKE_ROUTES:
-				return ".route.dirtbike";
-			default:
-				return attrName;
-		}
-	}
-
 	protected static String getDescription(@NonNull OsmandSettings settings,
 			@NonNull List<CommonPreference<Boolean>> prefs) {
 		int count = 0;

From e0fbf13bbcdb49a8600f470638c29a167492851d Mon Sep 17 00:00:00 2001
From: chumv <chumva.forever@gmail.com>
Date: Fri, 7 Feb 2025 11:08:11 +0200
Subject: [PATCH 10/10] Convert RenderingPropertyAttr to enum

---
 .../java/net/osmand/osm/OsmRouteType.java     |  9 +-
 .../net/osmand/osm/RenderingPropertyAttr.java | 87 ++++++++++---------
 .../plus/configmap/ConfigureMapUtils.java     |  3 +-
 3 files changed, 53 insertions(+), 46 deletions(-)

diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java b/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java
index 68a54007c5e..52ac3669b5c 100644
--- a/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java
+++ b/OsmAnd-java/src/main/java/net/osmand/osm/OsmRouteType.java
@@ -443,8 +443,13 @@ public RouteActivityTypeBuilder icon(String icon) {
 			return this;
 		}
 
-		public RouteActivityTypeBuilder renderingPropertyAttr(String renderingPropertyAttr) {
-			osmRouteType.renderingPropertyAttr = renderingPropertyAttr;
+		public RouteActivityTypeBuilder renderingPropertyAttr(String propertyAttr) {
+			osmRouteType.renderingPropertyAttr = propertyAttr;
+			return this;
+		}
+
+		public RouteActivityTypeBuilder renderingPropertyAttr(RenderingPropertyAttr propertyAttr) {
+			renderingPropertyAttr(propertyAttr.getAttrName());
 			return this;
 		}
 
diff --git a/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java b/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
index 0d5c6c92ca2..256ed321b83 100644
--- a/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
+++ b/OsmAnd-java/src/main/java/net/osmand/osm/RenderingPropertyAttr.java
@@ -1,47 +1,48 @@
 package net.osmand.osm;
 
-public class RenderingPropertyAttr {
-
-	public static final String HIKING_ROUTES = "hikingRoutesOSMC";
-	public static final String CYCLE_ROUTES = "showCycleRoutes";
-	public static final String MTB_ROUTES = "showMtbRoutes";
-	public static final String ALPINE_HIKING = "alpineHiking";
-	public static final String HORSE_ROUTES = "horseRoutes";
-	public static final String PISTE_ROUTES = "pisteRoutes";
-	public static final String WHITE_WATER_SPORTS = "whiteWaterSports";
-	public static final String RUNNING_ROUTES = "showRunningRoutes";
-	public static final String FITNESS_TRAILS = "showFitnessTrails";
-	public static final String DIRTBIKE_ROUTES = "showDirtbikeTrails";
-	public static final String CLIMBING_ROUTES = "showClimbingRoutes";
-	public static final String SHOW_MTB_SCALE = "showMtbScale";
-	public static final String SHOW_MTB_IMBA_SCALE = "showMtbScaleIMBATrails";
-
-	public static String getRenderingClassNameForAttr(String attrName) {
-		switch (attrName) {
-			case HIKING_ROUTES:
-				return ".route.hiking";
-			case CYCLE_ROUTES:
-				return ".route.bicycle";
-			case MTB_ROUTES:
-				return ".route.mtb";
-			case SHOW_MTB_SCALE:
-				return ".route.mtb.mtb_scale";
-			case SHOW_MTB_IMBA_SCALE:
-				return ".route.mtb.mtb_scale_imba";
-			case ALPINE_HIKING:
-				return ".road.alpinehiking";
-			case HORSE_ROUTES:
-				return ".route.horse";
-			case PISTE_ROUTES:
-				return ".route.piste";
-			case RUNNING_ROUTES:
-				return ".route.running";
-			case FITNESS_TRAILS:
-				return ".route.fitness_trail";
-			case DIRTBIKE_ROUTES:
-				return ".route.dirtbike";
-			default:
-				return attrName;
+public enum RenderingPropertyAttr {
+
+	HIKING_ROUTES("hikingRoutesOSMC", ".route.hiking"),
+	CYCLE_ROUTES("showCycleRoutes", ".route.bicycle"),
+	MTB_ROUTES("showMtbRoutes", ".route.mtb"),
+	ALPINE_HIKING("alpineHiking", ".road.alpinehiking"),
+	HORSE_ROUTES("horseRoutes", ".route.horse"),
+	PISTE_ROUTES("pisteRoutes", ".route.piste"),
+	WHITE_WATER_SPORTS("whiteWaterSports", null),
+	RUNNING_ROUTES("showRunningRoutes", ".route.running"),
+	FITNESS_TRAILS("showFitnessTrails", ".route.fitness_trail"),
+	DIRTBIKE_ROUTES("showDirtbikeTrails", ".route.dirtbike"),
+	CLIMBING_ROUTES("showClimbingRoutes", null),
+	SHOW_MTB_SCALE("showMtbScale", ".route.mtb.mtb_scale"),
+	SHOW_MTB_IMBA_SCALE("showMtbScaleIMBATrails", ".route.mtb.mtb_scale_imba");
+
+	private final String attrName;
+	private final String renderingClassName;
+
+	RenderingPropertyAttr(String attrName, String renderingClassName) {
+		this.attrName = attrName;
+		this.renderingClassName = renderingClassName;
+	}
+
+	public String getAttrName() {
+		return attrName;
+	}
+
+	public String getRenderingClassName() {
+		return renderingClassName;
+	}
+
+	public static String getRenderingClassName(String attrName) {
+		RenderingPropertyAttr attr = fromAttrName(attrName);
+		return attr != null && attr.renderingClassName != null ? attr.renderingClassName : attrName;
+	}
+
+	public static RenderingPropertyAttr fromAttrName(String attrName) {
+		for (RenderingPropertyAttr attr : values()) {
+			if (attr.attrName.equals(attrName)) {
+				return attr;
+			}
 		}
+		return null;
 	}
-}
+}
\ No newline at end of file
diff --git a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
index 36ed077fbc4..c30adc629b0 100644
--- a/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
+++ b/OsmAnd/src/net/osmand/plus/configmap/ConfigureMapUtils.java
@@ -130,7 +130,8 @@ public static Pair<RenderingClass, List<RenderingClass>> getRenderingClassWithCh
 			@NonNull OsmandApplication app, @NonNull String attrName) {
 		RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer();
 		if (renderer != null) {
-			String key = RenderingPropertyAttr.getRenderingClassNameForAttr(attrName);
+			RenderingPropertyAttr attr = RenderingPropertyAttr.fromAttrName(attrName);
+			String key = RenderingPropertyAttr.getRenderingClassName(attrName);
 			RenderingClass renderingClass = renderer.getRenderingClass(key);
 			if (renderingClass != null) {
 				List<RenderingClass> children = getChildrenRenderingClasses(app, renderingClass);