diff --git a/OsmAnd/res/layout/card_multi_state.xml b/OsmAnd/res/layout/card_multi_state.xml index 63002473c19..4f2fefcabc7 100644 --- a/OsmAnd/res/layout/card_multi_state.xml +++ b/OsmAnd/res/layout/card_multi_state.xml @@ -46,6 +46,7 @@ tools:text="Solid" /> diff --git a/OsmAnd/res/layout/map_button_icons_card.xml b/OsmAnd/res/layout/map_button_icons_card.xml index 0fe9bfaddc0..11acf66c1f0 100644 --- a/OsmAnd/res/layout/map_button_icons_card.xml +++ b/OsmAnd/res/layout/map_button_icons_card.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/card_and_list_background_basic" android:orientation="vertical"> - - - - - - - + \ No newline at end of file diff --git a/OsmAnd/res/layout/min_max_container.xml b/OsmAnd/res/layout/min_max_container.xml new file mode 100644 index 00000000000..391d8d17064 --- /dev/null +++ b/OsmAnd/res/layout/min_max_container.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/slider_with_buttons.xml b/OsmAnd/res/layout/slider_with_buttons.xml index 5775deb0ea9..f05803e95a7 100644 --- a/OsmAnd/res/layout/slider_with_buttons.xml +++ b/OsmAnd/res/layout/slider_with_buttons.xml @@ -8,61 +8,61 @@ android:orientation="vertical" android:paddingBottom="@dimen/content_padding_half"> + + + android:orientation="vertical"> - + android:orientation="horizontal" + android:paddingHorizontal="@dimen/content_padding_round_medium"> - + + + + + + + - - + android:orientation="vertical"> - + - + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index e75b808b505..21f62a232f8 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,9 @@ Thx - Hardy --> + Each button would keep its own size. + Each button would keep its own corner radius. + Each button would keep its own background opacity. Selected profile \"%s\" Interpolation Location interpolation percentage diff --git a/OsmAnd/src/net/osmand/plus/quickaction/ButtonAppearanceParams.kt b/OsmAnd/src/net/osmand/plus/quickaction/ButtonAppearanceParams.kt index 49cf34e0270..e1d0d52dffa 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/ButtonAppearanceParams.kt +++ b/OsmAnd/src/net/osmand/plus/quickaction/ButtonAppearanceParams.kt @@ -44,5 +44,7 @@ data class ButtonAppearanceParams( internal const val ROUND_RADIUS_DP = 36 internal const val RECTANGULAR_RADIUS_DP = 2 + + internal const val ORIGINAL_VALUE = -1 } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/quickaction/ButtonSizeCard.java b/OsmAnd/src/net/osmand/plus/quickaction/ButtonSizeCard.java index 76af69a2d09..a26ed2e6caa 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/ButtonSizeCard.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/ButtonSizeCard.java @@ -1,6 +1,7 @@ package net.osmand.plus.quickaction; import static com.google.android.material.slider.LabelFormatter.LABEL_FLOATING; +import static net.osmand.plus.quickaction.ButtonAppearanceParams.ORIGINAL_VALUE; import android.view.View; @@ -8,9 +9,15 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.card.base.multistate.CardState; import net.osmand.plus.utils.ColorUtilities; import net.osmand.plus.utils.UiUtilities; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + public class ButtonSizeCard extends SliderButtonsCard { public static final int MIN_BUTTON_SIZE = 40; @@ -19,15 +26,22 @@ public class ButtonSizeCard extends SliderButtonsCard { private final ButtonAppearanceParams appearanceParams; - public ButtonSizeCard(@NonNull MapActivity activity, @NonNull ButtonAppearanceParams appearanceParams) { - super(activity); + public ButtonSizeCard(@NonNull MapActivity activity, + @NonNull ButtonAppearanceParams appearanceParams, boolean showOriginal) { + super(activity, showOriginal); this.appearanceParams = appearanceParams; } protected void setupHeader(@NonNull View view) { super.setupHeader(view); title.setText(R.string.shared_string_size); - description.setText(getFormattedValue(appearanceParams.getSize())); + valueTv.setText(getFormattedValue(appearanceParams.getSize())); + } + + @Override + protected void setupDescription(@NonNull @NotNull View view) { + super.setupDescription(view); + description.setText(R.string.default_buttons_corners_original_description); } protected void setupSlider(@NonNull View view) { @@ -37,22 +51,57 @@ protected void setupSlider(@NonNull View view) { slider.setValueTo(MAX_BUTTON_SIZE); slider.setValueFrom(MIN_BUTTON_SIZE); slider.setStepSize(BUTTON_SIZE_STEP); - slider.setValue(appearanceParams.getSize()); slider.setLabelBehavior(LABEL_FLOATING); slider.setLabelFormatter(ButtonSizeCard.this::getFormattedValue); + + if (!isOriginalValue()) { + slider.setValue(appearanceParams.getSize()); + } } protected void onValueSelected(float value) { super.onValueSelected(value); appearanceParams.setSize((int) value); - description.setText(getFormattedValue(appearanceParams.getSize())); + valueTv.setText(getFormattedValue(appearanceParams.getSize())); notifyCardPressed(); } + @Override + protected boolean isOriginalValue() { + return appearanceParams.getSize() == ORIGINAL_VALUE; + } + @NonNull protected String getFormattedValue(float value) { + if (value == ORIGINAL_VALUE) { + return getString(R.string.shared_string_original); + } return getString(R.string.ltr_or_rtl_combine_via_space, (int) value, getString(R.string.shared_string_dp)); } + + @NonNull + @Override + protected List getCardStates() { + List list = new ArrayList<>(); + + list.add(new CardState(R.string.shared_string_original).setTag(ORIGINAL_VALUE)); + + for (int i = MIN_BUTTON_SIZE; i <= MAX_BUTTON_SIZE; i += BUTTON_SIZE_STEP) { + list.add(new CardState(getFormattedValue(i)) + .setShowTopDivider(i == MIN_BUTTON_SIZE) + .setTag(i)); + } + return list; + } + + @Override + protected void setSelectedState(@NonNull CardState cardState) { + if (cardState.getTag() instanceof Integer value) { + appearanceParams.setSize(value); + } + updateContent(); + notifyCardPressed(); + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/quickaction/CornerRadiusCard.java b/OsmAnd/src/net/osmand/plus/quickaction/CornerRadiusCard.java index c5285f80811..ad9f4c94b7c 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/CornerRadiusCard.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/CornerRadiusCard.java @@ -2,6 +2,7 @@ import static com.google.android.material.slider.LabelFormatter.LABEL_FLOATING; +import static net.osmand.plus.quickaction.ButtonAppearanceParams.ORIGINAL_VALUE; import android.view.View; @@ -9,24 +10,37 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.card.base.multistate.CardState; import net.osmand.plus.utils.ColorUtilities; import net.osmand.plus.utils.UiUtilities; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + public class CornerRadiusCard extends SliderButtonsCard { public static final int[] CORNER_RADIUS_VALUES = {3, 6, 9, 12, 36}; private final ButtonAppearanceParams appearanceParams; - public CornerRadiusCard(@NonNull MapActivity activity, @NonNull ButtonAppearanceParams appearanceParams) { - super(activity); + public CornerRadiusCard(@NonNull MapActivity activity, + @NonNull ButtonAppearanceParams appearanceParams, boolean showOriginal) { + super(activity, showOriginal); this.appearanceParams = appearanceParams; } protected void setupHeader(@NonNull View view) { super.setupHeader(view); title.setText(R.string.corner_radius); - description.setText(getFormattedValue(appearanceParams.getCornerRadius())); + valueTv.setText(getFormattedValue(appearanceParams.getCornerRadius())); + } + + @Override + protected void setupDescription(@NonNull @NotNull View view) { + super.setupDescription(view); + description.setText(R.string.default_buttons_corners_original_description); } protected void setupSlider(@NonNull View view) { @@ -36,9 +50,12 @@ protected void setupSlider(@NonNull View view) { slider.setValueTo(CORNER_RADIUS_VALUES.length - 1); slider.setValueFrom(0); slider.setStepSize(1); - slider.setValue(getSelectedIndex()); slider.setLabelBehavior(LABEL_FLOATING); slider.setLabelFormatter(value -> CornerRadiusCard.this.getFormattedValue(CORNER_RADIUS_VALUES[(int) value])); + + if (!isOriginalValue()) { + slider.setValue(getSelectedIndex()); + } } protected void onValueSelected(float value) { @@ -46,11 +63,16 @@ protected void onValueSelected(float value) { int index = (int) value; appearanceParams.setCornerRadius(CORNER_RADIUS_VALUES[index]); - description.setText(getFormattedValue(appearanceParams.getCornerRadius())); + valueTv.setText(getFormattedValue(appearanceParams.getCornerRadius())); notifyCardPressed(); } + @Override + protected boolean isOriginalValue() { + return appearanceParams.getCornerRadius() == ORIGINAL_VALUE; + } + private int getSelectedIndex() { int value = appearanceParams.getCornerRadius(); for (int i = 0; i < CORNER_RADIUS_VALUES.length; i++) { @@ -63,6 +85,34 @@ private int getSelectedIndex() { @NonNull protected String getFormattedValue(float value) { + if (value == ORIGINAL_VALUE) { + return getString(R.string.shared_string_original); + } return getString(R.string.ltr_or_rtl_combine_via_space, (int) value, getString(R.string.shared_string_dp)); } + + @NonNull + @Override + protected List getCardStates() { + List list = new ArrayList<>(); + + list.add(new CardState(R.string.shared_string_original).setTag(ORIGINAL_VALUE)); + + for (int i = 0; i < CORNER_RADIUS_VALUES.length; i++) { + int value = CORNER_RADIUS_VALUES[i]; + list.add(new CardState(getFormattedValue(value)) + .setShowTopDivider(i == 0) + .setTag(value)); + } + return list; + } + + @Override + protected void setSelectedState(@NonNull CardState cardState) { + if (cardState.getTag() instanceof Integer value) { + appearanceParams.setCornerRadius(value); + } + updateContent(); + notifyCardPressed(); + } } diff --git a/OsmAnd/src/net/osmand/plus/quickaction/MapButtonAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/quickaction/MapButtonAppearanceFragment.java index b4a5a7604ce..6a163b1001d 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/MapButtonAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/MapButtonAppearanceFragment.java @@ -133,11 +133,11 @@ private void setupCards(@NonNull View view) { mapButtonCard = new MapButtonCard(activity, buttonState, appearanceParams); addCard(container, mapButtonCard); addCard(container, new ButtonIconsCard(activity, iconController)); - addCard(container, new CornerRadiusCard(activity, appearanceParams)); + addCard(container, new CornerRadiusCard(activity, appearanceParams, false)); container.addView(themedInflater.inflate(R.layout.simple_divider_item, container, false)); - addCard(container, new ButtonSizeCard(activity, appearanceParams)); + addCard(container, new ButtonSizeCard(activity, appearanceParams, false)); container.addView(themedInflater.inflate(R.layout.simple_divider_item, container, false)); - addCard(container, new OpacitySliderCard(activity, appearanceParams)); + addCard(container, new OpacitySliderCard(activity, appearanceParams, false)); } private void addCard(@NonNull ViewGroup container, @NonNull BaseCard card) { diff --git a/OsmAnd/src/net/osmand/plus/quickaction/MapButtonsHelper.java b/OsmAnd/src/net/osmand/plus/quickaction/MapButtonsHelper.java index b95bf4a040f..3d3e5aaccd2 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/MapButtonsHelper.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/MapButtonsHelper.java @@ -1,5 +1,6 @@ package net.osmand.plus.quickaction; +import static net.osmand.plus.quickaction.ButtonAppearanceParams.ORIGINAL_VALUE; import static net.osmand.plus.quickaction.QuickActionType.CREATE_CATEGORY; import static net.osmand.plus.views.mapwidgets.configure.buttons.QuickActionButtonState.DEFAULT_BUTTON_ID; @@ -21,6 +22,7 @@ import net.osmand.plus.quickaction.actions.special.OpenWunderLINQDatagridAction; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.preferences.CommonPreference; import net.osmand.plus.views.mapwidgets.configure.buttons.*; import net.osmand.util.Algorithms; @@ -99,9 +101,14 @@ public static QuickActionType getCategoryActionTypeFromId(int typeId) { private final OsmandApplication app; private final OsmandSettings settings; + private final Collator collator = OsmAndCollator.primaryCollator(); private final QuickActionSerializer serializer = new QuickActionSerializer(); private final Gson gson = new GsonBuilder().registerTypeAdapter(QuickAction.class, serializer).create(); + private final CommonPreference defaultSizePref; + private final CommonPreference defaultOpacityPref; + private final CommonPreference defaultCornerRadiusPref; + private Map3DButtonState map3DButtonState; private MyLocationButtonState myLocationButtonState; private NavigationMenuButtonState navigationMenuButtonState; @@ -117,11 +124,15 @@ public static QuickActionType getCategoryActionTypeFromId(int typeId) { private Map quickActionTypesInt = new TreeMap<>(); private Map quickActionTypesStr = new TreeMap<>(); private Set updatesListeners = new HashSet<>(); - private final Collator collator = OsmAndCollator.primaryCollator(); + public MapButtonsHelper(@NonNull OsmandApplication app) { this.app = app; this.settings = app.getSettings(); + this.defaultSizePref = settings.registerIntPreference("default_map_button_size", ORIGINAL_VALUE).makeProfile().cache(); + this.defaultOpacityPref = settings.registerFloatPreference("default_map_button_opacity", ORIGINAL_VALUE).makeProfile().cache(); + this.defaultCornerRadiusPref = settings.registerIntPreference("default_map_button_corner_radius", ORIGINAL_VALUE).makeProfile().cache(); + updateActionTypes(); initDefaultButtons(); } @@ -597,6 +608,21 @@ public QuickActionButtonState getButtonStateByAction(@NonNull QuickAction action return null; } + @NonNull + public CommonPreference getDefaultSizePref() { + return defaultSizePref; + } + + @NonNull + public CommonPreference getDefaultOpacityPref() { + return defaultOpacityPref; + } + + @NonNull + public CommonPreference getDefaultCornerRadiusPref() { + return defaultCornerRadiusPref; + } + @NonNull public String createNewButtonStateId() { return DEFAULT_BUTTON_ID + "_" + System.currentTimeMillis(); diff --git a/OsmAnd/src/net/osmand/plus/quickaction/OpacitySliderCard.java b/OsmAnd/src/net/osmand/plus/quickaction/OpacitySliderCard.java index df3e2534141..fa6e86eb08d 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/OpacitySliderCard.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/OpacitySliderCard.java @@ -1,8 +1,12 @@ package net.osmand.plus.quickaction; import static com.google.android.material.slider.LabelFormatter.LABEL_FLOATING; +import static net.osmand.plus.quickaction.ButtonAppearanceParams.ORIGINAL_VALUE; +import static net.osmand.plus.quickaction.ButtonAppearanceParams.TRANSPARENT_ALPHA; +import android.content.Context; import android.view.View; +import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; @@ -10,9 +14,16 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.base.ProgressHelper; +import net.osmand.plus.card.base.multistate.CardState; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.utils.ColorUtilities; import net.osmand.plus.utils.UiUtilities; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + public class OpacitySliderCard extends SliderButtonsCard { public static final int MIN_OPACITY = 0; @@ -20,25 +31,25 @@ public class OpacitySliderCard extends SliderButtonsCard { private final ButtonAppearanceParams appearanceParams; - @Override - public int getCardLayoutId() { - return R.layout.map_button_opacity_card; - } + private float selectedOpacity; - public OpacitySliderCard(@NonNull MapActivity activity, @NonNull ButtonAppearanceParams appearanceParams) { - super(activity); + public OpacitySliderCard(@NonNull MapActivity activity, + @NonNull ButtonAppearanceParams appearanceParams, boolean showOriginal) { + super(activity, showOriginal); + this.selectedOpacity = appearanceParams.getOpacity(); this.appearanceParams = appearanceParams; } + @NonNull @Override - protected void updateContent() { - setupHeader(view); - setupSlider(view); + public View inflate(@NonNull Context ctx) { + View view = super.inflate(ctx); + addMinMaxRow(view.findViewById(R.id.slider_container)); + return view; } - @Override - protected void setupHeader(@NonNull View view) { - super.setupHeader(view); + private void addMinMaxRow(@NonNull ViewGroup container) { + View view = themedInflater.inflate(R.layout.min_max_container, container, false); TextView valueMin = view.findViewById(R.id.value_min); TextView valueMax = view.findViewById(R.id.value_max); @@ -46,9 +57,23 @@ protected void setupHeader(@NonNull View view) { valueMin.setText(getFormattedValue(MIN_OPACITY)); valueMax.setText(getFormattedValue(MAX_OPACITY)); + container.addView(view); + } + + @Override + protected void setupHeader(@NonNull View view) { + super.setupHeader(view); + + title.setText(R.string.background_opacity); updateDescription(); } + @Override + protected void setupDescription(@NonNull @NotNull View view) { + super.setupDescription(view); + description.setText(R.string.default_buttons_corners_original_description); + } + @Override protected void setupSlider(@NonNull View view) { super.setupSlider(view); @@ -56,23 +81,63 @@ protected void setupSlider(@NonNull View view) { slider.setValueTo(MAX_OPACITY); slider.setValueFrom(MIN_OPACITY); - slider.setValue(appearanceParams.getOpacity()); slider.setLabelBehavior(LABEL_FLOATING); slider.setLabelFormatter(OpacitySliderCard.this::getFormattedValue); + + if (!isOriginalValue()) { + slider.setValue(appearanceParams.getOpacity()); + } + } + + @Override + protected void setupButtons(@NonNull @NotNull View view) { + super.setupButtons(view); + + AndroidUiHelper.updateVisibility(increaseButton, false); + AndroidUiHelper.updateVisibility(decreaseButton, false); } protected void onValueSelected(float value) { + selectedOpacity = value; appearanceParams.setOpacity(value); updateDescription(); notifyCardPressed(); } + @Override + protected boolean isOriginalValue() { + return appearanceParams.getOpacity() == ORIGINAL_VALUE; + } + + @NonNull + @Override + protected List getCardStates() { + List list = new ArrayList<>(); + float value = selectedOpacity != ORIGINAL_VALUE ? selectedOpacity : TRANSPARENT_ALPHA; + list.add(new CardState(R.string.shared_string_original).setTag(ORIGINAL_VALUE)); + list.add(new CardState(R.string.shared_string_custom).setTag(value).setShowTopDivider(true)); + + return list; + } + private void updateDescription() { - description.setText(getFormattedValue(appearanceParams.getOpacity())); + valueTv.setText(getFormattedValue(appearanceParams.getOpacity())); } @NonNull protected String getFormattedValue(float value) { + if (value == ORIGINAL_VALUE) { + return getString(R.string.shared_string_original); + } return ProgressHelper.normalizeProgressPercent((int) (value * 100)) + "%"; } + + @Override + protected void setSelectedState(@NonNull CardState cardState) { + if (cardState.getTag() instanceof Number value) { + appearanceParams.setOpacity(value.floatValue()); + } + updateContent(); + notifyCardPressed(); + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/quickaction/SliderButtonsCard.java b/OsmAnd/src/net/osmand/plus/quickaction/SliderButtonsCard.java index 965fef5b406..6d896a875f5 100644 --- a/OsmAnd/src/net/osmand/plus/quickaction/SliderButtonsCard.java +++ b/OsmAnd/src/net/osmand/plus/quickaction/SliderButtonsCard.java @@ -12,37 +12,63 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.card.base.multistate.CardState; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.routepreparationmenu.cards.MapBaseCard; import net.osmand.plus.utils.AndroidUtils; import net.osmand.plus.utils.ColorUtilities; +import net.osmand.plus.widgets.popup.PopUpMenu; +import net.osmand.plus.widgets.popup.PopUpMenuDisplayData; +import net.osmand.plus.widgets.popup.PopUpMenuItem; + +import java.util.ArrayList; +import java.util.List; public abstract class SliderButtonsCard extends MapBaseCard { protected Slider slider; protected TextView title; + protected TextView valueTv; protected TextView description; protected ImageButton increaseButton; protected ImageButton decreaseButton; + protected boolean showOriginal; + @Override public int getCardLayoutId() { return R.layout.slider_with_buttons; } - public SliderButtonsCard(@NonNull MapActivity activity) { + public SliderButtonsCard(@NonNull MapActivity activity, boolean showOriginal) { super(activity, false); + this.showOriginal = showOriginal; } @Override protected void updateContent() { setupHeader(view); setupSlider(view); + setupDescription(view); setupButtons(view); } protected void setupHeader(@NonNull View view) { - title = view.findViewById(R.id.title); - description = view.findViewById(R.id.description); + View container = view.findViewById(R.id.header_container); + title = container.findViewById(R.id.card_title); + valueTv = container.findViewById(R.id.title); + + View selector = view.findViewById(R.id.card_selector); + if (showOriginal) { + selector.setOnClickListener(v -> showMenu(selector)); + } + AndroidUiHelper.updateVisibility(selector.findViewById(R.id.drop_down_icon), showOriginal); + } + + protected void setupDescription(@NonNull View view) { + View container = view.findViewById(R.id.description_container); + description = container.findViewById(R.id.summary); + AndroidUiHelper.updateVisibility(container, showOriginal && isOriginalValue()); } protected void setupSlider(@NonNull View view) { @@ -52,6 +78,7 @@ protected void setupSlider(@NonNull View view) { onValueSelected(value); } }); + AndroidUiHelper.updateVisibility(view.findViewById(R.id.slider_container), !showOriginal || !isOriginalValue()); } protected void setupButtons(@NonNull View view) { @@ -80,9 +107,36 @@ protected void onValueSelected(float value) { decreaseButton.setEnabled(value > slider.getValueFrom()); } + public void showMenu(@NonNull View view) { + List items = new ArrayList<>(); + for (CardState state : getCardStates()) { + items.add(new PopUpMenuItem.Builder(app) + .setTitle(state.toHumanString(app)) + .showTopDivider(state.isShowTopDivider()) + .setTitleColor(ColorUtilities.getPrimaryTextColor(app, nightMode)) + .setTag(state) + .create() + ); + } + PopUpMenuDisplayData data = new PopUpMenuDisplayData(); + data.anchorView = view; + data.menuItems = items; + data.nightMode = nightMode; + data.onItemClickListener = item -> setSelectedState((CardState) item.getTag()); + PopUpMenu.show(data); + } + + + protected abstract boolean isOriginalValue(); + @NonNull protected abstract String getFormattedValue(float value); + @NonNull + protected abstract List getCardStates(); + + protected abstract void setSelectedState(@NonNull CardState cardState); + @NonNull protected Drawable getPersistentPrefIcon(@DrawableRes int iconId) { Drawable enabled = getColoredIcon(iconId, ColorUtilities.getActiveColorId(nightMode)); diff --git a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java index 1e51d77c74c..bf94ca070cf 100644 --- a/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java +++ b/OsmAnd/src/net/osmand/plus/routepreparationmenu/cards/BaseCard.java @@ -128,12 +128,17 @@ public View build() { @NonNull public View build(@NonNull Context ctx) { - themedInflater = UiUtilities.getInflater(ctx, nightMode); - view = themedInflater.inflate(getCardLayoutId(), null); + view = inflate(ctx); update(); return view; } + @NonNull + public View inflate(@NonNull Context ctx) { + themedInflater = UiUtilities.getInflater(ctx, nightMode); + return themedInflater.inflate(getCardLayoutId(), null); + } + public OsmandApplication getMyApplication() { return app; } diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/DefaultButtonsAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/DefaultButtonsAppearanceFragment.java new file mode 100644 index 00000000000..f68a4320a5b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/DefaultButtonsAppearanceFragment.java @@ -0,0 +1,267 @@ +package net.osmand.plus.views.mapwidgets.configure.buttons; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.text.SpannableString; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; +import androidx.core.view.ViewCompat; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + +import com.google.android.material.snackbar.Snackbar; + +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.base.BaseOsmAndFragment; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.quickaction.ButtonAppearanceParams; +import net.osmand.plus.quickaction.ButtonSizeCard; +import net.osmand.plus.quickaction.CornerRadiusCard; +import net.osmand.plus.quickaction.MapButtonsHelper; +import net.osmand.plus.quickaction.OpacitySliderCard; +import net.osmand.plus.routepreparationmenu.cards.BaseCard; +import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener; +import net.osmand.plus.settings.backend.preferences.CommonPreference; +import net.osmand.plus.utils.AndroidUtils; +import net.osmand.plus.utils.ColorUtilities; +import net.osmand.plus.utils.UiUtilities; +import net.osmand.plus.widgets.dialogbutton.DialogButton; +import net.osmand.util.Algorithms; + +import java.util.ArrayList; +import java.util.List; + +public class DefaultButtonsAppearanceFragment extends BaseOsmAndFragment implements CardListener { + + public static final String TAG = DefaultButtonsAppearanceFragment.class.getSimpleName(); + + private CommonPreference defaultSizePref; + private CommonPreference defaultOpacityPref; + private CommonPreference defaultCornerRadiusPref; + + private ButtonAppearanceParams appearanceParams; + private ButtonAppearanceParams originalAppearanceParams; + + private List cards; + private DialogButton applyButton; + + public boolean getContentStatusBarNightMode() { + return nightMode; + } + + @Override + public int getStatusBarColorId() { + AndroidUiHelper.setStatusBarContentColor(getView(), nightMode); + return ColorUtilities.getListBgColorId(nightMode); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + MapButtonsHelper helper = app.getMapButtonsHelper(); + defaultSizePref = helper.getDefaultSizePref(); + defaultOpacityPref = helper.getDefaultOpacityPref(); + defaultCornerRadiusPref = helper.getDefaultCornerRadiusPref(); + + appearanceParams = createAppearanceParams(); + originalAppearanceParams = createAppearanceParams(); + + if (savedInstanceState != null) { + appearanceParams.readBundle(savedInstanceState); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + updateNightMode(); + View view = themedInflater.inflate(R.layout.map_button_appearance_fragment, container, false); + AndroidUtils.addStatusBarPadding21v(requireMyActivity(), view); + + setupToolbar(view); + setupCards(view); + setupApplyButton(view); + + updateContent(); + + return view; + } + + private void setupToolbar(@NonNull View view) { + Toolbar toolbar = view.findViewById(R.id.toolbar); + ViewCompat.setElevation(view.findViewById(R.id.appbar), 5.0f); + toolbar.setBackgroundColor(ColorUtilities.getAppBarSecondaryColor(view.getContext(), nightMode)); + + TextView title = toolbar.findViewById(R.id.toolbar_title); + title.setText(R.string.default_appearance); + title.setTextColor(ColorUtilities.getPrimaryTextColor(view.getContext(), nightMode)); + + ImageView closeButton = toolbar.findViewById(R.id.close_button); + closeButton.setImageDrawable(getContentIcon(R.drawable.ic_action_close)); + closeButton.setOnClickListener(v -> { + FragmentActivity activity = getActivity(); + if (activity != null) { + activity.onBackPressed(); + } + }); + ImageView resetButton = toolbar.findViewById(R.id.action_button); + resetButton.setOnClickListener(v -> resetAppearance()); + resetButton.setImageDrawable(getContentIcon(R.drawable.ic_action_reset)); + resetButton.setContentDescription(getString(R.string.shared_string_reset)); + AndroidUiHelper.updateVisibility(resetButton, true); + } + + private void setupCards(@NonNull View view) { + cards = new ArrayList<>(); + + ViewGroup container = view.findViewById(R.id.cards_container); + container.removeAllViews(); + + MapActivity activity = requireMapActivity(); + addCard(container, new CornerRadiusCard(activity, appearanceParams, true)); + container.addView(themedInflater.inflate(R.layout.list_item_divider, container, false)); + addCard(container, new ButtonSizeCard(activity, appearanceParams, true)); + container.addView(themedInflater.inflate(R.layout.list_item_divider, container, false)); + addCard(container, new OpacitySliderCard(activity, appearanceParams, true)); + } + + private void addCard(@NonNull ViewGroup container, @NonNull BaseCard card) { + cards.add(card); + card.setListener(this); + container.addView(card.build()); + } + + private void setupApplyButton(@NonNull View view) { + applyButton = view.findViewById(R.id.apply_button); + applyButton.setOnClickListener(v -> { + saveChanges(false); + showAllModesSnackbar(); + + FragmentActivity activity = getActivity(); + if (activity != null) { + activity.onBackPressed(); + } + }); + } + + private void showAllModesSnackbar() { + View containerView = getView(); + if (containerView != null) { + String name = settings.getApplicationMode().toHumanString(); + String text = app.getString(R.string.changes_applied_to_profile, name); + SpannableString message = UiUtilities.createSpannableString(text, Typeface.BOLD, name); + Snackbar snackbar = Snackbar.make(containerView, message, Snackbar.LENGTH_LONG) + .setAction(R.string.apply_to_all_profiles, view -> saveChanges(true)); + UiUtilities.setupSnackbarVerticalLayout(snackbar); + UiUtilities.setupSnackbar(snackbar, nightMode); + snackbar.show(); + } + } + + private void saveChanges(boolean applyToAllProfiles) { + if (applyToAllProfiles) { + settings.setPreferenceForAllModes(defaultSizePref.getId(), appearanceParams.getSize()); + settings.setPreferenceForAllModes(defaultOpacityPref.getId(), appearanceParams.getOpacity()); + settings.setPreferenceForAllModes(defaultCornerRadiusPref.getId(), appearanceParams.getCornerRadius()); + } else { + defaultSizePref.set(appearanceParams.getSize()); + defaultOpacityPref.set(appearanceParams.getOpacity()); + defaultCornerRadiusPref.set(appearanceParams.getCornerRadius()); + } + } + + private void updateContent() { + updateCards(); + updateButtons(); + } + + private void updateCards() { + for (BaseCard card : cards) { + card.update(); + } + } + + private void updateButtons() { + applyButton.setEnabled(!Algorithms.objectEquals(originalAppearanceParams, appearanceParams)); + } + + private void resetAppearance() { + appearanceParams.setSize(defaultSizePref.getDefaultValue()); + appearanceParams.setOpacity(defaultOpacityPref.getDefaultValue()); + appearanceParams.setCornerRadius(defaultCornerRadiusPref.getDefaultValue()); + updateContent(); + } + + @NonNull + private ButtonAppearanceParams createAppearanceParams() { + return new ButtonAppearanceParams(null, defaultSizePref.get(), + defaultOpacityPref.get(), defaultCornerRadiusPref.get()); + } + + @Override + public void onCardPressed(@NonNull BaseCard card) { + updateButtons(); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + appearanceParams.saveToBundle(outState); + originalAppearanceParams.saveToBundle(outState); + } + + @Override + public void onResume() { + super.onResume(); + + MapActivity activity = getMapActivity(); + if (activity != null) { + activity.disableDrawer(); + } + } + + @Override + public void onPause() { + super.onPause(); + + MapActivity activity = getMapActivity(); + if (activity != null) { + activity.enableDrawer(); + } + } + + @Nullable + public MapActivity getMapActivity() { + FragmentActivity activity = getActivity(); + return activity instanceof MapActivity ? ((MapActivity) activity) : null; + } + + @NonNull + public MapActivity requireMapActivity() { + FragmentActivity activity = getActivity(); + if (!(activity instanceof MapActivity)) { + throw new IllegalStateException("Fragment " + this + " not attached to an activity."); + } + return (MapActivity) activity; + } + + public static void showInstance(@NonNull FragmentManager manager) { + if (AndroidUtils.isFragmentCanBeAdded(manager, TAG)) { + DefaultButtonsAppearanceFragment fragment = new DefaultButtonsAppearanceFragment(); + manager.beginTransaction() + .replace(R.id.fragmentContainer, fragment, TAG) + .addToBackStack(TAG) + .commitAllowingStateLoss(); + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/DefaultMapButtonsFragment.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/DefaultMapButtonsFragment.java index 66a8b29b750..5332e64b069 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/DefaultMapButtonsFragment.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/DefaultMapButtonsFragment.java @@ -1,5 +1,6 @@ package net.osmand.plus.views.mapwidgets.configure.buttons; +import android.content.Context; import android.os.Bundle; import android.view.View; import android.widget.ImageView; @@ -69,8 +70,22 @@ public void onItemClick(@NonNull MapButtonState buttonState) { protected void showOptionsMenu(@NonNull View view) { List items = new ArrayList<>(); - items.add(new PopUpMenuItem.Builder(view.getContext()) + Context context = view.getContext(); + + items.add(new PopUpMenuItem.Builder(context) + .setTitleId(R.string.shared_string_appearance) + .setIcon(getContentIcon(R.drawable.ic_action_appearance)) + .setOnClickListener(v -> { + FragmentActivity activity = getActivity(); + if (activity != null) { + FragmentManager manager = activity.getSupportFragmentManager(); + DefaultButtonsAppearanceFragment.showInstance(manager); + } + }).create()); + + items.add(new PopUpMenuItem.Builder(context) .setTitle(getString(R.string.reset_to_default)) + .showTopDivider(true) .setIcon(getContentIcon(R.drawable.ic_action_reset)) .setOnClickListener(v -> { FragmentActivity activity = getActivity(); @@ -80,7 +95,7 @@ protected void showOptionsMenu(@NonNull View view) { } }).create()); - items.add(new PopUpMenuItem.Builder(view.getContext()) + items.add(new PopUpMenuItem.Builder(context) .setTitle(getString(R.string.copy_from_other_profile)) .setIcon(getContentIcon(R.drawable.ic_action_copy)) .setOnClickListener(v -> { @@ -96,7 +111,6 @@ protected void showOptionsMenu(@NonNull View view) { displayData.anchorView = view; displayData.menuItems = items; displayData.nightMode = nightMode; - displayData.layoutId = R.layout.simple_popup_menu_item; PopUpMenu.show(displayData); } diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonCard.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonCard.java index 881c003a50e..3ec3bde9c55 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonCard.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonCard.java @@ -10,6 +10,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.quickaction.ButtonAppearanceParams; @@ -28,7 +29,7 @@ public class MapButtonCard extends MapBaseCard { private MapButton mapButton; public MapButtonCard(@NonNull MapActivity mapActivity, @NonNull MapButtonState buttonState, - @Nullable ButtonAppearanceParams customAppearanceParams) { + @Nullable ButtonAppearanceParams customAppearanceParams) { super(mapActivity, false); this.buttonState = buttonState; this.customAppearanceParams = customAppearanceParams; @@ -45,7 +46,7 @@ protected void updateContent() { container.removeAllViews(); setupButton(container); - setupButtonBackground(container); + setupButtonBackground(container, nightMode); } public void setupButton(@NonNull ViewGroup container) { @@ -71,7 +72,8 @@ public void updateButton(@NonNull ButtonAppearanceParams appearanceParams) { } } - private void setupButtonBackground(@NonNull View view) { + public static void setupButtonBackground(@NonNull View view, boolean nightMode) { + OsmandApplication app = (OsmandApplication) view.getContext().getApplicationContext(); RenderingRulesStorage renderer = app.getRendererRegistry().getCurrentSelectedRenderer(); if (renderer != null) { MapRenderRepositories maps = app.getResourceManager().getRenderer(); diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonState.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonState.java index e6ca647e305..a0dbe391c38 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonState.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/configure/buttons/MapButtonState.java @@ -1,6 +1,7 @@ package net.osmand.plus.views.mapwidgets.configure.buttons; import static net.osmand.plus.quickaction.ButtonAppearanceParams.BIG_SIZE_DP; +import static net.osmand.plus.quickaction.ButtonAppearanceParams.ORIGINAL_VALUE; import static net.osmand.plus.quickaction.ButtonAppearanceParams.ROUND_RADIUS_DP; import static net.osmand.plus.quickaction.ButtonAppearanceParams.TRANSPARENT_ALPHA; @@ -18,6 +19,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.quickaction.ButtonAppearanceParams; +import net.osmand.plus.quickaction.MapButtonsHelper; import net.osmand.plus.render.RenderingIcons; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; @@ -60,11 +62,11 @@ public MapButtonState(@NonNull OsmandApplication app, @NonNull String id) { this.allPreferences = new ArrayList<>(); this.iconPref = addPreference(settings.registerStringPreference(id + "_icon", null)).makeProfile().cache(); - this.sizePref = addPreference(settings.registerIntPreference(id + "_size", -1)).makeProfile().cache(); - this.opacityPref = addPreference(settings.registerFloatPreference(id + "_opacity", -1)).makeProfile().cache(); - this.cornerRadiusPref = addPreference(settings.registerIntPreference(id + "_corner_radius", -1)).makeProfile().cache(); - this.portraitPositionPref = addPreference(settings.registerLongPreference(id + "_position_portrait", -1)).makeProfile().cache(); - this.landscapePositionPref = addPreference(settings.registerLongPreference(id + "_position_landscape", -1)).makeProfile().cache(); + this.sizePref = addPreference(settings.registerIntPreference(id + "_size", ORIGINAL_VALUE)).makeProfile().cache(); + this.opacityPref = addPreference(settings.registerFloatPreference(id + "_opacity", ORIGINAL_VALUE)).makeProfile().cache(); + this.cornerRadiusPref = addPreference(settings.registerIntPreference(id + "_corner_radius", ORIGINAL_VALUE)).makeProfile().cache(); + this.portraitPositionPref = addPreference(settings.registerLongPreference(id + "_position_portrait", ORIGINAL_VALUE)).makeProfile().cache(); + this.landscapePositionPref = addPreference(settings.registerLongPreference(id + "_position_landscape", ORIGINAL_VALUE)).makeProfile().cache(); this.positionSize = setupButtonPosition(new ButtonPositionSize(getId())); this.defaultPositionSize = setupButtonPosition(new ButtonPositionSize(getId()));