Merge pull request #11203 from osmandapp/RouteLine

Route Line Customization
This commit is contained in:
Vitaliy 2021-03-24 01:39:08 +02:00 committed by GitHub
commit feadaa0da5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 2000 additions and 70 deletions

View file

@ -10,8 +10,6 @@ import net.osmand.router.GeneralRouter.RouteDataObjectAttribute;
public class TransportRoutingConfiguration {
public static final String KEY = "public_transport";
public int ZOOM_TO_LOAD_TILES = 15;
public int walkRadius = 1500; // ? 3000

View file

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_transparent">
<LinearLayout
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/context_menu_top_shadow" />
<LinearLayout
android:id="@+id/route_menu_top_shadow_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color"
android:minHeight="@dimen/bottom_sheet_title_height"
android:orientation="vertical"
android:paddingLeft="@dimen/content_padding"
android:paddingRight="@dimen/content_padding">
<View
android:layout_width="@dimen/content_padding"
android:layout_height="2dp"
android:layout_gravity="center"
android:layout_marginTop="@dimen/context_menu_padding_margin_tiny"
android:layout_marginBottom="@dimen/list_item_button_padding"
android:background="?attr/bg_dash_line" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/header_title"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:gravity="center_vertical"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_medium" />
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/bottom_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="@drawable/bg_contextmenu_shadow"
android:foregroundGravity="top|fill_horizontal">
<net.osmand.plus.LockableScrollView
android:id="@+id/route_menu_bottom_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/route_info_bg">
<LinearLayout
android:id="@+id/route_menu_cards_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/route_info_bg"
android:orientation="vertical"
android:paddingBottom="@dimen/dialog_button_ex_height" />
</net.osmand.plus.LockableScrollView>
</FrameLayout>
</LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/context_menu_toolbar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/card_and_list_background_basic"
android:minHeight="@dimen/toolbar_height"
android:padding="0dp"
osmand:contentInsetEnd="0dp"
osmand:contentInsetLeft="0dp"
osmand:contentInsetRight="0dp"
osmand:contentInsetStart="0dp"
osmand:theme="@style/ThemeOverlay.AppCompat.ActionBar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="@dimen/toolbar_height">
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/close_button"
style="@style/Widget.AppCompat.Toolbar.Button.Navigation"
android:layout_width="@dimen/toolbar_height"
android:layout_height="@dimen/toolbar_height"
android:tint="?android:textColorPrimary"
android:contentDescription="@string/access_shared_string_navigate_up"
osmand:srcCompat="@drawable/ic_arrow_back" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/toolbar_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_weight="1"
android:ellipsize="end"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="2"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/dialog_header_text_size"
osmand:typeface="@string/font_roboto_medium"
android:text="@string/shared_string_route_line" />
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/context_menu_controls" />
<LinearLayout
android:id="@+id/control_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/buttons_shadow"
android:layout_width="match_parent"
android:layout_height="8dp"
android:layout_gravity="bottom"
android:background="@drawable/shadow" />
<include
layout="@layout/bottom_buttons"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_ex_height"
android:layout_gravity="bottom" />
</LinearLayout>
</FrameLayout>

View file

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<net.osmand.plus.widgets.TextViewEx
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:letterSpacing="@dimen/description_letter_spacing"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small"
android:text="@string/shared_string_color"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_medium" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/color_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:letterSpacing="@dimen/description_letter_spacing"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="Orange" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="@dimen/content_padding_half"
android:paddingLeft="@dimen/content_padding_half"
android:paddingEnd="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
tools:itemCount="2"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/point_editor_group_select_item"
tools:orientation="horizontal" />
</LinearLayout>
<FrameLayout
android:id="@+id/theme_toggle_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/content_padding_small" >
<include layout="@layout/custom_radio_buttons" />
</FrameLayout>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:letterSpacing="@dimen/description_letter_spacing"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Specify color for map mode: day." />
<FrameLayout
android:id="@+id/colors_card_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/content_padding_small"/>
</LinearLayout>

View file

@ -0,0 +1,170 @@
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<View
android:id="@+id/top_divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/divider_color_basic"
android:focusable="false" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<net.osmand.plus.widgets.TextViewEx
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:letterSpacing="@dimen/description_letter_spacing"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small"
android:text="@string/select_track_width"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_medium" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/width_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:letterSpacing="@dimen/description_letter_spacing"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_list_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="21" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="@dimen/context_menu_padding_margin_tiny">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="@dimen/content_padding_half"
android:paddingLeft="@dimen/content_padding_half"
android:paddingEnd="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
tools:itemCount="3"
tools:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/point_editor_group_select_item"
tools:orientation="horizontal" />
</LinearLayout>
<LinearLayout
android:id="@+id/slider_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/content_padding"
android:paddingBottom="@dimen/favorites_select_group_button_height">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding">
<net.osmand.plus.widgets.TextViewEx
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:lineSpacingExtra="@dimen/line_spacing_extra_description"
android:text="@string/shared_string_custom"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/width_value_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:lineSpacingExtra="@dimen/line_spacing_extra_description"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_list_text_size"
tools:text="1" />
</LinearLayout>
<com.google.android.material.slider.Slider
android:id="@+id/width_slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:stepSize="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/width_value_min"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="start"
android:lineSpacingExtra="@dimen/line_spacing_extra_description"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_list_text_size"
tools:text="1" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/width_value_max"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="end"
android:lineSpacingExtra="@dimen/line_spacing_extra_description"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_list_text_size"
tools:text="24" />
</LinearLayout>
</LinearLayout>
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:letterSpacing="@dimen/description_letter_spacing"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_regular"
tools:text="Route line would be use width specified on selected map style: OsmAnd." />
</LinearLayout>

View file

@ -11,6 +11,11 @@
Thx - Hardy
-->
<string name="specify_color_for_map_mode">Specify color for map mode: %1$s.</string>
<string name="route_line_use_map_style_appearance">Route line would be use %1$s specified on selected map style: %2$s.</string>
<string name="shared_string_route_line">Route line</string>
<string name="customize_route_line">Customize route line</string>
<string name="trip_recording_show_start_dialog_setting">If disabled, recording will start right after tap on the widget or menu item, skipping the confirmation dialog.</string>
<string name="show_start_dialog">Show start dialog</string>
<string name="lost_data_warning">All unsaved data will be lost.</string>

View file

@ -76,4 +76,11 @@
android:title="@string/select_navigation_icon"
android:selectable="false"/>
<Preference
android:key="customize_route_line"
android:layout="@layout/preference_button"
android:persistent="false"
android:title="@string/customize_route_line"
android:icon="@drawable/ic_action_route_distance" />
</PreferenceScreen>

View file

@ -125,6 +125,7 @@ import net.osmand.plus.settings.datastorage.DataStorageFragment;
import net.osmand.plus.settings.fragments.BaseSettingsFragment;
import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType;
import net.osmand.plus.settings.fragments.ConfigureProfileFragment;
import net.osmand.plus.settings.fragments.RouteLineAppearanceFragment;
import net.osmand.plus.track.TrackAppearanceFragment;
import net.osmand.plus.track.TrackMenuFragment;
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
@ -2265,6 +2266,10 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
return getFragment(TrackMenuFragment.TAG);
}
public RouteLineAppearanceFragment getRouteLineAppearanceFragment() {
return getFragment(RouteLineAppearanceFragment.TAG);
}
public void backToConfigureProfileFragment() {
FragmentManager fragmentManager = getSupportFragmentManager();
int backStackEntryCount = fragmentManager.getBackStackEntryCount();

View file

@ -193,10 +193,7 @@ public class SelectMapStyleBottomSheetDialogFragment extends MenuBottomSheetDial
List<String> names = new ArrayList<>(renderers.keySet());
for (String name : names) {
String translation = RendererRegistry.getTranslatedRendererName(context, name);
if (translation == null) {
translation = name.replace('_', ' ').replace('-', ' ');
}
String translation = RendererRegistry.getRendererName(context, name);
res.put(translation, name);
}

View file

@ -106,6 +106,7 @@ import static net.osmand.plus.measurementtool.command.ClearPointsCommand.ClearCo
import static net.osmand.plus.measurementtool.command.ClearPointsCommand.ClearCommandMode.AFTER;
import static net.osmand.plus.measurementtool.command.ClearPointsCommand.ClearCommandMode.ALL;
import static net.osmand.plus.measurementtool.command.ClearPointsCommand.ClearCommandMode.BEFORE;
import static net.osmand.plus.routing.TransportRoutingHelper.PUBLIC_TRANSPORT_KEY;
public class MeasurementToolFragment extends BaseOsmAndFragment implements RouteBetweenPointsFragmentListener,
OptionsFragmentListener, GpxApproximationFragmentListener, SelectedPointFragmentListener,
@ -906,7 +907,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route
case SnapTrackWarningFragment.CONTINUE_RESULT_CODE:
if (mapActivity != null) {
ApplicationMode mode = editingCtx.getAppMode();
if (mode == ApplicationMode.DEFAULT || "public_transport".equals(mode.getRoutingProfile())) {
if (mode == ApplicationMode.DEFAULT || PUBLIC_TRANSPORT_KEY.equals(mode.getRoutingProfile())) {
mode = null;
}
List<List<WptPt>> pointsSegments = editingCtx.getPointsSegments(true, false);

View file

@ -20,6 +20,8 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static net.osmand.plus.routing.TransportRoutingHelper.PUBLIC_TRANSPORT_KEY;
public class ProfileCard extends BaseCard {
private ApplicationMode selectedMode;
@ -42,7 +44,7 @@ public class ProfileCard extends BaseCard {
Iterator<ApplicationMode> iterator = modes.iterator();
while (iterator.hasNext()) {
ApplicationMode mode = iterator.next();
if ("public_transport".equals(mode.getRoutingProfile())) {
if (PUBLIC_TRANSPORT_KEY.equals(mode.getRoutingProfile())) {
iterator.remove();
}
}

View file

@ -35,6 +35,7 @@ import static net.osmand.plus.UiUtilities.CustomRadioButtonType.END;
import static net.osmand.plus.UiUtilities.CustomRadioButtonType.START;
import static net.osmand.plus.measurementtool.MeasurementEditingContext.DEFAULT_APP_MODE;
import static net.osmand.plus.measurementtool.SelectFileBottomSheet.BOTTOM_SHEET_HEIGHT_DP;
import static net.osmand.plus.routing.TransportRoutingHelper.PUBLIC_TRANSPORT_KEY;
public class RouteBetweenPointsBottomSheetDialogFragment extends BottomSheetBehaviourDialogFragment {
@ -116,7 +117,7 @@ public class RouteBetweenPointsBottomSheetDialogFragment extends BottomSheetBeha
for (int i = 0; i < modes.size(); i++) {
ApplicationMode mode = modes.get(i);
if (!"public_transport".equals(mode.getRoutingProfile())) {
if (!PUBLIC_TRANSPORT_KEY.equals(mode.getRoutingProfile())) {
icon = app.getUIUtilities().getPaintedIcon(mode.getIconRes(), mode.getProfileColor(nightMode));
addProfileView(navigationType, onClickListener, i, icon, mode.toHumanString(), mode.equals(appMode));
}

View file

@ -102,9 +102,7 @@ public class MapStyleAction extends SwitchableAction<String> {
@Override
public String getTranslatedItemName(Context context, String item) {
String translation = RendererRegistry.getTranslatedRendererName(context, item);
return translation != null ? translation
: item.replace('_', ' ').replace('-', ' ');
return RendererRegistry.getRendererName(context, item);
}
public List<String> getFilteredStyles() {
@ -175,9 +173,8 @@ public class MapStyleAction extends SwitchableAction<String> {
List<String> visibleNamesList = new ArrayList<>();
final List<String> items = new ArrayList<>(renderers.keySet());
for (String item : items) {
String translation = RendererRegistry.getTranslatedRendererName(activity, item);
visibleNamesList.add(translation != null ? translation
: item.replace('_', ' ').replace('-', ' '));
String name = RendererRegistry.getRendererName(activity, item);
visibleNamesList.add(name);
}
final ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(themedContext, R.layout.dialog_text_item);

View file

@ -289,6 +289,20 @@ public class RendererRegistry {
return renderers;
}
public String getSelectedRendererName() {
RenderingRulesStorage storage = getCurrentSelectedRenderer();
if (storage == null) {
return "";
}
return RendererRegistry.getRendererName(app, storage.getName());
}
public static String getRendererName(@NonNull Context ctx, @NonNull String name) {
String translation = getTranslatedRendererName(ctx, name);
return translation != null ? translation :
name.replace('_', ' ').replace('-', ' ');
}
@Nullable
public static String getTranslatedRendererName(@NonNull Context ctx, @NonNull String key) {
switch (key) {

View file

@ -0,0 +1,156 @@
package net.osmand.plus.routing;
import android.os.Bundle;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.util.Algorithms;
public class RouteLineDrawInfo {
private static final String LINE_COLOR = "line_color";
private static final String LINE_WIDTH = "line_width";
private static final String NAVIGATION_ICON_ID = "navigation_icon_id";
private static final String NAVIGATION_ICON_COLOR = "navigation_icon_color";
private static final String CENTER_X = "center_x";
private static final String CENTER_Y = "center_y";
private static final String SCREEN_HEIGHT = "screen_height";
// parameters to save
@ColorInt
private Integer color;
private String width;
// temporally parameters to show in preview
@ColorInt
private int iconColor;
private int iconId;
private int centerX;
private int centerY;
private int screenHeight;
public RouteLineDrawInfo(@Nullable @ColorInt Integer color,
@Nullable String width) {
this.color = color;
this.width = width;
}
public RouteLineDrawInfo(@NonNull Bundle bundle) {
readBundle(bundle);
}
public RouteLineDrawInfo(@NonNull RouteLineDrawInfo existed) {
this.color = existed.color;
this.width = existed.width;
this.iconId = existed.iconId;
this.iconColor = existed.iconColor;
this.centerX = existed.centerX;
this.centerY = existed.centerY;
this.screenHeight = existed.screenHeight;
}
public void setColor(@Nullable Integer color) {
this.color = color;
}
public void setWidth(@Nullable String width) {
this.width = width;
}
public void setIconId(int iconId) {
this.iconId = iconId;
}
public void setIconColor(int iconColor) {
this.iconColor = iconColor;
}
public void setCenterX(int centerX) {
this.centerX = centerX;
}
public void setCenterY(int centerY) {
this.centerY = centerY;
}
public void setScreenHeight(int screenHeight) {
this.screenHeight = screenHeight;
}
@Nullable
public Integer getColor() {
return color;
}
@Nullable
public String getWidth() {
return width;
}
public int getIconId() {
return iconId;
}
@ColorInt
public int getIconColor() {
return iconColor;
}
public int getCenterX() {
return centerX;
}
public int getCenterY() {
return centerY;
}
public int getScreenHeight() {
return screenHeight;
}
private void readBundle(@NonNull Bundle bundle) {
if (bundle.containsKey(LINE_COLOR)) {
color = bundle.getInt(LINE_COLOR);
}
width = bundle.getString(LINE_WIDTH);
iconId = bundle.getInt(NAVIGATION_ICON_ID);
iconColor = bundle.getInt(NAVIGATION_ICON_COLOR);
centerX = bundle.getInt(CENTER_X);
centerY = bundle.getInt(CENTER_Y);
screenHeight = bundle.getInt(SCREEN_HEIGHT);
}
public void saveToBundle(@NonNull Bundle bundle) {
if (color != null) {
bundle.putInt(LINE_COLOR, color);
}
if (width != null) {
bundle.putString(LINE_WIDTH, width);
}
bundle.putInt(NAVIGATION_ICON_ID, iconId);
bundle.putInt(NAVIGATION_ICON_COLOR, iconColor);
bundle.putInt(CENTER_X, centerX);
bundle.putInt(CENTER_Y, centerY);
bundle.putInt(SCREEN_HEIGHT, screenHeight);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof RouteLineDrawInfo)) return false;
RouteLineDrawInfo that = (RouteLineDrawInfo) o;
if (!Algorithms.objectEquals(getColor(), that.getColor())) return false;
return Algorithms.objectEquals(getWidth(), that.getWidth());
}
@Override
public int hashCode() {
int result = color != null ? color.hashCode() : 0;
result = 31 * result + (width != null ? width.hashCode() : 0);
return result;
}
}

View file

@ -55,6 +55,8 @@ public class TransportRoutingHelper {
private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(TransportRoutingHelper.class);
public static final String PUBLIC_TRANSPORT_KEY = "public_transport";
private List<WeakReference<IRouteInformationListener>> listeners = new LinkedList<>();
private final OsmandApplication app;

View file

@ -0,0 +1,332 @@
package net.osmand.plus.routing.cards;
import android.content.Context;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import net.osmand.AndroidUtils;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.ColorDialogs;
import net.osmand.plus.helpers.enums.DayNightMode;
import net.osmand.plus.render.RendererRegistry;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
import net.osmand.plus.routing.RouteLineDrawInfo;
import net.osmand.plus.settings.backend.ListStringPreference;
import net.osmand.plus.track.AppearanceViewHolder;
import net.osmand.plus.track.ColorsCard;
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
import net.osmand.plus.widgets.MultiStateToggleButton;
import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener;
import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class RouteLineColorCard extends BaseCard implements CardListener, ColorPickerListener {
private static final int DAY_TITLE_ID = R.string.day;
private static final int NIGHT_TITLE_ID = R.string.night;
private final Fragment targetFragment;
private ColorsCard colorsCard;
private ColorTypeAdapter colorAdapter;
private RecyclerView groupRecyclerView;
private TextView tvColorName;
private TextView tvDescription;
private View themeToggleContainer;
private ViewGroup cardsContainer;
private ColorMode selectedMode;
private RouteLineDrawInfo routeLineDrawInfo;
private DayNightMode initMapTheme;
private DayNightMode selectedMapTheme;
private enum ColorMode {
DEFAULT(R.string.map_widget_renderer, R.drawable.ic_action_map_style),
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings);
ColorMode(int titleId, int iconId) {
this.titleId = titleId;
this.iconId = iconId;
}
int titleId;
int iconId;
}
public RouteLineColorCard(@NonNull MapActivity mapActivity,
@NonNull Fragment targetFragment,
@NonNull RouteLineDrawInfo routeLineDrawInfo,
@NonNull DayNightMode initMapTheme,
@NonNull DayNightMode selectedMapTheme) {
super(mapActivity);
this.targetFragment = targetFragment;
this.routeLineDrawInfo = routeLineDrawInfo;
this.initMapTheme = initMapTheme;
this.selectedMapTheme = selectedMapTheme;
}
@Override
public int getCardLayoutId() {
return R.layout.route_line_color_card;
}
@Override
protected void updateContent() {
tvColorName = view.findViewById(R.id.color_name);
tvDescription = view.findViewById(R.id.description);
colorAdapter = new ColorTypeAdapter();
groupRecyclerView = view.findViewById(R.id.recycler_view);
groupRecyclerView.setAdapter(colorAdapter);
groupRecyclerView.setLayoutManager(new LinearLayoutManager(app, RecyclerView.HORIZONTAL, false));
themeToggleContainer = view.findViewById(R.id.theme_toggle_container);
LinearLayout radioGroup = (LinearLayout) view.findViewById(R.id.custom_radio_buttons);
setupRadioGroup(radioGroup);
cardsContainer = (ViewGroup) view.findViewById(R.id.colors_card_container);
createColorSelector(cardsContainer);
initSelectedMode();
}
private void initSelectedMode() {
selectedMode = routeLineDrawInfo.getColor() == null ? ColorMode.DEFAULT : ColorMode.CUSTOM;
modeChanged();
}
private void modeChanged() {
if (selectedMode == ColorMode.DEFAULT) {
themeToggleContainer.setVisibility(View.GONE);
cardsContainer.setVisibility(View.GONE);
changeMapTheme(initMapTheme);
} else {
themeToggleContainer.setVisibility(View.VISIBLE);
cardsContainer.setVisibility(View.VISIBLE);
changeMapTheme(isNightMap() ? DayNightMode.NIGHT : DayNightMode.DAY);
}
updateSelectedColor();
updateDescription();
}
private void setupRadioGroup(LinearLayout buttonsContainer) {
RadioItem day = createMapThemeButton(false);
RadioItem night = createMapThemeButton(true);
MultiStateToggleButton radioGroup = new MultiStateToggleButton(app, buttonsContainer, nightMode);
radioGroup.setItems(day, night);
radioGroup.setSelectedItem(!isNightMap() ? day : night);
}
private RadioItem createMapThemeButton(final boolean isNight) {
RadioItem item = new RadioItem(app.getString(!isNight ? DAY_TITLE_ID : NIGHT_TITLE_ID));
item.setOnClickListener(new OnRadioItemClickListener() {
@Override
public boolean onRadioItemClick(RadioItem radioItem, View view) {
selectedMapTheme = isNight ? DayNightMode.NIGHT : DayNightMode.DAY;
changeMapTheme(selectedMapTheme);
updateDescription();
return true;
}
});
return item;
}
private void changeMapTheme(DayNightMode mapTheme) {
if (targetFragment instanceof OnMapThemeUpdateListener) {
((OnMapThemeUpdateListener) targetFragment).onMapThemeUpdated(mapTheme);
}
}
private void createColorSelector(ViewGroup container) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
List<Integer> colors = new ArrayList<>();
for (int color : ColorDialogs.pallette) {
colors.add(color);
}
Integer selectedColor = routeLineDrawInfo.getColor();
if (selectedColor != null) {
if (!ColorDialogs.isPaletteColor(selectedColor)) {
colors.add(selectedColor);
}
} else {
selectedColor = colors.get(0);
}
ListStringPreference preference = app.getSettings().CUSTOM_ROUTE_LINE_COLORS;
colorsCard = new ColorsCard(mapActivity, selectedColor, targetFragment, colors, preference, null);
colorsCard.setListener(this);
container.addView(colorsCard.build(mapActivity));
}
}
@Override
public void onColorSelected(Integer prevColor, int newColor) {
colorsCard.onColorSelected(prevColor, newColor);
updateSelectedColor();
}
private void updateSelectedColor() {
Integer color = selectedMode == ColorMode.CUSTOM ? colorsCard.getSelectedColor() : null;
routeLineDrawInfo.setColor(color);
updateColorName();
if (targetFragment instanceof OnSelectedColorChangeListener) {
((OnSelectedColorChangeListener) targetFragment).onSelectedColorChanged();
}
}
private void updateColorName() {
if (selectedMode == ColorMode.DEFAULT) {
tvColorName.setText(app.getString(R.string.map_widget_renderer));
} else if (routeLineDrawInfo.getColor() != null) {
int colorNameId = ColorDialogs.getColorName(routeLineDrawInfo.getColor());
tvColorName.setText(app.getString(colorNameId));
}
}
private void updateDescription() {
String description;
if (selectedMode == ColorMode.DEFAULT) {
String pattern = app.getString(R.string.route_line_use_map_style_appearance);
String color = app.getString(R.string.shared_string_color).toLowerCase();
description = String.format(pattern, color, app.getRendererRegistry().getSelectedRendererName());
} else {
String pattern = app.getString(R.string.specify_color_for_map_mode);
String mapModeTitle = app.getString(isNightMap() ? NIGHT_TITLE_ID : DAY_TITLE_ID);
description = String.format(pattern, mapModeTitle.toLowerCase());
}
tvDescription.setText(description);
}
private boolean isNightMap() {
return selectedMapTheme.isNight();
}
@Override
public void onCardLayoutNeeded(@NonNull BaseCard card) {
}
@Override
public void onCardPressed(@NonNull BaseCard card) {
if (card instanceof ColorsCard) {
updateSelectedColor();
}
}
@Override
public void onCardButtonPressed(@NonNull BaseCard card, int buttonIndex) {
}
private class ColorTypeAdapter extends RecyclerView.Adapter<AppearanceViewHolder> {
private List<ColorMode> items = Arrays.asList(ColorMode.values());
@NonNull
@Override
public AppearanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater themedInflater = UiUtilities.getInflater(parent.getContext(), nightMode);
View view = themedInflater.inflate(R.layout.point_editor_group_select_item, parent, false);
view.getLayoutParams().width = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_width);
view.getLayoutParams().height = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_height);
AppearanceViewHolder holder = new AppearanceViewHolder(view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AndroidUtils.setBackground(app, holder.button, nightMode, R.drawable.ripple_solid_light_6dp,
R.drawable.ripple_solid_dark_6dp);
}
return holder;
}
@Override
public void onBindViewHolder(@NonNull final AppearanceViewHolder holder, int position) {
ColorMode item = items.get(position);
holder.title.setText(app.getString(item.titleId));
updateButtonBg(holder, item);
updateTextAndIconColor(holder, item);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
selectedMode = items.get(holder.getAdapterPosition());
notifyItemRangeChanged(0, getItemCount());
modeChanged();
CardListener listener = getListener();
if (listener != null) {
listener.onCardPressed(RouteLineColorCard.this);
}
}
});
}
private void updateButtonBg(AppearanceViewHolder holder, ColorMode item) {
GradientDrawable rectContourDrawable = (GradientDrawable) AppCompatResources.getDrawable(app, R.drawable.bg_select_group_button_outline);
if (rectContourDrawable != null) {
if (selectedMode == item) {
int strokeColor = ContextCompat.getColor(app, nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light);
rectContourDrawable.setStroke(AndroidUtils.dpToPx(app, 2), strokeColor);
} else {
int strokeColor = ContextCompat.getColor(app, nightMode ?
R.color.stroked_buttons_and_links_outline_dark :
R.color.stroked_buttons_and_links_outline_light);
rectContourDrawable.setStroke(AndroidUtils.dpToPx(app, 1), strokeColor);
}
holder.button.setImageDrawable(rectContourDrawable);
}
}
private void updateTextAndIconColor(AppearanceViewHolder holder, ColorMode item) {
Context ctx = holder.itemView.getContext();
int iconColorId;
int textColorId;
if (selectedMode == item) {
iconColorId = AndroidUtils.getColorFromAttr(ctx, R.attr.default_icon_color);
textColorId = AndroidUtils.getColorFromAttr(ctx, android.R.attr.textColor);
} else {
iconColorId = AndroidUtils.getColorFromAttr(ctx, R.attr.colorPrimary);
textColorId = iconColorId;
}
holder.icon.setImageDrawable(app.getUIUtilities().getPaintedIcon(item.iconId, iconColorId));
holder.title.setTextColor(textColorId);
}
@Override
public int getItemCount() {
return items.size();
}
}
public interface OnSelectedColorChangeListener {
void onSelectedColorChanged();
}
public interface OnMapThemeUpdateListener {
void onMapThemeUpdated(@NonNull DayNightMode mapTheme);
}
}

View file

@ -0,0 +1,305 @@
package net.osmand.plus.routing.cards;
import android.content.Context;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.github.ksoichiro.android.observablescrollview.ScrollUtils;
import com.google.android.material.slider.Slider;
import net.osmand.AndroidUtils;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.render.RendererRegistry;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.routing.RouteLineDrawInfo;
import net.osmand.plus.track.AppearanceViewHolder;
import net.osmand.plus.track.TrackAppearanceFragment.OnNeedScrollListener;
import net.osmand.util.Algorithms;
import java.util.Arrays;
import java.util.List;
public class RouteLineWidthCard extends BaseCard {
private final static int CUSTOM_WIDTH_MIN = 1;
private final static int CUSTOM_WIDTH_MAX = 24;
private RouteLineDrawInfo routeLineDrawInfo;
private OnNeedScrollListener onNeedScrollListener;
private WidthMode selectedMode;
private WidthAdapter widthAdapter;
private View sliderContainer;
private RecyclerView groupRecyclerView;
private TextView tvModeType;
private TextView tvDescription;
private enum WidthMode {
DEFAULT(R.string.map_widget_renderer, R.drawable.ic_action_map_style, null),
THIN(R.string.rendering_value_thin_name, R.drawable.ic_action_track_line_thin_color, "thin"),
MEDIUM(R.string.rendering_value_medium_name, R.drawable.ic_action_track_line_medium_color, "medium"),
THICK(R.string.rendering_value_bold_name, R.drawable.ic_action_track_line_bold_color, "bold"),
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings, null);
WidthMode(int titleId, int iconId, String widthKey) {
this.titleId = titleId;
this.iconId = iconId;
this.widthKey = widthKey;
}
int titleId;
int iconId;
String widthKey;
}
public RouteLineWidthCard(@NonNull MapActivity mapActivity,
@NonNull RouteLineDrawInfo routeLineDrawInfo,
@NonNull OnNeedScrollListener onNeedScrollListener) {
super(mapActivity);
this.routeLineDrawInfo = routeLineDrawInfo;
this.onNeedScrollListener = onNeedScrollListener;
}
@Override
public int getCardLayoutId() {
return R.layout.route_line_width_card;
}
@Override
protected void updateContent() {
widthAdapter = new WidthAdapter();
groupRecyclerView = view.findViewById(R.id.recycler_view);
groupRecyclerView.setAdapter(widthAdapter);
groupRecyclerView.setLayoutManager(new LinearLayoutManager(app, RecyclerView.HORIZONTAL, false));
tvModeType = view.findViewById(R.id.width_type);
tvDescription = view.findViewById(R.id.description);
sliderContainer = view.findViewById(R.id.slider_container);
AndroidUiHelper.updateVisibility(view.findViewById(R.id.top_divider), isShowDivider());
initSelectedMode();
}
private void initSelectedMode() {
selectedMode = findAppropriateMode(getRouteLineWidth());
modeChanged();
}
private void modeChanged() {
updateHeader();
updateDescription();
updateCustomWidthSlider();
scrollMenuToSelectedItem();
}
public void updateItems() {
if (widthAdapter != null) {
widthAdapter.notifyDataSetChanged();
}
}
private void setRouteLineWidth(String widthKey) {
routeLineDrawInfo.setWidth(widthKey);
mapActivity.refreshMap();
}
private String getRouteLineWidth() {
return routeLineDrawInfo.getWidth();
}
private void updateHeader() {
tvModeType.setText(app.getString(selectedMode.titleId));
}
private void updateDescription() {
if (selectedMode == WidthMode.DEFAULT) {
String pattern = app.getString(R.string.route_line_use_map_style_appearance);
String width = app.getString(R.string.shared_string_color).toLowerCase();
String description = String.format(pattern, width, app.getRendererRegistry().getSelectedRendererName());
tvDescription.setText(description);
tvDescription.setVisibility(View.VISIBLE);
} else {
tvDescription.setVisibility(View.GONE);
}
}
private void updateCustomWidthSlider() {
if (selectedMode == WidthMode.CUSTOM) {
Slider slider = view.findViewById(R.id.width_slider);
final TextView tvCustomWidth = view.findViewById(R.id.width_value_tv);
slider.setValueTo(CUSTOM_WIDTH_MAX);
slider.setValueFrom(CUSTOM_WIDTH_MIN);
((TextView) view.findViewById(R.id.width_value_min)).setText(String.valueOf(CUSTOM_WIDTH_MIN));
((TextView) view.findViewById(R.id.width_value_max)).setText(String.valueOf(CUSTOM_WIDTH_MAX));
String widthKey = getRouteLineWidth();
int width = Algorithms.parseIntSilently(widthKey, 1);
widthKey = String.valueOf(width);
setRouteLineWidth(widthKey);
tvCustomWidth.setText(widthKey);
slider.setValue(width);
slider.addOnChangeListener(new Slider.OnChangeListener() {
@Override
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
if (fromUser) {
String newWidth = String.valueOf((int) value);
setRouteLineWidth(newWidth);
tvCustomWidth.setText(newWidth);
}
}
});
UiUtilities.setupSlider(slider, nightMode, null, true);
ScrollUtils.addOnGlobalLayoutListener(sliderContainer, new Runnable() {
@Override
public void run() {
if (sliderContainer.getVisibility() == View.VISIBLE) {
onNeedScrollListener.onVerticalScrollNeeded(sliderContainer.getBottom());
}
}
});
AndroidUiHelper.updateVisibility(sliderContainer, true);
} else {
AndroidUiHelper.updateVisibility(sliderContainer, false);
}
}
private void scrollMenuToSelectedItem() {
int position = widthAdapter.getItemPosition(selectedMode);
if (position != -1) {
groupRecyclerView.scrollToPosition(position);
}
}
private static WidthMode findAppropriateMode(@Nullable String widthKey) {
if (widthKey != null) {
for (WidthMode mode : WidthMode.values()) {
if (mode.widthKey != null && mode.widthKey.equals(widthKey)) {
return mode;
}
}
return WidthMode.CUSTOM;
}
return WidthMode.DEFAULT;
}
private class WidthAdapter extends RecyclerView.Adapter<AppearanceViewHolder> {
private final List<WidthMode> items = Arrays.asList(WidthMode.values());
@NonNull
@Override
public AppearanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater themedInflater = UiUtilities.getInflater(parent.getContext(), nightMode);
View view = themedInflater.inflate(R.layout.point_editor_group_select_item, parent, false);
view.getLayoutParams().width = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_width);
view.getLayoutParams().height = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_height);
AppearanceViewHolder holder = new AppearanceViewHolder(view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AndroidUtils.setBackground(app, holder.button, nightMode, R.drawable.ripple_solid_light_6dp,
R.drawable.ripple_solid_dark_6dp);
}
return holder;
}
@Override
public void onBindViewHolder(@NonNull final AppearanceViewHolder holder, int position) {
WidthMode item = items.get(position);
holder.title.setText(app.getString(item.titleId));
updateButtonBg(holder, item);
updateTextAndIconColor(holder, item);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int prevSelectedPosition = getItemPosition(selectedMode);
selectedMode = items.get(holder.getAdapterPosition());
notifyItemChanged(holder.getAdapterPosition());
notifyItemChanged(prevSelectedPosition);
if (selectedMode != WidthMode.CUSTOM) {
setRouteLineWidth(selectedMode.widthKey);
}
modeChanged();
CardListener listener = getListener();
if (listener != null) {
listener.onCardPressed(RouteLineWidthCard.this);
}
}
});
}
private void updateTextAndIconColor(AppearanceViewHolder holder, WidthMode item) {
Context ctx = holder.itemView.getContext();
int iconColor;
int textColorId;
if (selectedMode == item) {
iconColor = getIconColor(item, AndroidUtils.getColorFromAttr(ctx, R.attr.default_icon_color));
textColorId = AndroidUtils.getColorFromAttr(ctx, android.R.attr.textColor);
} else {
iconColor = getIconColor(item, AndroidUtils.getColorFromAttr(ctx, R.attr.colorPrimary));
textColorId = AndroidUtils.getColorFromAttr(ctx, R.attr.colorPrimary);
}
holder.icon.setImageDrawable(app.getUIUtilities().getPaintedIcon(item.iconId, iconColor));
holder.title.setTextColor(textColorId);
}
private int getIconColor(@NonNull WidthMode mode, @ColorInt int defaultColor) {
return mode.widthKey != null ? getRouteLineColor() : defaultColor;
}
private int getRouteLineColor() {
Integer color = routeLineDrawInfo.getColor();
return color != null ? color :
mapActivity.getMapLayers().getRouteLayer().getRouteLineColor(nightMode);
}
private void updateButtonBg(AppearanceViewHolder holder, WidthMode item) {
GradientDrawable rectContourDrawable = (GradientDrawable) AppCompatResources.getDrawable(app, R.drawable.bg_select_group_button_outline);
if (rectContourDrawable != null) {
if (selectedMode == item) {
int strokeColor = ContextCompat.getColor(app, nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light);
rectContourDrawable.setStroke(AndroidUtils.dpToPx(app, 2), strokeColor);
} else {
int strokeColor = ContextCompat.getColor(app, nightMode ?
R.color.stroked_buttons_and_links_outline_dark
: R.color.stroked_buttons_and_links_outline_light);
rectContourDrawable.setStroke(AndroidUtils.dpToPx(app, 1), strokeColor);
}
holder.button.setImageDrawable(rectContourDrawable);
}
}
@Override
public int getItemCount() {
return items.size();
}
int getItemPosition(WidthMode widthMode) {
return items.indexOf(widthMode);
}
}
}

