diff --git a/OsmAnd/res/layout/speedometer_widget.xml b/OsmAnd/res/layout/speedometer_widget.xml index 99fc6e75390..07e60f9f141 100644 --- a/OsmAnd/res/layout/speedometer_widget.xml +++ b/OsmAnd/res/layout/speedometer_widget.xml @@ -3,11 +3,10 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - android:padding="@dimen/content_padding_medium" - android:clipToPadding="false" android:clipChildren="false" - > + android:clipToPadding="false" + android:orientation="horizontal" + android:padding="@dimen/content_padding_medium"> + android:paddingVertical="9dp"> + tools:ignore="SpUsage" + tools:text="KM/H" /> @@ -59,20 +57,22 @@ android:layout_width="72dp" android:layout_height="72dp" android:layout_gravity="center" - android:gravity="center" android:layout_marginVertical="9dp" - android:layout_marginStart="-6dp" - android:layout_marginEnd="6dp" android:background="@drawable/speed_limit_shape" android:elevation="2dp" + android:gravity="center" android:orientation="vertical" + android:paddingHorizontal="9dp" + android:paddingVertical="9dp" + android:translationX="-6dp" android:translationZ="2dp"> - + diff --git a/OsmAnd/res/xml/monitoring_settings.xml b/OsmAnd/res/xml/monitoring_settings.xml index 7eb551315ae..3131e53891d 100644 --- a/OsmAnd/res/xml/monitoring_settings.xml +++ b/OsmAnd/res/xml/monitoring_settings.xml @@ -124,7 +124,7 @@ enabledCommands = plugin.getTRIP_RECORDING_VEHICLE_METRICS().getStringsListForProfile(getSelectedAppMode()); if (!Algorithms.isEmpty(enabledCommands)) { summary = String.valueOf(enabledCommands.size()); + prefIcon = getActiveIcon(iconId); } } preference.setSummary(summary); @@ -288,8 +294,7 @@ public void setupObdRecordingPref() { preference.setVisible(purchased && PluginsHelper.isEnabled(VehicleMetricsPlugin.class)); promo.setVisible(!purchased); - int iconId = R.drawable.ic_action_car_info; - preference.setIcon(getPersistentPrefIcon(iconId)); + preference.setIcon(prefIcon); promo.setIcon(getContentIcon(iconId)); } diff --git a/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingAdapter.java b/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingAdapter.java index 2fd42ddf824..3f68515190f 100644 --- a/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingAdapter.java +++ b/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingAdapter.java @@ -3,6 +3,7 @@ import static net.osmand.plus.plugins.monitoring.VehicleMetricsRecordingFragment.*; import android.annotation.SuppressLint; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -20,6 +21,7 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.utils.AndroidUtils; import net.osmand.plus.utils.ColorUtilities; import net.osmand.plus.utils.UiUtilities; import net.osmand.shared.obd.OBDCommand; @@ -185,11 +187,13 @@ private void updateIcon(boolean checked, @DrawableRes int iconId) { class CategoryHolder extends RecyclerView.ViewHolder { private final View itemView; private final TextView textView; + private final ImageView imageView; public CategoryHolder(@NonNull View itemView) { super(itemView); this.itemView = itemView; textView = itemView.findViewById(android.R.id.title); + imageView = itemView.findViewById(android.R.id.icon); } public void bindView(@NonNull VehicleMetricsRecordingCategory category) { @@ -199,6 +203,13 @@ public void bindView(@NonNull VehicleMetricsRecordingCategory category) { int dp36 = getDimen(app, R.dimen.showAllButtonHeight); itemView.setMinimumHeight(dp36); textView.setText(category.titleId); + + LinearLayout.LayoutParams currentImageParams = (LinearLayout.LayoutParams) imageView.getLayoutParams(); + int size = app.getResources().getDimensionPixelSize(R.dimen.standard_icon_size); + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(size, size); + params.gravity = Gravity.CENTER; + params.setMargins(currentImageParams.leftMargin, currentImageParams.topMargin, AndroidUtils.dpToPx(app, 8), currentImageParams.bottomMargin); + imageView.setLayoutParams(params); } } diff --git a/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingFragment.java b/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingFragment.java index 0332df4e9f4..c5764ae7981 100644 --- a/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingFragment.java +++ b/OsmAnd/src/net/osmand/plus/plugins/monitoring/VehicleMetricsRecordingFragment.java @@ -11,6 +11,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.Window; import android.widget.ImageView; import android.widget.TextView; @@ -20,6 +21,7 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; @@ -82,6 +84,11 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c updateNightMode(); View view = themedInflater.inflate(R.layout.vehicle_metrics_recording_fragment, container, false); AndroidUtils.addStatusBarPadding21v(requireMyActivity(), view); + Window window = requireMapActivity().getWindow(); + if (window != null) { + window.setStatusBarColor(ContextCompat.getColor(requireMapActivity(), getStatusBarColorId())); + AndroidUiHelper.setStatusBarContentColor(window.getDecorView(), nightMode); + } toolbar = view.findViewById(R.id.toolbar); AppBarLayout appBarLayout = view.findViewById(R.id.app_bar); @@ -108,7 +115,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c } updateSelectAllButton(); - setupButtons(view); + setupButtons(); setupToolbar(); setupItems(); @@ -116,7 +123,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c } @SuppressLint("NotifyDataSetChanged") - private void setupButtons(@NonNull View view) { + private void setupButtons() { selectAllButton.setOnClickListener(v -> { if (areAllCommandsSelected()) { selectedCommands.clear(); diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index 2740eedd197..812b7b47904 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -318,7 +318,7 @@ public boolean setPreference(String key, Object value, ApplicationMode mode) { } else if (preference instanceof ListStringPreference listStringPreference) { if (value instanceof List || (value == null && listStringPreference.isNullSupported(mode))) { if (value == null) { - listStringPreference.setStringsListForProfile(mode,null); + listStringPreference.setStringsListForProfile(mode, null); } else { List list = (List) value; boolean isListOfString = list.stream().allMatch(element -> element instanceof String); diff --git a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java index 66ea78e5e30..d000a41abaa 100644 --- a/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java +++ b/OsmAnd/src/net/osmand/plus/views/mapwidgets/widgets/SpeedometerWidget.java @@ -55,6 +55,8 @@ import net.osmand.plus.views.mapwidgets.WidgetsVisibilityHelper; import net.osmand.util.Algorithms; +import java.util.List; + public class SpeedometerWidget { private final static int PREVIEW_VALUE = 85; @@ -84,6 +86,11 @@ public class SpeedometerWidget { private final static int SPEED_LIMIT_SIZE_L = 96; private final static int SPEED_LIMIT_DESCRIPTION_SIZE_USUAL = 11; private final static int SPEED_LIMIT_DESCRIPTION_SIZE_CANADA_S = 9; + private final static int SPEED_LIMIT_CONTAINER_US_PADDING_S = 7; + private final static int SPEED_LIMIT_CONTAINER_US_PADDING_M = 9; + private final static int SPEED_LIMIT_CONTAINER_US_PADDING_L = 12; + private final static int SPEED_LIMIT_VALUE_WEIGHT = 2; + private final static int SPEED_LIMIT_DESCRIPTION_WEIGHT = 1; private final static int SPEEDOMETER_PADDING_SIDE_S = 9; private final static int SPEEDOMETER_PADDING_SIDE_ML = 12; @@ -93,11 +100,6 @@ public class SpeedometerWidget { private final static int SPEEDOMETER_PADDING_BOTTOM_L = 12; private final static int SPEEDOMETER_PADDING_SIDE_AA = 12; private final static int SPEEDOMETER_PADDING_TOP_BOTTOM_AA = 9; - private final static int US_SPEED_LIMIT_BOTTOM = 18; - private final static int US_SPEED_LIMIT_BOTTOM_S = 14; - private final static int US_SPEED_LIMIT_DESCRIPTION_TOP_S = 18; - private final static int US_SPEED_LIMIT_DESCRIPTION_TOP_M = 24; - private final static int US_SPEED_LIMIT_DESCRIPTION_TOP_L = 28; private final static int SPEEDOMETER_UNIT_DESCR_CANADA_SIZE_S = 8; private final static int SHADOW_SIZE = 4; @@ -127,6 +129,8 @@ public class SpeedometerWidget { private float cachedSpeed = UNDEFINED_SPEED; private String cachedSpeedLimitText; private boolean lastNightMode; + private Integer cachedMetricSystem; + private DrivingRegion cachedRegion; @Nullable private Bitmap widgetBitmap; @@ -157,24 +161,29 @@ private void setupWidget() { return; } + boolean isUsaOrCanada = isUsaOrCanadaRegion(); LinearLayout.LayoutParams speedLimitValueParams = (LinearLayout.LayoutParams) speedLimitValueView.getLayoutParams(); speedLimitValueParams.setMargins(0, 0, 0, 0); speedLimitValueView.setLayoutParams(speedLimitValueParams); - AndroidUiHelper.updateVisibility(speedLimitDescription, false); + AndroidUiHelper.updateVisibility(speedLimitDescription, isUsaOrCanada); WidgetSize newWidgetSize = settings.SPEEDOMETER_SIZE.getModeValue(mode); - if (previousWidgetSize == newWidgetSize) { + DrivingRegion newDrivingRegion = settings.DRIVING_REGION.getModeValue(mode); + if (previousWidgetSize == newWidgetSize && cachedRegion == newDrivingRegion) { return; } previousWidgetSize = newWidgetSize; + cachedRegion = newDrivingRegion; LinearLayout.LayoutParams speedLimitLayoutParams = (LinearLayout.LayoutParams) speedLimitContainer.getLayoutParams(); speedLimitLayoutParams.gravity = Gravity.CENTER; LinearLayout.LayoutParams speedometerLayoutParams = (LinearLayout.LayoutParams) speedometerContainer.getLayoutParams(); - if (isUsaOrCanadaRegion()) { + if (isUsaOrCanada) { speedLimitValueParams.height = LinearLayout.LayoutParams.WRAP_CONTENT; - } else{ + } else { speedLimitValueParams.height = LinearLayout.LayoutParams.MATCH_PARENT; } + + int speedLimitPadding; speedLimitContainer.setLayoutParams(speedLimitLayoutParams); switch (previousWidgetSize) { case MEDIUM: @@ -187,6 +196,8 @@ private void setupWidget() { speedLimitLayoutParams.height = dpToPx(SPEED_LIMIT_SIZE_M); speedLimitLayoutParams.width = dpToPx(SPEED_LIMIT_SIZE_M); speedLimitValueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, SPEED_LIMIT_TEXT_SIZE_M); + speedLimitPadding = isUsaOrCanada ? dpToPx(SPEED_LIMIT_CONTAINER_US_PADDING_M) : 0; + speedLimitContainer.setPadding(speedLimitPadding, speedLimitPadding, speedLimitPadding, speedLimitPadding); speedLimitContainer.setLayoutParams(speedLimitLayoutParams); speedLimitDescription.setTextSize(TypedValue.COMPLEX_UNIT_SP, SPEED_LIMIT_DESCRIPTION_SIZE_USUAL); break; @@ -199,6 +210,8 @@ private void setupWidget() { speedLimitLayoutParams.height = dpToPx(SPEED_LIMIT_SIZE_L); speedLimitLayoutParams.width = dpToPx(SPEED_LIMIT_SIZE_L); + speedLimitPadding = isUsaOrCanada ? dpToPx(SPEED_LIMIT_CONTAINER_US_PADDING_L) : 0; + speedLimitContainer.setPadding(speedLimitPadding, speedLimitPadding, speedLimitPadding, speedLimitPadding); speedLimitContainer.setLayoutParams(speedLimitLayoutParams); speedLimitValueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, SPEED_LIMIT_TEXT_SIZE_L); speedLimitDescription.setTextSize(TypedValue.COMPLEX_UNIT_SP, SPEED_LIMIT_DESCRIPTION_SIZE_USUAL); @@ -212,6 +225,8 @@ private void setupWidget() { speedLimitLayoutParams.height = dpToPx(SPEED_LIMIT_SIZE_S); speedLimitLayoutParams.width = dpToPx(SPEED_LIMIT_SIZE_S); + speedLimitPadding = isUsaOrCanada ? dpToPx(SPEED_LIMIT_CONTAINER_US_PADDING_S) : 0; + speedLimitContainer.setPadding(speedLimitPadding, speedLimitPadding, speedLimitPadding, speedLimitPadding); speedLimitContainer.setLayoutParams(speedLimitLayoutParams); speedLimitValueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, SPEED_LIMIT_TEXT_SIZE_S); speedLimitDescription.setTextSize(TypedValue.COMPLEX_UNIT_SP, isCanadaRegion() ? SPEED_LIMIT_DESCRIPTION_SIZE_CANADA_S : SPEED_LIMIT_DESCRIPTION_SIZE_USUAL); @@ -255,6 +270,9 @@ public void updateInfo(@Nullable DrawSettings drawSettings, boolean drawBitmap, lastNightMode = nightMode; isChanged = true; } + if (isMetricUpdateNeeded()) { + isChanged = true; + } Location location = provider.getLastKnownLocation(); if (location != null && location.hasSpeed()) { float updateThreshold = cachedSpeed < LOW_SPEED_THRESHOLD_MPS @@ -344,6 +362,15 @@ public void updateInfo(@Nullable DrawSettings drawSettings, boolean drawBitmap, } } + public boolean isMetricUpdateNeeded() { + int metricSystem = app.getSettings().METRIC_SYSTEM.get().ordinal(); + if (cachedMetricSystem == null || cachedMetricSystem != metricSystem) { + cachedMetricSystem = metricSystem; + return true; + } + return false; + } + private boolean shouldShowWidget() { boolean showSpeedometerSetting = settings.SHOW_SPEEDOMETER.getModeValue(mode); if (visibilityHelper != null) { @@ -428,20 +455,19 @@ private void drawSpeedLimit(Canvas canvas, int textSize, float density, Rect ale float x = alertRect.left + (float) alertRect.width() / 2 - textPaint.measureText(cachedSpeedLimitText) / 2; float y; if (isUsaOrCanadaRegion()) { - int padding; - if (newWidgetSize == WidgetSize.SMALL) { - padding = US_SPEED_LIMIT_BOTTOM_S; - } else { - padding = US_SPEED_LIMIT_BOTTOM; - } - y = alertRect.bottom - padding * density; + Rect contentRect = getSpeedlimitContentRect(alertRect, newWidgetSize, density); + y = calculateYWeightPose(contentRect, SPEED_LIMIT_VALUE_WEIGHT, List.of(SPEED_LIMIT_DESCRIPTION_WEIGHT, SPEED_LIMIT_VALUE_WEIGHT), List.of(SPEED_LIMIT_DESCRIPTION_WEIGHT), textBounds); } else { y = alertRect.top + (float) alertRect.height() / 2 + (float) textBounds.height() / 2; } canvas.drawText(cachedSpeedLimitText, x, y, textPaint); } - private void drawSpeedLimitDescription(Canvas canvas, TextPaint textPaint, Rect alertRect, float density, WidgetSize newWidgetSize){ + private float calculateFrameTextHeight(float weight, float totalWeight, float rectHeight) { + return ((weight * rectHeight / totalWeight)); + } + + private void drawSpeedLimitDescription(Canvas canvas, TextPaint textPaint, Rect alertRect, float density, WidgetSize newWidgetSize) { float unitTextSize; String textLimitDescription; if (isUsaRegion()) { @@ -455,18 +481,51 @@ private void drawSpeedLimitDescription(Canvas canvas, TextPaint textPaint, Rect unitTextSize = SPEEDOMETER_UNIT_TEXT_SIZE; } } + Rect contentRect = getSpeedlimitContentRect(alertRect, newWidgetSize, density); textPaint.setTextSize(unitTextSize * density); - float xDescr = alertRect.left + (float) alertRect.width() / 2 - textPaint.measureText(textLimitDescription) / 2; - int padding; - if (newWidgetSize == WidgetSize.SMALL) { - padding = US_SPEED_LIMIT_DESCRIPTION_TOP_S; - } else if (newWidgetSize == WidgetSize.MEDIUM) { - padding = US_SPEED_LIMIT_DESCRIPTION_TOP_M; + Rect textBounds = new Rect(); + textPaint.getTextBounds(textLimitDescription, 0, textLimitDescription.length(), textBounds); + float xDescr = contentRect.left + (float) contentRect.width() / 2 - textPaint.measureText(textLimitDescription) / 2; + float yDescr = calculateYWeightPose(contentRect, SPEED_LIMIT_DESCRIPTION_WEIGHT, List.of(SPEED_LIMIT_VALUE_WEIGHT, SPEED_LIMIT_DESCRIPTION_WEIGHT), null, textBounds); + + canvas.drawText(textLimitDescription, xDescr, yDescr, textPaint); + } + + private float calculateYWeightPose(@NonNull Rect contentRect, int frameWeight, @NonNull List allWeights, + @Nullable List weightsAbove, @NonNull Rect textBounds) { + float halfTextHeight = textBounds.height() / 2.0f; + float totalWeight = allWeights.stream().reduce(0, Integer::sum); + + float totalHeightAbove = 0; + if (weightsAbove != null) { + for (Integer weight : weightsAbove) { + totalHeightAbove += calculateFrameTextHeight(weight, totalWeight, contentRect.height()); + } + } + + float calculatedFrameTextHeight = calculateFrameTextHeight(frameWeight, totalWeight, contentRect.height()); + float frameCenterPosition = calculatedFrameTextHeight / 2 + totalHeightAbove + halfTextHeight; + return frameCenterPosition + contentRect.top; + } + + private Rect getSpeedlimitContentRect(@NonNull Rect speedlimitRect, @NonNull WidgetSize newWidgetSize, float density) { + if (isUsaOrCanadaRegion()) { + int padding; + if (newWidgetSize == WidgetSize.SMALL) { + padding = SPEED_LIMIT_CONTAINER_US_PADDING_S; + } else if (newWidgetSize == WidgetSize.MEDIUM) { + padding = SPEED_LIMIT_CONTAINER_US_PADDING_M; + } else { + padding = SPEED_LIMIT_CONTAINER_US_PADDING_L; + } + float scaledPadding = padding * density; + return new Rect((int) (speedlimitRect.left + scaledPadding), + (int) (speedlimitRect.top + scaledPadding), + (int) (speedlimitRect.right - scaledPadding), + (int) (speedlimitRect.bottom - scaledPadding)); } else { - padding = US_SPEED_LIMIT_DESCRIPTION_TOP_L; + return speedlimitRect; } - float yDescr = alertRect.top + padding * density; - canvas.drawText(textLimitDescription, xDescr, yDescr, textPaint); } private void drawCurrentSpeed(Canvas canvas, int textSize, Rect speedArea, float density, boolean speedExceed) { @@ -508,7 +567,7 @@ private void drawCurrentSpeed(Canvas canvas, int textSize, Rect speedArea, float canvas.drawText(formattedSpeed.value, x, y, textPaint); } - private int getSpeedLimitColor(boolean nightMode){ + private int getSpeedLimitColor(boolean nightMode) { if (isUsaOrCanadaRegion() || isEuropeRegion()) { return app.getColor(nightMode ? R.color.widgettext_night : R.color.widgettext_day); } else { @@ -609,15 +668,10 @@ private void setSpeedLimitText(String value) { } private void setSpeedLimitDescription() { - boolean visible = false; - if (isUsaOrCanadaRegion()) { - if (speedLimitDescription != null) { - String textLimitDescription = app.getString(isCanadaRegion() ? R.string.speedometer_maximum : R.string.shared_string_limit).toUpperCase(); - speedLimitDescription.setText(textLimitDescription); - visible = true; - } + if (isUsaOrCanadaRegion() && speedLimitDescription != null) { + String textLimitDescription = app.getString(isCanadaRegion() ? R.string.speedometer_maximum : R.string.shared_string_limit).toUpperCase(); + speedLimitDescription.setText(textLimitDescription); } - AndroidUiHelper.updateVisibility(speedLimitDescription, visible); } private boolean isUsaOrCanadaRegion() {