diff --git a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java index a8069ce9da..7aa4389f67 100644 --- a/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java +++ b/OsmAnd-java/src/main/java/net/osmand/util/Algorithms.java @@ -119,6 +119,28 @@ public class Algorithms { return def; } + public static int parseIntSilently(String input, int def) { + if (input != null && input.length() > 0) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + return def; + } + } + return def; + } + + public static double parseDoubleSilently(String input, double def) { + if (input != null && input.length() > 0) { + try { + return Double.parseDouble(input); + } catch (NumberFormatException e) { + return def; + } + } + return def; + } + public static boolean isFirstPolygonInsideSecond(List firstPolygon, List secondPolygon) { for (LatLon point : firstPolygon) { @@ -147,28 +169,6 @@ public class Algorithms { } return result; } - - public static int parseIntSilently(String input, int def) { - if (input != null && input.length() > 0) { - try { - return Integer.parseInt(input); - } catch (NumberFormatException e) { - return def; - } - } - return def; - } - - public static double parseDoubleSilently(String input, double def) { - if (input != null && input.length() > 0) { - try { - return Double.parseDouble(input); - } catch (NumberFormatException e) { - return def; - } - } - return def; - } public static String getFileNameWithoutExtension(File f) { return getFileNameWithoutExtension(f.getName()); @@ -788,6 +788,10 @@ public class Algorithms { return false; } + public static boolean isInt(double d) { + return (d == Math.floor(d)) && !Double.isInfinite(d); + } + public static boolean isInt(String value) { int length = value.length(); for (int i = 0; i < length; i++) { diff --git a/OsmAnd/res/layout/track_split_interval.xml b/OsmAnd/res/layout/track_split_interval.xml index ae823012cd..2fdcfc467c 100644 --- a/OsmAnd/res/layout/track_split_interval.xml +++ b/OsmAnd/res/layout/track_split_interval.xml @@ -5,57 +5,25 @@ android:layout_height="match_parent" android:orientation="vertical"> - + android:layout_height="wrap_content" + android:ellipsize="end" + android:paddingLeft="@dimen/content_padding" + android:paddingRight="@dimen/content_padding" + android:textColor="?android:textColorSecondary" + android:textSize="@dimen/default_desc_text_size" + android:lineSpacingMultiplier="@dimen/bottom_sheet_text_spacing_multiplier" + android:text="@string/gpx_split_interval_descr" + android:paddingEnd="@dimen/content_padding" + android:paddingStart="@dimen/content_padding" /> - + - - - - - + @@ -143,6 +111,7 @@ android:layout_marginStart="@dimen/content_padding" android:layout_marginEnd="@dimen/content_padding" android:layout_marginBottom="@dimen/content_padding" + android:layout_marginTop="@dimen/content_padding_half" android:lineSpacingExtra="@dimen/line_spacing_extra_description" android:text="@string/gpx_split_interval_none_descr" android:textColor="?android:textColorSecondary" diff --git a/OsmAnd/res/layout/track_width_card.xml b/OsmAnd/res/layout/track_width_card.xml index 3710ad43b4..e919df6e29 100644 --- a/OsmAnd/res/layout/track_width_card.xml +++ b/OsmAnd/res/layout/track_width_card.xml @@ -52,7 +52,6 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:paddingStart="@dimen/content_padding" - android:paddingTop="@dimen/content_padding" android:paddingEnd="@dimen/content_padding"> + android:textColor="?android:textColorSecondary" + android:textSize="@dimen/default_list_text_size" + tools:text="1" /> + + - - + android:textSize="@dimen/default_list_text_size" + tools:text="1" /> + android:textSize="@dimen/default_list_text_size" + tools:text="24" /> diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index a93a58dec7..66d7b6ec59 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -12,6 +12,7 @@ --> + Interval Show/hide Copy POI name The recording will be continued. diff --git a/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java b/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java index 6a52fe9804..883cc908c5 100644 --- a/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java +++ b/OsmAnd/src/net/osmand/plus/OsmAndFormatter.java @@ -121,13 +121,22 @@ public class OsmAndFormatter { } public static String getFormattedTimeInterval(OsmandApplication app, double interval) { + String unitsStr; + double intervalInUnits; if (interval < 60) { - return interval + " " + app.getString(R.string.int_seconds); + unitsStr = app.getString(R.string.shared_string_sec); + intervalInUnits = interval; } else if (interval % 60 == 0) { - return (interval / 60) + " " + app.getString(R.string.int_min); + unitsStr = app.getString(R.string.int_min); + intervalInUnits = (interval / 60); } else { - return (interval / 60f) + " " + app.getString(R.string.int_min); + unitsStr = app.getString(R.string.int_min); + intervalInUnits = (interval / 60f); } + String formattedInterval = Algorithms.isInt(intervalInUnits) ? + String.format(Locale.US, "%d", (long) intervalInUnits) : + String.format("%s", intervalInUnits); + return formattedInterval + " " + unitsStr; } public static String getFormattedDistanceInterval(OsmandApplication app, double interval) { diff --git a/OsmAnd/src/net/osmand/plus/UiUtilities.java b/OsmAnd/src/net/osmand/plus/UiUtilities.java index 4e12388b5c..e6ce06ca35 100644 --- a/OsmAnd/src/net/osmand/plus/UiUtilities.java +++ b/OsmAnd/src/net/osmand/plus/UiUtilities.java @@ -635,8 +635,9 @@ public class UiUtilities { } int activeDisableColor = getColorWithAlpha(activeColor, 0.25f); ColorStateList activeCsl = new ColorStateList(states, new int[] {activeColor, activeDisableColor}); - int inactiveColor = ContextCompat.getColor(ctx, nightMode ? R.color.icon_color_default_dark : R.color.icon_color_secondary_light); - ColorStateList inactiveCsl = new ColorStateList(states, new int[] {inactiveColor, inactiveColor}); + int inactiveColor = getColorWithAlpha(activeColor, 0.5f); + int inactiveDisableColor = ContextCompat.getColor(ctx, nightMode ? R.color.icon_color_default_dark : R.color.icon_color_secondary_light); + ColorStateList inactiveCsl = new ColorStateList(states, new int[] {inactiveColor, inactiveDisableColor}); slider.setTrackActiveTintList(activeCsl); slider.setTrackInactiveTintList(inactiveCsl); slider.setHaloTintList(activeCsl); diff --git a/OsmAnd/src/net/osmand/plus/track/ColorsCard.java b/OsmAnd/src/net/osmand/plus/track/ColorsCard.java index b3970b5a7b..e6da7e736f 100644 --- a/OsmAnd/src/net/osmand/plus/track/ColorsCard.java +++ b/OsmAnd/src/net/osmand/plus/track/ColorsCard.java @@ -28,10 +28,16 @@ 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; - public static final int MINIMUM_CONTRAST_RATIO = 3; + public static final double MINIMUM_CONTRAST_RATIO = 1.5; private static final Log log = PlatformUtil.getLog(TrackColoringCard.class); @@ -139,7 +145,11 @@ public class ColorsCard extends BaseCard implements ColorPickerListener { Drawable transparencyIcon = getTransparencyIcon(app, color); Drawable colorIcon = app.getUIUtilities().getPaintedIcon(R.drawable.bg_point_circle, color); Drawable layeredIcon = UiUtilities.getLayeredIcon(transparencyIcon, colorIcon); - double contrastRatio = ColorUtils.calculateContrast(color, ContextCompat.getColor(app, nightMode ? R.color.card_and_list_background_dark : R.color.card_and_list_background_light)); + int listBgColorId = nightMode ? + R.color.card_and_list_background_dark : + R.color.card_and_list_background_light; + int listBgColor = getResolvedColor(listBgColorId); + double contrastRatio = ColorUtils.calculateContrast(color, listBgColor); if (contrastRatio < MINIMUM_CONTRAST_RATIO) { backgroundCircle.setBackgroundResource(nightMode ? R.drawable.circle_contour_bg_dark : R.drawable.circle_contour_bg_light); } diff --git a/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java b/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java index 15d94db2cc..7d36e90b27 100644 --- a/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/track/SplitIntervalBottomSheet.java @@ -3,7 +3,7 @@ package net.osmand.plus.track; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; -import android.widget.RadioGroup; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; @@ -25,6 +25,9 @@ import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.LongDescriptionItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.widgets.MultiStateToggleButton; +import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; import org.apache.commons.logging.Log; @@ -93,7 +96,6 @@ public class SplitIntervalBottomSheet extends MenuBottomSheetDialogFragment { @Override public void createMenuItems(Bundle savedInstanceState) { items.add(new TitleItem(getString(R.string.gpx_split_interval))); - items.add(new LongDescriptionItem(getString(R.string.gpx_split_interval_descr))); LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); View view = themedInflater.inflate(R.layout.track_split_interval, null); @@ -106,29 +108,10 @@ public class SplitIntervalBottomSheet extends MenuBottomSheetDialogFragment { selectedSplitValue = view.findViewById(R.id.split_value_tv); splitIntervalNoneDescr = view.findViewById(R.id.split_interval_none_descr); - UiUtilities.setupSlider(slider, nightMode, null); + UiUtilities.setupSlider(slider, nightMode, null, true); - RadioGroup splitTypeGroup = view.findViewById(R.id.split_type); - if (selectedSplitType == GpxSplitType.NO_SPLIT) { - splitTypeGroup.check(R.id.no_split); - } else if (selectedSplitType == GpxSplitType.TIME) { - splitTypeGroup.check(R.id.time_split); - } else if (selectedSplitType == GpxSplitType.DISTANCE) { - splitTypeGroup.check(R.id.distance_split); - } - splitTypeGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - if (checkedId == R.id.no_split) { - selectedSplitType = GpxSplitType.NO_SPLIT; - } else if (checkedId == R.id.time_split) { - selectedSplitType = GpxSplitType.TIME; - } else if (checkedId == R.id.distance_split) { - selectedSplitType = GpxSplitType.DISTANCE; - } - updateSlider(); - } - }); + LinearLayout radioGroup = (LinearLayout) view.findViewById(R.id.custom_radio_buttons); + setupTypeRadioGroup(radioGroup); SimpleBottomSheetItem titleItem = (SimpleBottomSheetItem) new SimpleBottomSheetItem.Builder() .setCustomView(view) @@ -136,6 +119,35 @@ public class SplitIntervalBottomSheet extends MenuBottomSheetDialogFragment { items.add(titleItem); } + private void setupTypeRadioGroup(LinearLayout buttonsContainer) { + RadioItem none = createRadioButton(GpxSplitType.NO_SPLIT, R.string.shared_string_none); + RadioItem time = createRadioButton(GpxSplitType.TIME, R.string.shared_string_time); + RadioItem distance = createRadioButton(GpxSplitType.DISTANCE, R.string.distance); + + MultiStateToggleButton radioGroup = new MultiStateToggleButton(app, buttonsContainer, nightMode); + radioGroup.setItems(none, time, distance); + + if (selectedSplitType == GpxSplitType.NO_SPLIT) { + radioGroup.setSelectedItem(none); + } else { + radioGroup.setSelectedItem(selectedSplitType == GpxSplitType.TIME ? time : distance); + } + } + + private RadioItem createRadioButton(final GpxSplitType splitType, int titleId) { + String title = app.getString(titleId); + RadioItem item = new RadioItem(title); + item.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { + selectedSplitType = splitType; + updateSlider(); + return true; + } + }); + return item; + } + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); diff --git a/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java index 4642175b09..43b0c78eca 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java @@ -409,15 +409,17 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement RotatedTileBox tb = mapActivity.getMapView().getCurrentRotatedTileBox().copy(); int tileBoxWidthPx = 0; int tileBoxHeightPx = 0; + int marginLeftPx = 0; if (!isPortrait()) { tileBoxWidthPx = tb.getPixWidth() - getWidth(); + marginLeftPx = getWidth(); } else { int fHeight = getViewHeight() - y - AndroidUtils.getStatusBarHeight(mapActivity); tileBoxHeightPx = tb.getPixHeight() - fHeight; } if (r.left != 0 && r.right != 0) { - mapActivity.getMapView().fitRectToMap(r.left, r.right, r.top, r.bottom, tileBoxWidthPx, tileBoxHeightPx, 0); + mapActivity.getMapView().fitRectToMap(r.left, r.right, r.top, r.bottom, tileBoxWidthPx, tileBoxHeightPx, 0, marginLeftPx); } } } diff --git a/OsmAnd/src/net/osmand/plus/track/TrackWidthCard.java b/OsmAnd/src/net/osmand/plus/track/TrackWidthCard.java index 2a8f34c024..9e98414d34 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackWidthCard.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackWidthCard.java @@ -45,6 +45,7 @@ public class TrackWidthCard extends BaseCard { private GpxWidthAdapter widthAdapter; private View sliderContainer; + private RecyclerView groupRecyclerView; public TrackWidthCard(MapActivity mapActivity, TrackDrawInfo trackDrawInfo, OnNeedScrollListener onNeedScrollListener) { @@ -65,9 +66,10 @@ public class TrackWidthCard extends BaseCard { updateCustomWidthSlider(); widthAdapter = new GpxWidthAdapter(appearanceItems); - RecyclerView groupRecyclerView = view.findViewById(R.id.recycler_view); + groupRecyclerView = view.findViewById(R.id.recycler_view); groupRecyclerView.setAdapter(widthAdapter); groupRecyclerView.setLayoutManager(new LinearLayoutManager(app, RecyclerView.HORIZONTAL, false)); + scrollMenuToSelectedItem(); AndroidUiHelper.updateVisibility(view.findViewById(R.id.top_divider), isShowDivider()); } @@ -155,7 +157,7 @@ public class TrackWidthCard extends BaseCard { } } }); - UiUtilities.setupSlider(widthSlider, nightMode, null); + UiUtilities.setupSlider(widthSlider, nightMode, null, true); ScrollUtils.addOnGlobalLayoutListener(sliderContainer, new Runnable() { @Override public void run() { @@ -175,6 +177,13 @@ public class TrackWidthCard extends BaseCard { mapActivity.refreshMap(); } + private void scrollMenuToSelectedItem() { + int position = widthAdapter.getItemPosition(selectedItem); + if (position != -1) { + groupRecyclerView.scrollToPosition(position); + } + } + private class GpxWidthAdapter extends RecyclerView.Adapter { private List items; @@ -219,6 +228,7 @@ public class TrackWidthCard extends BaseCard { updateHeader(); updateCustomWidthSlider(); + scrollMenuToSelectedItem(); CardListener listener = getListener(); if (listener != null) { diff --git a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java index 475e7879aa..ad1a8bb190 100644 --- a/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java +++ b/OsmAnd/src/net/osmand/plus/views/OsmandMapTileView.java @@ -638,8 +638,7 @@ public class OsmandMapTileView implements IMapDownloaderCallback { } else if (mapPosition == OsmandSettings.LANDSCAPE_MIDDLE_RIGHT_CONSTANT) { ratiox = 0.7f; } else { - boolean isLayoutRtl = AndroidUtils.isLayoutRtl(application); - ratiox = mapPositionX == 0 ? 0.5f : (isLayoutRtl ? 0.25f : 0.75f); + ratiox = mapPositionX == 0 ? 0.5f : (isLayoutRtl() ? 0.25f : 0.75f); } final int cy = (int) (ratioy * view.getHeight()); final int cx = (int) (ratiox * view.getWidth()); @@ -959,7 +958,8 @@ public class OsmandMapTileView implements IMapDownloaderCallback { if (tileBoxWidthPx > 0) { tbw = (int) (tileBoxWidthPx * border); if (marginLeftPx > 0) { - dx = (tb.getPixWidth() - tileBoxWidthPx) / 2 - marginLeftPx; + int offset = (tb.getPixWidth() - tileBoxWidthPx) / 2 - marginLeftPx; + dx = isLayoutRtl() ? -offset : offset; } } else if (tileBoxHeightPx > 0) { tbh = (int) (tileBoxHeightPx * border); @@ -1425,4 +1425,8 @@ public class OsmandMapTileView implements IMapDownloaderCallback { public Context getContext() { return activity; } + + public boolean isLayoutRtl() { + return AndroidUtils.isLayoutRtl(application); + } } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java index 945796a726..ed425c0993 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapControlsLayer.java @@ -876,9 +876,13 @@ public class MapControlsLayer extends OsmandMapLayer { routePlanningBtn.updateVisibility(showBottomMenuButtons); menuControl.updateVisibility(showBottomMenuButtons); - boolean showZoomButtons = !routeDialogOpened && !shouldHideTopControls && !isInTrackAppearanceMode() - && (!isInGpxApproximationMode() || !isPotrait()) - && !isInFollowTrackMode() && (!isInChoosingRoutesMode() || !isInWaypointsChoosingMode() || !portrait); + boolean additionalDialogsHide = !isInGpxApproximationMode() + && !isInTrackAppearanceMode() + && !isInChoosingRoutesMode() + && !isInWaypointsChoosingMode(); + boolean showZoomButtons = !routeDialogOpened && !shouldHideTopControls + && !isInFollowTrackMode() + && (additionalDialogsHide || !portrait); mapZoomIn.updateVisibility(showZoomButtons); mapZoomOut.updateVisibility(showZoomButtons); @@ -1020,9 +1024,14 @@ public class MapControlsLayer extends OsmandMapLayer { public void updateMyLocationVisibility(MapHudButton backToLocationControl, RoutingHelper rh, boolean dialogOpened) { boolean tracked = mapActivity.getMapViewTrackingUtilities().isMapLinkedToLocation(); - boolean visible = !(tracked && rh.isFollowingMode()) && (!isInGpxApproximationMode() || !isPotrait()); + boolean visible = !(tracked && rh.isFollowingMode()); + boolean additionalDialogsHide = !isInTrackAppearanceMode() + && !isInGpxApproximationMode() + && !isInChoosingRoutesMode() + && !isInWaypointsChoosingMode() + && !isInFollowTrackMode(); backToLocationControl.updateVisibility(visible && !dialogOpened && !isInPlanRouteMode() - && !isInTrackAppearanceMode() && (!isInChoosingRoutesMode() || !isInWaypointsChoosingMode() || !isInFollowTrackMode() || !isPotrait())); + && (additionalDialogsHide || !isPotrait())); } public boolean onSingleTap(PointF point, RotatedTileBox tileBox) {