View file

@ -84,6 +84,7 @@ import java.util.StringTokenizer;
import static net.osmand.aidlapi.OsmAndCustomizationConstants.CONFIGURE_MAP_ITEM_ID_SCHEME;
import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_ITEM_ID_SCHEME;
import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_ACTIONS;
import static net.osmand.plus.routing.TransportRoutingHelper.PUBLIC_TRANSPORT_KEY;
public class OsmandSettings {
@ -998,7 +999,7 @@ public class OsmandSettings {
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.CAR, "car");
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.BICYCLE, "bicycle");
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.PEDESTRIAN, "pedestrian");
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.PUBLIC_TRANSPORT, "public_transport");
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.PUBLIC_TRANSPORT, PUBLIC_TRANSPORT_KEY);
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.BOAT, "boat");
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.AIRCRAFT, "STRAIGHT_LINE_MODE");
ROUTING_PROFILE.setModeDefaultValue(ApplicationMode.SKI, "ski");
@ -2690,6 +2691,9 @@ public class OsmandSettings {
public final CommonPreference<Float> ROUTE_RECALCULATION_DISTANCE = new FloatPreference(this, "routing_recalc_distance", 0.f).makeProfile();
public final CommonPreference<Float> ROUTE_STRAIGHT_ANGLE = new FloatPreference(this, "routing_straight_angle", 30.f).makeProfile();
public final ListStringPreference CUSTOM_ROUTE_LINE_COLORS = (ListStringPreference) new ListStringPreference(this, "custom_route_line_colors", null, ",").makeShared().makeGlobal();
public final CommonPreference<Integer> ROUTE_LINE_COLOR = new IntPreference(this, "route_line_color", 0).makeProfile();
public final CommonPreference<String> ROUTE_LINE_WIDTH = new StringPreference(this, "route_line_width", null).makeProfile();
public final OsmandPreference<Boolean> USE_OSM_LIVE_FOR_ROUTING = new BooleanPreference(this, "enable_osmc_routing", true).makeProfile();

View file

@ -685,6 +685,11 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl
ContextCompat.getColor(app, nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light);
}
@ColorRes
protected int getActiveColorRes() {
return isNightMode() ? R.color.active_color_primary_dark : R.color.active_color_primary_light;
}
@ColorRes
protected int getBackgroundColorRes() {
return isNightMode() ? R.color.list_background_color_dark : R.color.list_background_color_light;

View file

@ -54,10 +54,13 @@ import net.osmand.plus.profiles.SelectProfileBottomSheet.DialogMode;
import net.osmand.plus.profiles.SelectProfileBottomSheet.OnSelectProfileCallback;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
import net.osmand.plus.routing.RouteLineDrawInfo;
import net.osmand.plus.routing.RouteService;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.settings.backend.backup.ProfileSettingsItem;
import net.osmand.plus.settings.backend.backup.SettingsHelper;
import net.osmand.plus.settings.fragments.RouteLineAppearanceFragment.OnApplyRouteLineListener;
import net.osmand.plus.track.ColorsCard;
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
import net.osmand.plus.widgets.FlowLayout;
@ -74,8 +77,9 @@ import java.util.List;
import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SETTINGS_ID;
import static net.osmand.plus.profiles.SelectProfileBottomSheet.PROFILES_LIST_UPDATED_ARG;
import static net.osmand.plus.profiles.SelectProfileBottomSheet.PROFILE_KEY_ARG;
import static net.osmand.plus.routing.TransportRoutingHelper.PUBLIC_TRANSPORT_KEY;
public class ProfileAppearanceFragment extends BaseSettingsFragment implements OnSelectProfileCallback, CardListener, ColorPickerListener {
public class ProfileAppearanceFragment extends BaseSettingsFragment implements OnSelectProfileCallback, CardListener, ColorPickerListener, OnApplyRouteLineListener {
private static final Log LOG = PlatformUtil.getLog(ProfileAppearanceFragment.class);
@ -91,6 +95,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
private static final String LOCATION_ICON_ITEMS = "location_icon_items";
private static final String SELECT_NAV_ICON = "select_nav_icon";
private static final String NAV_ICON_ITEMS = "nav_icon_items";
private static final String CUSTOMIZE_ROUTE_LINE = "customize_route_line";
private static final String PROFILE_NAME_KEY = "profile_name_key";
private static final String PROFILE_STRINGKEY_KEY = "profile_stringkey_key";
@ -159,6 +164,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
changedProfile.routeService = profile.routeService;
changedProfile.locationIcon = profile.locationIcon;
changedProfile.navigationIcon = profile.navigationIcon;
changedProfile.routeLineDrawInfo = profile.routeLineDrawInfo;
isNewProfile = ApplicationMode.valueOfStringKey(changedProfile.stringKey, null) == null;
}
requireMyActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@ -179,6 +185,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
profile.routeService = baseModeForNewProfile.getRouteService();
profile.locationIcon = baseModeForNewProfile.getLocationIcon();
profile.navigationIcon = baseModeForNewProfile.getNavigationIcon();
profile.routeLineDrawInfo = createRouteLineDrawInfo(baseModeForNewProfile);
}
@Override
@ -239,6 +246,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
findPreference(SELECT_ICON).setVisible(false);
findPreference(ICON_ITEMS).setVisible(false);
}
updateRouteLinePreference();
}
@SuppressLint("InlinedApi")
@ -318,6 +326,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
outState.putBoolean(IS_BASE_PROFILE_IMPORTED, isBaseProfileImported);
outState.putSerializable(PROFILE_LOCATION_ICON_KEY, changedProfile.locationIcon);
outState.putSerializable(PROFILE_NAVIGATION_ICON_KEY, changedProfile.navigationIcon);
changedProfile.routeLineDrawInfo.saveToBundle(outState);
}
private void restoreState(Bundle savedInstanceState) {
@ -331,6 +340,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
isBaseProfileImported = savedInstanceState.getBoolean(IS_BASE_PROFILE_IMPORTED);
changedProfile.locationIcon = (LocationIcon) savedInstanceState.getSerializable(PROFILE_LOCATION_ICON_KEY);
changedProfile.navigationIcon = (NavigationIcon) savedInstanceState.getSerializable(PROFILE_NAVIGATION_ICON_KEY);
changedProfile.routeLineDrawInfo = new RouteLineDrawInfo(savedInstanceState);
isNewProfile = savedInstanceState.getBoolean(IS_NEW_PROFILE_KEY);
}
@ -709,6 +719,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
changedProfile.routingProfile = changedProfile.parent.getRoutingProfile();
changedProfile.routeService = changedProfile.parent.getRouteService();
this.isBaseProfileImported = isBaseProfileImported;
updateRouteLinePreference();
}
private void setupBaseProfileView(String stringKey) {
@ -716,6 +727,14 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
baseProfileName.setText(Algorithms.capitalizeFirstLetter(mode.toHumanString()));
}
private void updateRouteLinePreference() {
Preference preference = findPreference(CUSTOMIZE_ROUTE_LINE);
boolean isDefaultProfile = getSelectedAppMode().equals(ApplicationMode.DEFAULT) && !isNewProfile;
boolean isPublicTransport = PUBLIC_TRANSPORT_KEY.equals(changedProfile.routingProfile);
preference.setVisible(!isDefaultProfile && !isPublicTransport);
preference.setIcon(getIcon(R.drawable.ic_action_route_distance, getActiveColorRes()));
}
private boolean checkProfileName() {
if (Algorithms.isBlank(changedProfile.name)) {
Activity activity = getActivity();
@ -756,6 +775,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
mode.setCustomIconColor(changedProfile.customColor);
mode.setLocationIcon(changedProfile.locationIcon);
mode.setNavigationIcon(changedProfile.navigationIcon);
saveRouteLineAppearance(mode, changedProfile.routeLineDrawInfo);
FragmentActivity activity = getActivity();
if (activity != null) {
@ -783,6 +803,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
if (!ApplicationMode.values(app).contains(mode)) {
ApplicationMode.changeProfileAvailability(mode, true, app);
}
saveRouteLineAppearance(mode, changedProfile.routeLineDrawInfo);
return mode;
}
@ -967,6 +988,21 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
}
}
@Override
public boolean onPreferenceClick(Preference preference) {
String prefId = preference.getKey();
if (CUSTOMIZE_ROUTE_LINE.equals(prefId)) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
RouteLineDrawInfo drawInfo = changedProfile.routeLineDrawInfo;
drawInfo.setIconId(changedProfile.navigationIcon.getIconId());
drawInfo.setIconColor(changedProfile.getActualColor());
RouteLineAppearanceFragment.showInstance(mapActivity, drawInfo, this);
}
}
return super.onPreferenceClick(preference);
}
@Override
public void onCardButtonPressed(@NonNull BaseCard card, int buttonIndex) {
}
@ -977,6 +1013,29 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
this.onCardPressed(colorsCard);
}
@Override
public void applyRouteLineAppearance(@NonNull RouteLineDrawInfo routeLineDrawInfo) {
changedProfile.routeLineDrawInfo = routeLineDrawInfo;
}
private RouteLineDrawInfo createRouteLineDrawInfo(@NonNull ApplicationMode appMode) {
int storedValue = settings.ROUTE_LINE_COLOR.getModeValue(appMode);
Integer color = storedValue != 0 ? storedValue : null;
String widthKey = settings.ROUTE_LINE_WIDTH.getModeValue(appMode);
return new RouteLineDrawInfo(color, widthKey);
}
private void saveRouteLineAppearance(@NonNull ApplicationMode appMode,
@NonNull RouteLineDrawInfo drawInfo) {
Integer color = drawInfo.getColor();
if (color != null) {
settings.ROUTE_LINE_COLOR.setModeValue(appMode, color);
} else {
settings.ROUTE_LINE_COLOR.resetModeToDefault(appMode);
}
settings.ROUTE_LINE_WIDTH.setModeValue(appMode, drawInfo.getWidth());
}
public static boolean showInstance(FragmentActivity activity, SettingsScreenType screenType, @Nullable String appMode, boolean imported) {
try {
Fragment fragment = Fragment.instantiate(activity, screenType.fragmentName);
@ -1008,6 +1067,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
RouteService routeService;
NavigationIcon navigationIcon;
LocationIcon locationIcon;
RouteLineDrawInfo routeLineDrawInfo;
@ColorInt
public int getActualColor() {
@ -1044,6 +1104,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
return false;
if (routeService != that.routeService) return false;
if (navigationIcon != that.navigationIcon) return false;
if (routeLineDrawInfo != null ? !routeLineDrawInfo.equals(that.routeLineDrawInfo) : that.routeLineDrawInfo != null) return false;
return locationIcon == that.locationIcon;
}
@ -1059,6 +1120,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
result = 31 * result + (routeService != null ? routeService.hashCode() : 0);
result = 31 * result + (navigationIcon != null ? navigationIcon.hashCode() : 0);
result = 31 * result + (locationIcon != null ? locationIcon.hashCode() : 0);
result = 31 * result + (routeLineDrawInfo != null ? routeLineDrawInfo.hashCode() : 0);
return result;
}
}

