From 14bd85a64c52627de2e6e595493e6f40a6db4c08 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Sun, 21 Mar 2021 14:44:53 +0200 Subject: [PATCH] add ability to edit RouteLineDrawInfo and finish dialog UI --- OsmAnd/res/layout/route_line_color_card.xml | 100 +++++ OsmAnd/res/layout/route_line_width_card.xml | 170 +++++++++ .../routing/cards/RouteLineColorCard.java | 347 ++++++++++++++++++ .../routing/cards/RouteLineWidthCard.java | 313 ++++++++++++++++ .../fragments/ProfileAppearanceFragment.java | 21 +- .../RouteLineAppearanceFragment.java | 135 ++++++- .../osmand/plus/views/layers/RouteLayer.java | 19 +- 7 files changed, 1091 insertions(+), 14 deletions(-) create mode 100644 OsmAnd/res/layout/route_line_color_card.xml create mode 100644 OsmAnd/res/layout/route_line_width_card.xml create mode 100644 OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java create mode 100644 OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java diff --git a/OsmAnd/res/layout/route_line_color_card.xml b/OsmAnd/res/layout/route_line_color_card.xml new file mode 100644 index 0000000000..d1c96345b5 --- /dev/null +++ b/OsmAnd/res/layout/route_line_color_card.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/route_line_width_card.xml b/OsmAnd/res/layout/route_line_width_card.xml new file mode 100644 index 0000000000..9d0702b1dd --- /dev/null +++ b/OsmAnd/res/layout/route_line_width_card.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java new file mode 100644 index 0000000000..88e91c3b2a --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineColorCard.java @@ -0,0 +1,347 @@ +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.ApplicationMode; +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 net.osmand.render.RenderingRulesStorage; + +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 ApplicationMode appMode; + + 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 ApplicationMode appMode, + @NonNull DayNightMode initMapTheme, + @NonNull DayNightMode selectedMapTheme) { + super(mapActivity); + this.targetFragment = targetFragment; + this.routeLineDrawInfo = routeLineDrawInfo; + this.appMode = appMode; + 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 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, appMode); + 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 { + int selectedColor = routeLineDrawInfo.getColor(); + int colorNameId = ColorDialogs.getColorName(selectedColor); + 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, getMapStyleName()); + } 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 String getMapStyleName() { + RendererRegistry rr = app.getRendererRegistry(); + RenderingRulesStorage storage = rr.getCurrentSelectedRenderer(); + if (storage == null) { + return ""; + } + return RendererRegistry.getRendererName(app, storage.getName()); + } + + 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 { + + private List 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); + } +} diff --git a/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java new file mode 100644 index 0000000000..8925fcec37 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/routing/cards/RouteLineWidthCard.java @@ -0,0 +1,313 @@ +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.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.routing.RouteLineHelper; +import net.osmand.plus.track.AppearanceViewHolder; +import net.osmand.plus.track.TrackAppearanceFragment.OnNeedScrollListener; +import net.osmand.render.RenderingRulesStorage; + +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, 5), + MEDIUM(R.string.rendering_value_medium_name, R.drawable.ic_action_track_line_medium_color, 13), + THICK(R.string.rendering_value_bold_name, R.drawable.ic_action_track_line_bold_color, 28), + CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings, null); + + WidthMode(int titleId, int iconId, Integer width) { + this.titleId = titleId; + this.iconId = iconId; + this.width = width; + } + + int titleId; + int iconId; + Integer width; + } + + 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(Integer width) { + routeLineDrawInfo.setWidth(width); + mapActivity.refreshMap(); + } + + private Integer 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, getMapStyleName()); + tvDescription.setText(description); + tvDescription.setVisibility(View.VISIBLE); + } else { + tvDescription.setVisibility(View.GONE); + } + } + + private String getMapStyleName() { + RendererRegistry rr = app.getRendererRegistry(); + RenderingRulesStorage storage = rr.getCurrentSelectedRenderer(); + if (storage == null) { + return ""; + } + return RendererRegistry.getRendererName(app, storage.getName()); + } + + private void updateCustomWidthSlider() { + if (selectedMode == WidthMode.CUSTOM) { + Slider widthSlider = view.findViewById(R.id.width_slider); + + widthSlider.setValueTo(CUSTOM_WIDTH_MAX); + widthSlider.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)); + + Integer width = getRouteLineWidth(); + if (width == null || width > CUSTOM_WIDTH_MAX || width < CUSTOM_WIDTH_MIN) { + width = CUSTOM_WIDTH_MIN; + } + widthSlider.setValue(width); + + final TextView tvCustomWidth = view.findViewById(R.id.width_value_tv); + widthSlider.addOnChangeListener(new Slider.OnChangeListener() { + @Override + public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) { + if (fromUser) { + Integer newWidth = (int) value; + setRouteLineWidth(newWidth); + tvCustomWidth.setText(String.valueOf(newWidth)); + } + } + }); + UiUtilities.setupSlider(widthSlider, 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(Integer width) { + WidthMode result = null; + if (width != null) { + for (WidthMode mode : WidthMode.values()) { + if (mode.width != null && (int) width == mode.width) { + result = mode; + break; + } + } + if (result == null) { + result = WidthMode.CUSTOM; + } + } else { + result = WidthMode.DEFAULT; + } + return result; + } + + private class WidthAdapter extends RecyclerView.Adapter { + + private final List 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.width); + } + 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.width != null ? RouteLineHelper.getColor(routeLineDrawInfo) : defaultColor; + } + + 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); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java index 939e8124c7..3416222b95 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java @@ -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.RouteLineHelper; import net.osmand.plus.routing.RouteService; import net.osmand.plus.settings.backend.ApplicationMode; 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; @@ -76,7 +79,7 @@ import static net.osmand.plus.profiles.SelectProfileBottomSheet.PROFILES_LIST_UP 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); @@ -161,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) { @@ -181,6 +185,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O profile.routeService = baseModeForNewProfile.getRouteService(); profile.locationIcon = baseModeForNewProfile.getLocationIcon(); profile.navigationIcon = baseModeForNewProfile.getNavigationIcon(); + profile.routeLineDrawInfo = RouteLineHelper.createDrawInfoForAppMode(app, profile.parent); } @Override @@ -323,6 +328,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) { @@ -336,6 +342,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); } @@ -771,6 +778,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O mode.setCustomIconColor(changedProfile.customColor); mode.setLocationIcon(changedProfile.locationIcon); mode.setNavigationIcon(changedProfile.navigationIcon); + RouteLineHelper.saveRouteLineAppearance(app, mode, changedProfile.routeLineDrawInfo); FragmentActivity activity = getActivity(); if (activity != null) { @@ -798,6 +806,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O if (!ApplicationMode.values(app).contains(mode)) { ApplicationMode.changeProfileAvailability(mode, true, app); } + RouteLineHelper.saveRouteLineAppearance(app, mode, changedProfile.routeLineDrawInfo); return mode; } @@ -989,7 +998,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O MapActivity mapActivity = getMapActivity(); ApplicationMode appMode = getSelectedAppMode(); if (mapActivity != null && appMode != null) { - RouteLineAppearanceFragment.showInstance(mapActivity, appMode, this); + RouteLineAppearanceFragment.showInstance(mapActivity, changedProfile.routeLineDrawInfo, appMode, this); } } return super.onPreferenceClick(preference); @@ -1005,6 +1014,11 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O this.onCardPressed(colorsCard); } + @Override + public void applyRouteLineAppearance(@NonNull RouteLineDrawInfo routeLineDrawInfo) { + changedProfile.routeLineDrawInfo = routeLineDrawInfo; + } + public static boolean showInstance(FragmentActivity activity, SettingsScreenType screenType, @Nullable String appMode, boolean imported) { try { Fragment fragment = Fragment.instantiate(activity, screenType.fragmentName); @@ -1036,6 +1050,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O RouteService routeService; NavigationIcon navigationIcon; LocationIcon locationIcon; + RouteLineDrawInfo routeLineDrawInfo; @ColorInt public int getActualColor() { @@ -1072,6 +1087,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O return false; if (routeService != that.routeService) return false; if (navigationIcon != that.navigationIcon) return false; + if (routeLineDrawInfo != that.routeLineDrawInfo) return false; return locationIcon == that.locationIcon; } @@ -1087,6 +1103,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; } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java index ee6ffec18e..39d9c853ca 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/RouteLineAppearanceFragment.java @@ -10,6 +10,7 @@ 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; @@ -19,26 +20,45 @@ import androidx.fragment.app.FragmentActivity; import net.osmand.AndroidUtils; import net.osmand.plus.LockableScrollView; +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 { +public class RouteLineAppearanceFragment extends ContextMenuScrollFragment implements ColorPickerListener, OnSelectedColorChangeListener, OnMapThemeUpdateListener { - public final static String TAG = RouteLineAppearanceFragment.class.getName(); + 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 ApplicationMode appMode; + 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; @@ -82,8 +102,19 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + toolbarHeightPx = getResources().getDimensionPixelSize(R.dimen.dashboard_map_toolbar); - requireMyActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { + + 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(); } @@ -136,6 +167,36 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { private void setupCards() { MapActivity mapActivity = requireMapActivity(); ViewGroup cardsContainer = getCardsContainer(); + + colorCard = new RouteLineColorCard(mapActivity, this, routeLineDrawInfo, appMode, 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() { @@ -143,7 +204,6 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { closeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // discard changes dismiss(); } }); @@ -172,7 +232,9 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { saveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // save changes + if (getTargetFragment() instanceof OnApplyRouteLineListener) { + ((OnApplyRouteLineListener) getTargetFragment()).applyRouteLineAppearance(routeLineDrawInfo); + } dismiss(); } }); @@ -181,7 +243,6 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { cancelButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - // discard changes FragmentActivity activity = getActivity(); if (activity != null) { activity.onBackPressed(); @@ -225,6 +286,33 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { .setDuration(200); } + @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(); @@ -250,6 +338,7 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { R.id.map_right_widgets_panel, R.id.map_center_info, R.id.map_search_button); + changeMapTheme(initMapTheme); } } @@ -271,13 +360,30 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { } } + @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 ApplicationMode appMode, @NonNull Fragment target) { try { RouteLineAppearanceFragment fragment = new RouteLineAppearanceFragment(); fragment.setRetainInstance(true); fragment.setTargetFragment(target, 0); + fragment.routeLineDrawInfo = new RouteLineDrawInfo(drawInfo); fragment.appMode = appMode; mapActivity.getSupportFragmentManager() @@ -290,4 +396,21 @@ public class RouteLineAppearanceFragment extends ContextMenuScrollFragment { 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); + } } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java index 1c934e9651..ce7a9e12bd 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/RouteLayer.java @@ -32,6 +32,7 @@ import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.profiles.LocationIcon; 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; @@ -67,6 +68,7 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont private LayerDrawable selectedPoint; private TrackChartPoints trackChartPoints; + private RouteLineDrawInfo routeLineDrawInfo; private RenderingLineAttributes attrs; private RenderingLineAttributes attrsPT; @@ -217,10 +219,11 @@ public class RouteLayer extends OsmandMapLayer implements ContextMenuLayer.ICont } public boolean isInRouteLineAppearanceMode() { - MapActivity mapActivity = getMapActivity(); - return mapActivity != null - && mapActivity.getRouteLineAppearanceFragment() != null - && mapActivity.getRouteLineAppearanceFragment().isVisible(); + return routeLineDrawInfo != null; + } + + public void setRouteLineDrawInfo(RouteLineDrawInfo routeLineDrawInfo) { + this.routeLineDrawInfo = routeLineDrawInfo; } private MapActivity getMapActivity() { @@ -277,9 +280,13 @@ 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) { + // draw route line on map + } + } private void drawAction(RotatedTileBox tb, Canvas canvas, List actionPoints) { if (actionPoints.size() > 0) {