View file

@ -0,0 +1,431 @@
package net.osmand.plus.settings.fragments;
import android.os.Build;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import net.osmand.AndroidUtils;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.UiUtilities.DialogButtonType;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.ContextMenuScrollFragment;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.enums.DayNightMode;
import net.osmand.plus.routing.RouteLineDrawInfo;
import net.osmand.plus.routing.cards.RouteLineColorCard;
import net.osmand.plus.routing.cards.RouteLineColorCard.OnMapThemeUpdateListener;
import net.osmand.plus.routing.cards.RouteLineColorCard.OnSelectedColorChangeListener;
import net.osmand.plus.routing.cards.RouteLineWidthCard;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
import net.osmand.plus.track.TrackAppearanceFragment.OnNeedScrollListener;
public class RouteLineAppearanceFragment extends ContextMenuScrollFragment implements ColorPickerListener, OnSelectedColorChangeListener, OnMapThemeUpdateListener {
public static final String TAG = RouteLineAppearanceFragment.class.getName();
private static final String INIT_MAP_THEME = "init_map_theme";
private static final String SELECTED_MAP_THEME = "selected_map_theme";
private RouteLineDrawInfo routeLineDrawInfo;
private int toolbarHeightPx;
private DayNightMode initMapTheme;
private DayNightMode selectedMapTheme;
private View buttonsShadow;
private View controlButtons;
private View toolbarContainer;
private RouteLineColorCard colorCard;
private RouteLineWidthCard widthCard;
@Override
public int getMainLayoutId() {
return R.layout.route_line_appearance;
}
@Override
public int getHeaderViewHeight() {
return 0;
}
@Override
public boolean isHeaderViewDetached() {
return false;
}
@Override
public int getToolbarHeight() {
return isPortrait() ? toolbarHeightPx : 0;
}
@Override
public float getMiddleStateKoef() {
return 0.5f;
}
@Override
public int getInitialMenuState() {
return MenuState.HALF_SCREEN;
}
@Override
public int getSupportedMenuStatesPortrait() {
return MenuState.HALF_SCREEN | MenuState.FULL_SCREEN;
}
@Override
public boolean shouldShowMapControls(int menuState) {
return menuState == MenuState.HALF_SCREEN;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
toolbarHeightPx = getResources().getDimensionPixelSize(R.dimen.dashboard_map_toolbar);
if (savedInstanceState != null) {
routeLineDrawInfo = new RouteLineDrawInfo(savedInstanceState);
initMapTheme = DayNightMode.valueOf(savedInstanceState.getString(INIT_MAP_THEME));
selectedMapTheme = DayNightMode.valueOf(savedInstanceState.getString(SELECTED_MAP_THEME));
} else {
initMapTheme = getMyApplication().getSettings().DAYNIGHT_MODE.get();
selectedMapTheme = initMapTheme;
}
requireMapActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
public void handleOnBackPressed() {
dismiss();
}
});
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (view != null) {
toolbarContainer = view.findViewById(R.id.context_menu_toolbar_container);
buttonsShadow = view.findViewById(R.id.buttons_shadow);
controlButtons = view.findViewById(R.id.control_buttons);
if (isPortrait()) {
updateCardsLayout();
} else {
int widthNoShadow = getLandscapeNoShadowWidth();
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(widthNoShadow, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.BOTTOM | Gravity.START;
controlButtons.setLayoutParams(params);
}
initContent(view);
}
return view;
}
private void initContent(@NonNull View view) {
setupCards();
setupToolbar();
setupButtons(view);
setupScrollShadow();
enterAppearanceMode();
openMenuHalfScreen();
calculateLayout();
}
private void calculateLayout() {
runLayoutListener(new Runnable() {
@Override
public void run() {
updateMapControlsPos(RouteLineAppearanceFragment.this, getViewY(), true);
initVisibleRect();
}
});
}
private void setupCards() {
MapActivity mapActivity = requireMapActivity();
ViewGroup cardsContainer = getCardsContainer();
cardsContainer.removeAllViews();
colorCard = new RouteLineColorCard(mapActivity, this, routeLineDrawInfo, initMapTheme, selectedMapTheme);
cardsContainer.addView(colorCard.build(mapActivity));
widthCard = new RouteLineWidthCard(mapActivity, routeLineDrawInfo, createScrollListener());
cardsContainer.addView(widthCard.build(mapActivity));
}
private OnNeedScrollListener createScrollListener() {
return new OnNeedScrollListener() {
@Override
public void onVerticalScrollNeeded(int y) {
View view = widthCard.getView();
if (view != null) {
int resultYPosition = view.getTop() + y;
int dialogHeight = getInnerScrollableHeight();
ScrollView scrollView = (ScrollView) getBottomScrollView();
if (resultYPosition > (scrollView.getScrollY() + dialogHeight)) {
scrollView.smoothScrollTo(0, resultYPosition - dialogHeight);
}
}
}
private int getInnerScrollableHeight() {
int totalScreenHeight = getViewHeight() - getMenuStatePosY(getCurrentMenuState());
int frameTotalHeight = controlButtons.getHeight() + buttonsShadow.getHeight();
return totalScreenHeight - frameTotalHeight;
}
};
}
private void setupToolbar() {
ImageView closeButton = toolbarContainer.findViewById(R.id.close_button);
closeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
closeButton.setImageResource(AndroidUtils.getNavigationIconResId(toolbarContainer.getContext()));
updateToolbarVisibility(toolbarContainer);
}
@Override
public int getStatusBarColorId() {
View view = getView();
if (Build.VERSION.SDK_INT >= 23 && !isNightMode() && view != null) {
view.setSystemUiVisibility(view.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
return isNightMode() ? R.color.divider_color_dark : R.color.divider_color_light;
}
@Override
public float getToolbarAlpha(int y) {
return isPortrait() ? 1f : 0f;
}
private void setupButtons(View view) {
View buttonsContainer = view.findViewById(R.id.buttons_container);
buttonsContainer.setBackgroundColor(AndroidUtils.getColorFromAttr(view.getContext(), R.attr.bg_color));
View saveButton = view.findViewById(R.id.right_bottom_button);
saveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getTargetFragment() instanceof OnApplyRouteLineListener) {
((OnApplyRouteLineListener) getTargetFragment()).applyRouteLineAppearance(routeLineDrawInfo);
}
dismiss();
}
});
View cancelButton = view.findViewById(R.id.dismiss_button);
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentActivity activity = getActivity();
if (activity != null) {
activity.onBackPressed();
}
}
});
UiUtilities.setupDialogButton(isNightMode(), cancelButton, DialogButtonType.SECONDARY, R.string.shared_string_cancel);
UiUtilities.setupDialogButton(isNightMode(), saveButton, DialogButtonType.PRIMARY, R.string.shared_string_apply);
AndroidUiHelper.updateVisibility(saveButton, true);
AndroidUiHelper.updateVisibility(view.findViewById(R.id.buttons_divider), true);
}
private void setupScrollShadow() {
final View scrollView = getBottomScrollView();
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
boolean scrollToBottomAvailable = scrollView.canScrollVertically(1);
if (scrollToBottomAvailable) {
showShadowButton();
} else {
hideShadowButton();
}
}
});
}
private void showShadowButton() {
buttonsShadow.setVisibility(View.VISIBLE);
buttonsShadow.animate()
.alpha(0.8f)
.setDuration(200)
.setListener(null);
}
private void hideShadowButton() {
buttonsShadow.animate()
.alpha(0f)
.setDuration(200);
}
private void initVisibleRect() {
MapActivity ctx = getMapActivity();
int screenHeight = AndroidUtils.getScreenHeight(ctx);
int screenWidth = AndroidUtils.getScreenWidth(ctx);
int statusBarHeight = AndroidUtils.getStatusBarHeight(ctx);
int centerX;
int centerY;
if (isPortrait()) {
centerX = screenWidth / 2;
centerY = (getViewY() + toolbarContainer.getHeight() + statusBarHeight) / 2;
} else {
boolean isRtl = AndroidUtils.isLayoutRtl(ctx);
int dialogWidth = getLandscapeNoShadowWidth();
int left = isRtl ? 0 : dialogWidth;
int right = isRtl ? screenWidth - dialogWidth : screenWidth;
centerX = (left + right) / 2;
centerY = (screenHeight + statusBarHeight) / 2 ;
}
routeLineDrawInfo.setCenterX(centerX);
routeLineDrawInfo.setCenterY(centerY);
routeLineDrawInfo.setScreenHeight(screenHeight);
}
@Override
public void onResume() {
super.onResume();
setDrawInfoOnRouteLayer(routeLineDrawInfo);
}
@Override
public void onPause() {
super.onPause();
setDrawInfoOnRouteLayer(null);
}
private void setDrawInfoOnRouteLayer(@Nullable RouteLineDrawInfo drawInfo) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
mapActivity.getMapLayers().getRouteLayer().setRouteLineDrawInfo(drawInfo);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(INIT_MAP_THEME, initMapTheme.name());
outState.putString(SELECTED_MAP_THEME, selectedMapTheme.name());
routeLineDrawInfo.saveToBundle(outState);
}
@Override
public void onDestroyView() {
super.onDestroyView();
exitAppearanceMode();
}
private void enterAppearanceMode() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
boolean portrait = AndroidUiHelper.isOrientationPortrait(mapActivity);
AndroidUiHelper.setVisibility(mapActivity, portrait ? View.INVISIBLE : View.GONE,
R.id.map_left_widgets_panel,
R.id.map_right_widgets_panel,
R.id.map_center_info);
}
}
private void exitAppearanceMode() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
AndroidUiHelper.setVisibility(mapActivity, View.VISIBLE,
R.id.map_left_widgets_panel,
R.id.map_right_widgets_panel,
R.id.map_center_info,
R.id.map_search_button);
changeMapTheme(initMapTheme);
}
}
private void updateCardsLayout() {
View mainView = getMainView();
if (mainView != null) {
LinearLayout cardsContainer = getCardsContainer();
View topShadow = getTopShadow();
FrameLayout bottomContainer = getBottomContainer();
if (getCurrentMenuState() == MenuState.HEADER_ONLY) {
topShadow.setVisibility(View.INVISIBLE);
bottomContainer.setBackgroundDrawable(null);
AndroidUtils.setBackground(mainView.getContext(), cardsContainer, isNightMode(), R.drawable.travel_card_bg_light, R.drawable.travel_card_bg_dark);
} else {
topShadow.setVisibility(View.VISIBLE);
AndroidUtils.setBackground(mainView.getContext(), bottomContainer, isNightMode(), R.color.list_background_color_light, R.color.list_background_color_dark);
AndroidUtils.setBackground(mainView.getContext(), cardsContainer, isNightMode(), R.color.list_background_color_light, R.color.list_background_color_dark);
}
}
}
@Override
public void onColorSelected(Integer prevColor, int newColor) {
colorCard.onColorSelected(prevColor, newColor);
}
@Override
public void onSelectedColorChanged() {
if (widthCard != null) {
widthCard.updateItems();
}
if (getMapActivity() != null) {
getMapActivity().refreshMap();
}
}
public static boolean showInstance(@NonNull MapActivity mapActivity,
@NonNull RouteLineDrawInfo drawInfo,
@NonNull Fragment target) {
try {
RouteLineAppearanceFragment fragment = new RouteLineAppearanceFragment();
fragment.setTargetFragment(target, 0);
fragment.routeLineDrawInfo = new RouteLineDrawInfo(drawInfo);
mapActivity.getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentContainer, fragment, TAG)
.addToBackStack(TAG)
.commitAllowingStateLoss();
return true;
} catch (RuntimeException e) {
return false;
}
}
@Override
public void onMapThemeUpdated(@NonNull DayNightMode mapTheme) {
changeMapTheme(mapTheme);
}
private void changeMapTheme(@NonNull DayNightMode mapTheme) {
OsmandApplication app = getMyApplication();
if (app != null) {
app.getSettings().DAYNIGHT_MODE.set(mapTheme);
selectedMapTheme = mapTheme;
}
}
public interface OnApplyRouteLineListener {
void applyRouteLineAppearance(@NonNull RouteLineDrawInfo routeLineDrawInfo);
}
}

View file

@ -8,13 +8,13 @@ import androidx.recyclerview.widget.RecyclerView;
import net.osmand.plus.R;
public class TrackAppearanceViewHolder extends RecyclerView.ViewHolder {
public class AppearanceViewHolder extends RecyclerView.ViewHolder {
final TextView title;
final ImageView icon;
final ImageView button;
public final TextView title;
public final ImageView icon;
public final ImageView button;
TrackAppearanceViewHolder(View itemView) {
public AppearanceViewHolder(View itemView) {
super(itemView);
title = itemView.findViewById(R.id.groupName);
icon = itemView.findViewById(R.id.groupIcon);

View file

@ -6,7 +6,6 @@ import android.view.View;
import android.widget.ImageView;
import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.fragment.app.Fragment;
@ -28,12 +27,6 @@ import org.apache.commons.logging.Log;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.ColorUtils;
import androidx.fragment.app.Fragment;
public class ColorsCard extends BaseCard implements ColorPickerListener {
public static final int MAX_CUSTOM_COLORS = 6;
@ -58,7 +51,12 @@ public class ColorsCard extends BaseCard implements ColorPickerListener {
return R.layout.colors_card;
}
public ColorsCard(MapActivity mapActivity, int selectedColor, Fragment targetFragment, List<Integer> colors, ListStringPreference colorsListPreference, ApplicationMode appMode) {
public ColorsCard(MapActivity mapActivity,
int selectedColor,
Fragment targetFragment,
List<Integer> colors,
ListStringPreference colorsListPreference,
ApplicationMode appMode) {
super(mapActivity);
this.targetFragment = targetFragment;
this.selectedColor = selectedColor;

View file

@ -135,7 +135,7 @@ public class TrackColoringCard extends BaseCard {
updateHeader();
}
private class TrackColoringAdapter extends RecyclerView.Adapter<TrackAppearanceViewHolder> {
private class TrackColoringAdapter extends RecyclerView.Adapter<AppearanceViewHolder> {
private List<TrackAppearanceItem> items;
@ -145,16 +145,16 @@ public class TrackColoringCard extends BaseCard {
@NonNull
@Override
public TrackAppearanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public AppearanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater themedInflater = UiUtilities.getInflater(parent.getContext(), nightMode);
View view = themedInflater.inflate(R.layout.point_editor_group_select_item, parent, false);
view.getLayoutParams().width = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_width);
view.getLayoutParams().height = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_height);
return new TrackAppearanceViewHolder(view);
return new AppearanceViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final TrackAppearanceViewHolder holder, int position) {
public void onBindViewHolder(@NonNull final AppearanceViewHolder holder, int position) {
final TrackAppearanceItem item = items.get(position);
if (item.isActive() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@ -186,7 +186,7 @@ public class TrackColoringCard extends BaseCard {
});
}
private void updateButtonBg(TrackAppearanceViewHolder holder, TrackAppearanceItem item) {
private void updateButtonBg(AppearanceViewHolder holder, TrackAppearanceItem item) {
GradientDrawable rectContourDrawable = (GradientDrawable) AppCompatResources.getDrawable(app, R.drawable.bg_select_group_button_outline);
if (rectContourDrawable != null) {
Context ctx = holder.itemView.getContext();
@ -217,7 +217,7 @@ public class TrackColoringCard extends BaseCard {
}
}
private void updateTextAndIconColor(TrackAppearanceViewHolder holder, TrackAppearanceItem item) {
private void updateTextAndIconColor(AppearanceViewHolder holder, TrackAppearanceItem item) {
Context ctx = holder.itemView.getContext();
boolean isSelected = item.getAttrName().equals(getSelectedAppearanceItem().getAttrName());
int iconColorId;

View file

@ -184,7 +184,7 @@ public class TrackWidthCard extends BaseCard {
}
}
private class GpxWidthAdapter extends RecyclerView.Adapter<TrackAppearanceViewHolder> {
private class GpxWidthAdapter extends RecyclerView.Adapter<AppearanceViewHolder> {
private List<AppearanceListItem> items;
@ -194,13 +194,13 @@ public class TrackWidthCard extends BaseCard {
@NonNull
@Override
public TrackAppearanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public AppearanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater themedInflater = UiUtilities.getInflater(parent.getContext(), nightMode);
View view = themedInflater.inflate(R.layout.point_editor_group_select_item, parent, false);
view.getLayoutParams().width = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_width);
view.getLayoutParams().height = app.getResources().getDimensionPixelSize(R.dimen.gpx_group_button_height);
TrackAppearanceViewHolder holder = new TrackAppearanceViewHolder(view);
AppearanceViewHolder holder = new AppearanceViewHolder(view);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AndroidUtils.setBackground(app, holder.button, nightMode, R.drawable.ripple_solid_light_6dp,
R.drawable.ripple_solid_dark_6dp);
@ -209,7 +209,7 @@ public class TrackWidthCard extends BaseCard {
}
@Override
public void onBindViewHolder(@NonNull final TrackAppearanceViewHolder holder, int position) {
public void onBindViewHolder(@NonNull final AppearanceViewHolder holder, int position) {
AppearanceListItem item = items.get(position);
holder.title.setText(item.getLocalizedValue());
@ -238,7 +238,7 @@ public class TrackWidthCard extends BaseCard {
});
}
private void updateWidthIcon(TrackAppearanceViewHolder holder, AppearanceListItem item) {
private void updateWidthIcon(AppearanceViewHolder holder, AppearanceListItem item) {
int color = trackDrawInfo.getColor();
int iconId;
@ -251,7 +251,7 @@ public class TrackWidthCard extends BaseCard {
holder.icon.setImageDrawable(app.getUIUtilities().getPaintedIcon(iconId, color));
}
private void updateButtonBg(TrackAppearanceViewHolder holder, AppearanceListItem item) {
private void updateButtonBg(AppearanceViewHolder holder, AppearanceListItem item) {
GradientDrawable rectContourDrawable = (GradientDrawable) AppCompatResources.getDrawable(app, R.drawable.bg_select_group_button_outline);
if (rectContourDrawable != null) {
if (getSelectedItem() != null && getSelectedItem().equals(item)) {

View file

@ -377,6 +377,7 @@ public abstract class OsmandMapLayer {
public Paint paint;
public Paint customColorPaint;
public int customColor = 0;
public float customWidth = 0;
public int defaultWidth = 0;
public int defaultColor = 0;
public boolean isPaint2;
@ -481,8 +482,13 @@ public abstract class OsmandMapLayer {
if (isShadowPaint) {
canvas.drawPath(path, shadowPaint);
}
if (customColor != 0 || customWidth != 0) {
if (customColor != 0) {
customColorPaint.setColor(customColor);
}
if (customWidth != 0) {
customColorPaint.setStrokeWidth(customWidth);
}
canvas.drawPath(path, customColorPaint);
} else {
canvas.drawPath(path, paint);

View file

@ -872,14 +872,16 @@ public class MapControlsLayer extends OsmandMapLayer {
boolean showBottomMenuButtons = (showRouteCalculationControls || !routeFollowingMode)
&& !isInMovingMarkerMode() && !isInGpxDetailsMode() && !isInMeasurementToolMode()
&& !isInPlanRouteMode() && !shouldHideTopControls && !isInChoosingRoutesMode()
&& !isInWaypointsChoosingMode() && !isInFollowTrackMode() && !isInTrackAppearanceMode();
&& !isInWaypointsChoosingMode() && !isInFollowTrackMode() && !isInTrackAppearanceMode()
&& !isInRouteLineAppearanceMode();
routePlanningBtn.updateVisibility(showBottomMenuButtons);
menuControl.updateVisibility(showBottomMenuButtons);
boolean additionalDialogsHide = !isInGpxApproximationMode()
&& !isInTrackAppearanceMode()
&& !isInChoosingRoutesMode()
&& !isInWaypointsChoosingMode();
&& !isInWaypointsChoosingMode()
&& !isInRouteLineAppearanceMode();
boolean showZoomButtons = !routeDialogOpened && !shouldHideTopControls
&& !isInFollowTrackMode()
&& (additionalDialogsHide || !portrait);
@ -888,7 +890,8 @@ public class MapControlsLayer extends OsmandMapLayer {
boolean forceHideCompass = routeDialogOpened || trackDialogOpened || isInMeasurementToolMode()
|| isInPlanRouteMode() || shouldHideTopControls || isInChoosingRoutesMode()
|| isInTrackAppearanceMode() || isInWaypointsChoosingMode() || isInFollowTrackMode();
|| isInTrackAppearanceMode() || isInWaypointsChoosingMode() || isInFollowTrackMode()
|| isInRouteLineAppearanceMode();
compassHud.forceHideCompass = forceHideCompass;
compassHud.updateVisibility(!forceHideCompass && shouldShowCompass());
@ -899,7 +902,8 @@ public class MapControlsLayer extends OsmandMapLayer {
}
boolean showTopButtons = !routeDialogOpened && !trackDialogOpened && !shouldHideTopControls
&& !isInMeasurementToolMode() && !isInPlanRouteMode() && !isInChoosingRoutesMode()
&& !isInTrackAppearanceMode() && !isInWaypointsChoosingMode() && !isInFollowTrackMode();
&& !isInTrackAppearanceMode() && !isInWaypointsChoosingMode() && !isInFollowTrackMode()
&& !isInRouteLineAppearanceMode();
layersHud.updateVisibility(showTopButtons);
quickSearchHud.updateVisibility(showTopButtons);
@ -1029,7 +1033,8 @@ public class MapControlsLayer extends OsmandMapLayer {
&& !isInGpxApproximationMode()
&& !isInChoosingRoutesMode()
&& !isInWaypointsChoosingMode()
&& !isInFollowTrackMode();
&& !isInFollowTrackMode()
&& !isInRouteLineAppearanceMode();
backToLocationControl.updateVisibility(visible && !dialogOpened && !isInPlanRouteMode()
&& (additionalDialogsHide || !isPotrait()));
}
@ -1397,6 +1402,10 @@ public class MapControlsLayer extends OsmandMapLayer {
return MapRouteInfoMenu.waypointsVisible;
}
private boolean isInRouteLineAppearanceMode() {
return mapActivity.getMapLayers().getRouteLayer().isInRouteLineAppearanceMode();
}
private boolean isInFollowTrackMode() {
return MapRouteInfoMenu.followTrackVisible;
}

View file

@ -61,6 +61,7 @@ public class MapQuickActionLayer extends OsmandMapLayer implements QuickActionRe
private final MapMarkersLayer mapMarkersLayer;
private final MapControlsLayer mapControlsLayer;
private final GPXLayer gpxLayer;
private final RouteLayer routeLayer;
private ImageView contextMarker;
private final MapActivity mapActivity;
private final OsmandApplication app;
@ -90,6 +91,7 @@ public class MapQuickActionLayer extends OsmandMapLayer implements QuickActionRe
mapMarkersLayer = mapActivity.getMapLayers().getMapMarkersLayer();
gpxLayer = mapActivity.getMapLayers().getGpxLayer();
mapControlsLayer = mapActivity.getMapLayers().getMapControlsLayer();
routeLayer = mapActivity.getMapLayers().getRouteLayer();
}
@Override
@ -426,6 +428,7 @@ public class MapQuickActionLayer extends OsmandMapLayer implements QuickActionRe
mapMarkersLayer.isInPlanRouteMode() ||
gpxLayer.isInTrackAppearanceMode() ||
mapControlsLayer.isInTrackMenuMode() ||
routeLayer.isInRouteLineAppearanceMode() ||
mapRouteInfoMenu.isVisible() ||
MapRouteInfoMenu.chooseRoutesVisible ||
MapRouteInfoMenu.waypointsVisible ||

View file

@ -11,17 +11,21 @@ import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.drawable.DrawableCompat;
import net.osmand.AndroidUtils;
import net.osmand.Location;
import net.osmand.PlatformUtil;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.data.QuadPoint;
import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox;
import net.osmand.data.TransportStop;
@ -30,8 +34,11 @@ import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.mapcontextmenu.other.TrackChartPoints;
import net.osmand.plus.measurementtool.MeasurementToolFragment;
import net.osmand.plus.profiles.LocationIcon;
import net.osmand.plus.render.OsmandRenderer;
import net.osmand.plus.render.OsmandRenderer.RenderingContext;
import net.osmand.plus.routing.RouteCalculationResult;
import net.osmand.plus.routing.RouteDirectionInfo;
import net.osmand.plus.routing.RouteLineDrawInfo;
import net.osmand.plus.routing.RouteService;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.routing.TransportRoutingHelper;
@ -41,16 +48,29 @@ import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWay;
import net.osmand.plus.views.layers.geometry.PublicTransportGeometryWayContext;
import net.osmand.plus.views.layers.geometry.RouteGeometryWay;
import net.osmand.plus.views.layers.geometry.RouteGeometryWayContext;
import net.osmand.render.RenderingRuleProperty;
import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.TransportRouteResult;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_WIDTH_ATTR;
public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.IContextMenuProvider {
private static final Log log = PlatformUtil.getLog(RouteLayer.class);
private static final int DEFAULT_WIDTH_MULTIPLIER = 7;
private OsmandMapTileView view;
private final RoutingHelper helper;
@ -64,13 +84,16 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
private Paint paintIconAction;
private Paint paintGridOuterCircle;
private Paint paintGridCircle;
private Paint paintRouteLinePreview;
private LayerDrawable selectedPoint;
private TrackChartPoints trackChartPoints;
private RouteLineDrawInfo routeLineDrawInfo;
private RenderingLineAttributes attrs;
private RenderingLineAttributes attrsPT;
private RenderingLineAttributes attrsW;
private Map<String, Float> cachedRouteLineWidth = new HashMap<>();
private boolean nightMode;
private RouteGeometryWayContext routeWayContext;
@ -79,6 +102,7 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
private PublicTransportGeometryWay publicTransportRouteGeometry;
private LayerDrawable projectionIcon;
private LayerDrawable previewIcon;
public RouteLayer(RoutingHelper helper) {
this.helper = helper;
@ -149,6 +173,8 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
paintGridOuterCircle.setAntiAlias(true);
paintGridOuterCircle.setColor(Color.WHITE);
paintGridOuterCircle.setAlpha(204);
paintRouteLinePreview = new Paint();
}
@Override
@ -206,8 +232,8 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
}
private boolean isPlanRouteGraphsAvailable() {
if (view.getContext() instanceof MapActivity) {
MapActivity mapActivity = (MapActivity) view.getContext();
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
MeasurementToolFragment fragment = mapActivity.getMeasurementToolFragment();
if (fragment != null) {
return fragment.hasVisibleGraph();
@ -216,6 +242,24 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
return false;
}
public boolean isInRouteLineAppearanceMode() {
return routeLineDrawInfo != null;
}
public void setRouteLineDrawInfo(RouteLineDrawInfo drawInfo) {
this.routeLineDrawInfo = drawInfo;
if (drawInfo == null) {
previewIcon = null;
}
}
private MapActivity getMapActivity() {
if (view.getContext() instanceof MapActivity) {
return (MapActivity) view.getContext();
}
return null;
}
private void updateAttrs(DrawSettings settings, RotatedTileBox tileBox) {
boolean updatePaints = attrs.updatePaints(view.getApplication(), settings, tileBox);
attrs.isPaint3 = false;
@ -265,7 +309,37 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
}
@Override
public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {}
public void onDraw(Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) {
if (routeLineDrawInfo != null) {
float angle = tileBox.getRotate();
QuadPoint c = tileBox.getCenterPixelPoint();
canvas.rotate(-angle, c.x, c.y);
drawRouteLinePreview(canvas, tileBox, routeLineDrawInfo);
canvas.rotate(angle, c.x, c.y);
}
}
private void drawRouteLinePreview(Canvas canvas,
RotatedTileBox tileBox,
RouteLineDrawInfo drawInfo) {
paintRouteLinePreview.setColor(getRouteLineColor(nightMode));
paintRouteLinePreview.setStrokeWidth(getRouteLineWidth(tileBox));
int centerX = drawInfo.getCenterX();
int centerY = drawInfo.getCenterY();
int screenHeight = drawInfo.getScreenHeight();
canvas.drawLine(centerX, 0, centerX, screenHeight, paintRouteLinePreview);
if (previewIcon == null) {
previewIcon = (LayerDrawable) AppCompatResources.getDrawable(view.getContext(), drawInfo.getIconId());
DrawableCompat.setTint(previewIcon.getDrawable(1), drawInfo.getIconColor());
}
canvas.rotate(-90, centerX, centerY);
drawIcon(canvas, previewIcon, centerX, centerY);
canvas.rotate(90, centerX, centerY);
}
private void drawAction(RotatedTileBox tb, Canvas canvas, List<Location> actionPoints) {
if (actionPoints.size() > 0) {
@ -328,19 +402,76 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
}
int locationX = (int) projectionXY[0];
int locationY = (int) projectionXY[1];
drawIcon(canvas, projectionIcon, locationX, locationY);
}
projectionIcon.setBounds(locationX - projectionIcon.getIntrinsicWidth() / 2,
locationY - projectionIcon.getIntrinsicHeight() / 2,
locationX + projectionIcon.getIntrinsicWidth() / 2,
locationY + projectionIcon.getIntrinsicHeight() / 2);
projectionIcon.draw(canvas);
private static void drawIcon(Canvas canvas, Drawable drawable, int locationX, int locationY) {
drawable.setBounds(locationX - drawable.getIntrinsicWidth() / 2,
locationY - drawable.getIntrinsicHeight() / 2,
locationX + drawable.getIntrinsicWidth() / 2,
locationY + drawable.getIntrinsicHeight() / 2);
drawable.draw(canvas);
}
@ColorInt
public int getRouteLineColor(boolean night) {
Integer color;
if (routeLineDrawInfo != null) {
color = routeLineDrawInfo.getColor();
} else {
int storedValue = view.getSettings().ROUTE_LINE_COLOR.getModeValue(helper.getAppMode());
color = storedValue != 0 ? storedValue : null;
}
if (color == null) {
updateAttrs(new DrawSettings(night), view.getCurrentRotatedTileBox());
return attrs.paint.getColor();
color = attrs.paint.getColor();
}
return color;
}
private float getRouteLineWidth(@NonNull RotatedTileBox tileBox) {
String widthKey;
if (routeLineDrawInfo != null) {
widthKey = routeLineDrawInfo.getWidth();
} else {
widthKey = view.getSettings().ROUTE_LINE_WIDTH.getModeValue(helper.getAppMode());
}
return widthKey != null ? getWidthByKey(tileBox, widthKey) : attrs.paint.getStrokeWidth();
}
@Nullable
private Float getWidthByKey(RotatedTileBox tileBox, String widthKey) {
Float resultValue = cachedRouteLineWidth.get(widthKey);
if (resultValue != null) {
return resultValue;
}
if (!Algorithms.isEmpty(widthKey) && Algorithms.isInt(widthKey)) {
try {
int widthDp = Integer.parseInt(widthKey);
resultValue = (float) AndroidUtils.dpToPx(view.getApplication(), widthDp);
} catch (NumberFormatException e) {
log.error(e.getMessage(), e);
resultValue = DEFAULT_WIDTH_MULTIPLIER * view.getDensity();
}
} else {
RenderingRulesStorage rrs = view.getApplication().getRendererRegistry().getCurrentSelectedRenderer();
RenderingRuleSearchRequest req = new RenderingRuleSearchRequest(rrs);
req.setBooleanFilter(rrs.PROPS.R_NIGHT_MODE, nightMode);
req.setIntFilter(rrs.PROPS.R_MINZOOM, tileBox.getZoom());
req.setIntFilter(rrs.PROPS.R_MAXZOOM, tileBox.getZoom());
RenderingRuleProperty ctWidth = rrs.PROPS.get(CURRENT_TRACK_WIDTH_ATTR);
if (ctWidth != null) {
req.setStringFilter(ctWidth, widthKey);
}
if (req.searchRenderingAttribute("gpx")) {
RenderingContext rc = new OsmandRenderer.RenderingContext(view.getContext());
rc.setDensityValue((float) tileBox.getMapDensity());
resultValue = rc.getComplexValue(req, req.ALL.R_STROKE_WIDTH);
}
}
cachedRouteLineWidth.put(widthKey, resultValue);
return resultValue;
}
public void drawLocations(RotatedTileBox tb, Canvas canvas, double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude) {
@ -364,6 +495,7 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
boolean straight = route.getRouteService() == RouteService.STRAIGHT;
publicTransportRouteGeometry.clearRoute();
routeGeometry.updateRoute(tb, route);
routeGeometry.setRouteStyleParams(getRouteLineColor(nightMode), getRouteLineWidth(tb));
if (directTo) {
routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
null, 0);
@ -636,12 +768,12 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont
@Override
public boolean disableSingleTap() {
return false;
return isInRouteLineAppearanceMode();
}
@Override
public boolean disableLongPressOnMap() {
return false;
return isInRouteLineAppearanceMode();
}
@Override

View file

@ -95,6 +95,7 @@ public class GeometryWayDrawer<T extends GeometryWayContext> {
public void drawPath(Canvas canvas, Path path, GeometryWayStyle<?> style) {
context.getAttrs().customColor = style.getColor();
context.getAttrs().customWidth = style.getWidth();
context.getAttrs().drawPath(canvas, path);
}

View file

@ -7,6 +7,7 @@ public abstract class GeometryWayStyle<T extends GeometryWayContext> {
private T context;
protected Integer color;
protected Float width;
public GeometryWayStyle(T context) {
this.context = context;
@ -17,6 +18,12 @@ public abstract class GeometryWayStyle<T extends GeometryWayContext> {
this.color = color;
}
public GeometryWayStyle(T context, Integer color, Float width) {
this.context = context;
this.color = color;
this.width = width;
}
public T getContext() {
return context;
}
@ -29,6 +36,10 @@ public abstract class GeometryWayStyle<T extends GeometryWayContext> {
return color;
}
public Float getWidth() {
return width;
}
public Integer getStrokeColor() {
return context.getStrokeColor(color);
}

View file

@ -1,8 +1,11 @@
package net.osmand.plus.views.layers.geometry;
import android.graphics.Bitmap;
import android.graphics.Paint;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.Location;
import net.osmand.data.RotatedTileBox;
@ -17,15 +20,26 @@ public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, Geome
private RoutingHelper helper;
private RouteCalculationResult route;
private Integer customColor;
private Float customWidth;
public RouteGeometryWay(RouteGeometryWayContext context) {
super(context, new GeometryWayDrawer<>(context));
this.helper = context.getApp().getRoutingHelper();
}
public void setRouteStyleParams(@Nullable @ColorInt Integer color, @Nullable Float width) {
this.customColor = color;
this.customWidth = width;
}
@NonNull
@Override
public GeometryWayStyle<RouteGeometryWayContext> getDefaultWayStyle() {
return new GeometrySolidWayStyle(getContext(), getContext().getAttrs().paint.getColor());
Paint paint = getContext().getAttrs().paint;
int color = customColor != null ? customColor : paint.getColor();
float width = customWidth != null ? customWidth : paint.getStrokeWidth();
return new GeometrySolidWayStyle(getContext(), color, width);
}
public void updateRoute(RotatedTileBox tb, RouteCalculationResult route) {
@ -50,8 +64,8 @@ public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, Geome
private static class GeometrySolidWayStyle extends GeometryWayStyle<RouteGeometryWayContext> {
GeometrySolidWayStyle(RouteGeometryWayContext context, Integer color) {
super(context, color);
GeometrySolidWayStyle(RouteGeometryWayContext context, Integer color, Float width) {
super(context, color, width);
}
@Override

View file

@ -1207,6 +1207,7 @@ public class MapInfoWidgetsFactory {
boolean visible = settings.SHOW_COORDINATES_WIDGET.get() && !map.shouldHideTopControls()
&& map.getMapRouteInfoMenu().shouldShowTopControls() && !map.isTopToolbarActive()
&& !map.getMapLayers().getGpxLayer().isInTrackAppearanceMode()
&& !map.getMapLayers().getRouteLayer().isInRouteLineAppearanceMode()
&& !MapRouteInfoMenu.chooseRoutesVisible && !MapRouteInfoMenu.waypointsVisible
&& !MapRouteInfoMenu.followTrackVisible;

View file

@ -191,7 +191,8 @@ public class MapMarkersWidgetsFactory {
|| map.isTopToolbarActive()
|| map.shouldHideTopControls()
|| map.getMapLayers().getGpxLayer().isInTrackAppearanceMode()
|| map.getMapLayers().getMapMarkersLayer().isInPlanRouteMode()) {
|| map.getMapLayers().getMapMarkersLayer().isInPlanRouteMode()
|| map.getMapLayers().getRouteLayer().isInRouteLineAppearanceMode()) {
updateVisibility(false);
return;
}