Implement widget for the current speed #9738

Open
TobiPeterG wants to merge 1 commit from TobiPeterG/cur-speed-widget into master
14 changed files with 501 additions and 252 deletions

View file

View file

@ -249,7 +249,7 @@ public class NavigationController implements TrafficManager.TrafficCallback,
{
final Location location = LocationHelper.from(mFrame.getContext()).getSavedLocation();
if (location == null) {
mSpeedLimit.setSpeedLimitMps(0);
mSpeedLimit.setSpeedLimitMps(-1);
return;
}
mSpeedLimit.setCurrentSpeed(location.getSpeed());

View file

@ -1,21 +1,28 @@
package app.organicmaps.widget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.View;
import android.view.WindowInsets;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import app.organicmaps.Framework;
import app.organicmaps.R;
import app.organicmaps.util.StringUtils;
@ -28,17 +35,27 @@ public class SpeedLimitView extends View
@ColorInt
int TEXT_COLOR = Color.BLACK;
@ColorInt
int UNLIMITED_BORDER_COLOR = Color.BLACK;
@ColorInt
int TEXT_ALERT_COLOR = Color.WHITE;
float BORDER_WIDTH_RATIO = 0.1f;
float BORDER_WIDTH_RATIO = 0.2f;
float BORDER_RADIUS_RATIO = 0.95f;
}
@ColorInt
private final int mBackgroundColor;
@ColorInt
private final int mBackgroundColorWidget;
@ColorInt
private final int mBorderColor;
@ColorInt
private final int mUnlimitedBorderColor;
@ColorInt
private final int mAlertColor;
@ -53,7 +70,23 @@ public class SpeedLimitView extends View
@NonNull
private final Paint mSignBorderPaint;
@NonNull
private final Paint mSignUnlimitedBorderPaint;
@NonNull
private final Paint mTextPaint;
@NonNull
private final Paint mCurrentSpeedBorderPaint;
@NonNull
private final Paint mCurrentSpeedBackgroundPaint;
@NonNull
private final Paint mCurrentSpeedTextPaint;
@NonNull
private final Paint mCurrentSpeedUnitsTextPaint; // Added for units text
@NonNull
private final Paint mBackgroundPaint;
@NonNull
private final Paint mWidgetBackgroundPaint;
private float mSpeedLimitAlpha;
private float mWidth;
private float mHeight;
@ -64,18 +97,37 @@ public class SpeedLimitView extends View
private double mSpeedLimitMps;
@Nullable
private String mSpeedLimitStr;
@Nullable
private String mCurrentSpeedStr;
@Nullable
private String mCurrentSpeedUnitsStr;
private double mCurrentSpeed;
private double mCurrentSpeedMps;
/**
* Well store the bottom system navigation bar (or gesture bar) inset here.
* Instead of subtracting it from the height, well apply a vertical translation
* so the entire widget moves up.
*/
private int mBottomSystemWindowInset = 0;
private int mLeftSystemWindowInset = 0;
@SuppressLint("SwitchIntDef")
public SpeedLimitView(Context context, @Nullable AttributeSet attrs)
{
super(context, attrs);
try (TypedArray data = context.getTheme()
.obtainStyledAttributes(attrs, R.styleable.SpeedLimitView, 0, 0))
.obtainStyledAttributes(attrs, R.styleable.SpeedLimitView, 0, 0))
{
mBackgroundColorWidget = data.getColor(R.styleable.SpeedLimitView_WidgetBackgroundColor, switch (Framework.nativeGetMapStyle()) {
case Framework.MAP_STYLE_DARK, Framework.MAP_STYLE_VEHICLE_DARK, Framework.MAP_STYLE_OUTDOORS_DARK ->
Color.DKGRAY;
default -> Color.WHITE;
});
mBackgroundColor = data.getColor(R.styleable.SpeedLimitView_BackgroundColor, DefaultValues.BACKGROUND_COLOR);
mBorderColor = data.getColor(R.styleable.SpeedLimitView_borderColor, ContextCompat.getColor(context, R.color.base_red));
mUnlimitedBorderColor = data.getColor(R.styleable.SpeedLimitView_unlimitedBorderColor, DefaultValues.UNLIMITED_BORDER_COLOR);
mAlertColor = data.getColor(R.styleable.SpeedLimitView_alertColor, ContextCompat.getColor(context, R.color.base_red));
mTextColor = data.getColor(R.styleable.SpeedLimitView_textColor, DefaultValues.TEXT_COLOR);
mTextAlertColor = data.getColor(R.styleable.SpeedLimitView_textAlertColor, DefaultValues.TEXT_ALERT_COLOR);
@ -83,7 +135,8 @@ public class SpeedLimitView extends View
{
mSpeedLimitMps = data.getInt(R.styleable.SpeedLimitView_editModeSpeedLimit, -1);
mSpeedLimitStr = mSpeedLimitMps > 0 ? String.valueOf(((int) mSpeedLimitMps)) : null;
mCurrentSpeed = data.getInt(R.styleable.SpeedLimitView_editModeCurrentSpeed, -1);
mCurrentSpeedMps = data.getInt(R.styleable.SpeedLimitView_editModeCurrentSpeed, -1);
mCurrentSpeedStr = mCurrentSpeedMps > 0 ? String.valueOf(((int) mCurrentSpeedMps)) : null;
}
}
@ -94,11 +147,49 @@ public class SpeedLimitView extends View
mSignBorderPaint.setColor(mBorderColor);
mSignBorderPaint.setStrokeWidth(mBorderWidth);
mSignBorderPaint.setStyle(Paint.Style.STROKE);
mSignUnlimitedBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mSignUnlimitedBorderPaint.setColor(mUnlimitedBorderColor);
mSignUnlimitedBorderPaint.setStrokeWidth(mBorderWidth);
mSignUnlimitedBorderPaint.setStyle(Paint.Style.STROKE);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setColor(mTextColor);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
mCurrentSpeedBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCurrentSpeedBackgroundPaint.setColor(mBackgroundColor);
mCurrentSpeedTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCurrentSpeedTextPaint.setColor(mTextColor);
mCurrentSpeedTextPaint.setTextAlign(Paint.Align.CENTER);
mCurrentSpeedTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
mCurrentSpeedUnitsTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCurrentSpeedUnitsTextPaint.setColor(mTextColor);
mCurrentSpeedUnitsTextPaint.setTextAlign(Paint.Align.CENTER);
mCurrentSpeedUnitsTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
mCurrentSpeedBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCurrentSpeedBorderPaint.setColor(Color.BLACK);
mCurrentSpeedBorderPaint.setStyle(Paint.Style.STROKE);
mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBackgroundPaint.setColor(mBackgroundColor);
mWidgetBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mWidgetBackgroundPaint.setColor(mBackgroundColorWidget);
mSpeedLimitMps = -1.0;
}
/**
* Capture bottom navigation bar insets (gesture area).
* Well use this to translate the canvas up.
*/
@Override
@NonNull
public WindowInsets onApplyWindowInsets(@NonNull WindowInsets insets)
{
mBottomSystemWindowInset = insets.getSystemWindowInsetBottom();
mLeftSystemWindowInset = insets.getSystemWindowInsetLeft();
// Request a re-draw so we can apply the translation in onDraw().
invalidate();
return super.onApplyWindowInsets(insets);
}
public void setSpeedLimitMps(final double speedLimitMps)
@ -106,65 +197,192 @@ public class SpeedLimitView extends View
if (mSpeedLimitMps == speedLimitMps)
return;
boolean hadSpeedLimit = mSpeedLimitMps >= 0;
mSpeedLimitMps = speedLimitMps;
if (mSpeedLimitMps <= 0)
{
mSpeedLimitStr = null;
setVisibility(GONE);
return;
boolean hasSpeedLimit = mSpeedLimitMps >= 0;
if (mSpeedLimitMps >= 0) {
final Pair<String, String> speedLimitAndUnits = StringUtils.nativeFormatSpeedAndUnits(mSpeedLimitMps);
mSpeedLimitStr = speedLimitAndUnits.first;
}
final Pair<String, String> speedLimitAndUnits = StringUtils.nativeFormatSpeedAndUnits(mSpeedLimitMps);
setVisibility(VISIBLE);
mSpeedLimitStr = speedLimitAndUnits.first;
if (hadSpeedLimit != hasSpeedLimit) {
animateSpeedLimitAlpha(hasSpeedLimit);
} else {
invalidate();
}
configureTextSize();
invalidate();
}
public void setCurrentSpeed(final double currentSpeed)
{
mCurrentSpeed = currentSpeed;
private void animateSpeedLimitAlpha(final boolean fadeIn) {
float startAlpha = fadeIn ? 0f : 1f;
float endAlpha = fadeIn ? 1f : 0f;
ValueAnimator animator = ValueAnimator.ofFloat(startAlpha, endAlpha);
animator.setDuration(500);
animator.addUpdateListener(animation -> {
mSpeedLimitAlpha = (float) animation.getAnimatedValue();
invalidate();
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (!fadeIn) {
mSpeedLimitStr = null;
}
}
});
animator.start();
}
public void setCurrentSpeed(final double currentSpeedMps) {
mCurrentSpeedMps = currentSpeedMps;
final Pair<String, String> speedAndUnits = StringUtils.nativeFormatSpeedAndUnits(mCurrentSpeedMps);
if (mCurrentSpeedMps < 0)
mCurrentSpeedStr = "0";
else
mCurrentSpeedStr = speedAndUnits.first;
mCurrentSpeedUnitsStr = speedAndUnits.second;
configureTextSize();
invalidate();
}
@Override
protected void onDraw(@NonNull Canvas canvas)
{
super.onDraw(canvas);
private void draw_widgets(@NonNull Canvas canvas, float cx, float cx_or_cy, float cy, boolean isLandscape) {
final boolean alert = mSpeedLimitMps > 0 && mSpeedLimitStr != null && Integer.parseInt(mCurrentSpeedStr) > Integer.parseInt(mSpeedLimitStr); //TODO do better?
float second_cx = (isLandscape) ? cx_or_cy : cx;
float second_cy = (isLandscape) ? cy : cx_or_cy;
// Draw combined background and speed limit sign with layer alpha
if (mSpeedLimitAlpha > 0f) {
int saveCount = canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (mSpeedLimitAlpha * 255));
drawCombinedBackground(canvas, cx, cx_or_cy, cy);
// Draw speed limit sign and text
if (mSpeedLimitStr != null)
drawSpeedLimitSign(canvas, cx, second_cy);
final boolean alert = mCurrentSpeed > mSpeedLimitMps && mSpeedLimitMps > 0;
final float cx = mWidth / 2;
final float cy = mHeight / 2;
drawSign(canvas, cx, cy, alert);
drawText(canvas, cx, cy, alert);
canvas.restoreToCount(saveCount);
}
// Draw current speed sign and text (always visible)
if (mCurrentSpeedStr != null)
drawCurrentSpeedSign(canvas, second_cx, cy, alert);
}
private void drawSign(@NonNull Canvas canvas, float cx, float cy, boolean alert)
{
if (alert)
mSignBackgroundPaint.setColor(mAlertColor);
else
mSignBackgroundPaint.setColor(mBackgroundColor);
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.translate(mLeftSystemWindowInset, -mBottomSystemWindowInset);
final boolean isLandscape = mWidth > mHeight;
canvas.drawCircle(cx, cy, mBackgroundRadius, mSignBackgroundPaint);
if (!alert)
{
mSignBorderPaint.setStrokeWidth(mBorderWidth);
canvas.drawCircle(cx, cy, mBorderRadius, mSignBorderPaint);
if (isLandscape) {
final float cy = mHeight / 2;
float gap = mWidth * 0.02f;
final float totalWidth = 4 * mBackgroundRadius + gap;
final float startX = (mWidth - totalWidth) / 2;
final float currentSpeedCx = startX + mBackgroundRadius;
final float speedLimitCx = currentSpeedCx + 2 * mBackgroundRadius + gap;
draw_widgets(canvas, speedLimitCx, currentSpeedCx, cy, true);
} else {
final float cx = mWidth / 2;
float gap = mHeight * 0.02f;
final float totalHeight = 4 * mBackgroundRadius + gap;
final float startY = (mHeight - totalHeight) / 2;
final float speedLimitCy = startY + mBackgroundRadius;
final float currentSpeedCy = speedLimitCy + 2 * mBackgroundRadius + gap;
draw_widgets(canvas, cx, speedLimitCy, currentSpeedCy, false);
}
}
private void drawText(@NonNull Canvas canvas, float cx, float cy, boolean alert)
private void drawCombinedBackground(Canvas canvas, float cx, float cx_or_cy, float cy) {
final boolean isLandscape = mWidth > mHeight;
float left;
float right;
float top;
float bottom;
if (isLandscape) {
top = cy - mBackgroundRadius;
bottom = cy + mBackgroundRadius;
left = cx_or_cy - mBackgroundRadius;
right = cx + mBackgroundRadius;
} else {
left = cx - mBackgroundRadius;
right = cx + mBackgroundRadius;
top = cx_or_cy - mBackgroundRadius;
bottom = cy + mBackgroundRadius;
}
RectF rect = new RectF(left, top, right, bottom);
float cornerRadius = mBackgroundRadius;
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, mWidgetBackgroundPaint);
}
private void drawUnlimitedSign(@NonNull Canvas canvas, float cx, float cy) {
// Paint for stripes
Paint stripesPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
stripesPaint.setColor(Color.BLACK);
stripesPaint.setStyle(Paint.Style.STROKE);
// Thickness of the stripes can be relative to the border width or radius
float stripeThickness = mBorderWidth * 0.4f;
stripesPaint.setStrokeWidth(stripeThickness);
float r = mBackgroundRadius;
// Direction vector for the diagonal lines (top-right to bottom-left)
float dx = -1.0f / (float)Math.sqrt(2);
float dy = 1.0f / (float)Math.sqrt(2);
// Perpendicular vector to D (rotate by +90°)
float px = -dy;
float py = dx;
// Increase lineSpacing to spread stripes further apart
float lineSpacing = r * 0.12f; // slightly larger than 0.1f
// Use a scale factor to shorten the lines
float lineScale = 0.8f; // reduce from full radius to 85% radius
int[] offsets = {-2, -1, 0, 1, 2};
// Draw 5 parallel stripes
for (int i : offsets) {
float ox = i * lineSpacing * px;
float oy = i * lineSpacing * py;
// Start and end points of the line, shortened by lineScale
float sx = cx + dx * r * lineScale + ox;
float sy = cy + dy * r * lineScale + oy;
float ex = cx - dx * r * lineScale + ox;
float ey = cy - dy * r * lineScale + oy;
canvas.drawLine(sx, sy, ex, ey, stripesPaint);
}
}
private void drawSpeedLimitSign(@NonNull Canvas canvas, float cx, float cy) {
// Draw the sign background and border as before
mSignBackgroundPaint.setColor(mBackgroundColor);
canvas.drawCircle(cx, cy, mBackgroundRadius, mSignBackgroundPaint);
if (mSpeedLimitMps == 0) {
// Unlimited speed scenario: Draw the special no-limit pattern
mSignUnlimitedBorderPaint.setStrokeWidth(mBorderWidth);
canvas.drawCircle(cx, cy, mBorderRadius * DefaultValues.BORDER_RADIUS_RATIO, mSignUnlimitedBorderPaint);
drawUnlimitedSign(canvas, cx, cy);
}
else if (mSpeedLimitStr != null && mSpeedLimitMps > 0) {
// Normal speed limit scenario: Draw the text
mSignBorderPaint.setStrokeWidth(mBorderWidth);
canvas.drawCircle(cx, cy, mBorderRadius * DefaultValues.BORDER_RADIUS_RATIO, mSignBorderPaint);
drawSpeedLimitText(canvas, cx, cy);
}
}
private void drawSpeedLimitText(@NonNull Canvas canvas, float cx, float cy)
{
if (mSpeedLimitStr == null)
return;
if (alert)
mTextPaint.setColor(mTextAlertColor);
else
mTextPaint.setColor(mTextColor);
mTextPaint.setColor(mTextColor);
final Rect textBounds = new Rect();
mTextPaint.getTextBounds(mSpeedLimitStr, 0, mSpeedLimitStr.length(), textBounds);
@ -172,32 +390,106 @@ public class SpeedLimitView extends View
canvas.drawText(mSpeedLimitStr, cx, textY, mTextPaint);
}
Review
  private void drawWidgets(@NonNull Canvas canvas, float cx, float cx_or_cy, float cy, boolean isLandscape) {
```suggestion private void drawWidgets(@NonNull Canvas canvas, float cx, float cx_or_cy, float cy, boolean isLandscape) { ```
private void drawCurrentSpeedSign(@NonNull Canvas canvas, float cx, float cy, boolean alert) {
// Change background color based on alert state
mCurrentSpeedBackgroundPaint.setColor(mBackgroundColor);
// Draw current speed circle (background)
canvas.drawCircle(cx, cy, mBackgroundRadius, mCurrentSpeedBackgroundPaint);
// Draw border around current speed circle
if (alert) {
mCurrentSpeedBackgroundPaint.setColor(mAlertColor);
canvas.drawCircle(cx, cy, mBackgroundRadius * DefaultValues.BORDER_RADIUS_RATIO * 0.95f, mCurrentSpeedBackgroundPaint);
}
mCurrentSpeedBorderPaint.setStrokeWidth(mBorderWidth / 2);
canvas.drawCircle(cx, cy, mBorderRadius, mCurrentSpeedBorderPaint);
drawCurrentSpeedText(canvas, cx, cy, alert);
}
private void drawCurrentSpeedText(@NonNull Canvas canvas, float cx, float cy, boolean alert) {
if (mCurrentSpeedStr == null || mCurrentSpeedUnitsStr == null)
return;
// Change text color based on alert state
if (alert) {
mCurrentSpeedTextPaint.setColor(mTextAlertColor);
mCurrentSpeedUnitsTextPaint.setColor(mTextAlertColor);
} else {
mCurrentSpeedTextPaint.setColor(mTextColor);
mCurrentSpeedUnitsTextPaint.setColor(mTextColor);
}
final Rect speedTextBounds = new Rect();
mCurrentSpeedTextPaint.getTextBounds(mCurrentSpeedStr, 0, mCurrentSpeedStr.length(), speedTextBounds);
final Rect unitsTextBounds = new Rect();
mCurrentSpeedUnitsTextPaint.getTextBounds(mCurrentSpeedUnitsStr, 0, mCurrentSpeedUnitsStr.length(), unitsTextBounds);
// Position speed text
float speedTextY = cy - (float) speedTextBounds.height() / 2 + speedTextBounds.height();
// Position units text
float unitsTextY = speedTextY + unitsTextBounds.height() + 10f;
// Draw speed text
canvas.drawText(mCurrentSpeedStr, cx, speedTextY, mCurrentSpeedTextPaint);
// Draw units text
canvas.drawText(mCurrentSpeedUnitsStr, cx, unitsTextY, mCurrentSpeedUnitsTextPaint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
final float paddingX = (float) (getPaddingLeft() + getPaddingRight());
final float paddingY = (float) (getPaddingTop() + getPaddingBottom());
final float paddingX = getPaddingLeft() + getPaddingRight();
final float paddingY = getPaddingTop() + getPaddingBottom();
mWidth = w - paddingX;
mHeight = h - paddingY;
mWidth = (float) w - paddingX;
mHeight = (float) h - paddingY;
mBackgroundRadius = Math.min(mWidth, mHeight) / 2;
mBorderWidth = mBackgroundRadius * 2 * DefaultValues.BORDER_WIDTH_RATIO;
boolean isLandscape = mWidth > mHeight;
float gap = (isLandscape) ? mWidth * 0.1f : mHeight * 0.1f;
// Compute maximum possible radius
mBackgroundRadius = (isLandscape) ? (mWidth - gap) / 4 : (mHeight - gap) / 4;
// Ensure the radius does not exceed half of the width
mBackgroundRadius = (isLandscape) ? Math.min(mBackgroundRadius, mHeight / 2) : Math.min(mBackgroundRadius, mWidth / 2);
mBorderWidth = mBackgroundRadius * DefaultValues.BORDER_WIDTH_RATIO;
mBorderRadius = mBackgroundRadius - mBorderWidth / 2;
configureTextSize();
}
// Apply binary search to determine the optimal text size that fits within the circular boundary.
private void configureTextSize()
{
if (mSpeedLimitStr == null)
return;
private void configureTextSize() {
// Use reference strings to keep text size consistent
configureTextSizeForString(mTextPaint); // For speed limit
configureCurrentSpeedTextSize(); // For current speed and units
}
private void configureCurrentSpeedTextSize() {
final float textRadius = mBorderRadius * 0.75f - mCurrentSpeedBorderPaint.getStrokeWidth();
final float availableHeight = 2 * textRadius;
float speedTextHeightRatio = 0.75f;
float unitsTextHeightRatio = 0.2f;
float speedTextHeight = availableHeight * speedTextHeightRatio;
float unitsTextHeight = availableHeight * unitsTextHeightRatio;
float speedTextSize = findMaxTextSizeForHeight("299", mCurrentSpeedTextPaint, speedTextHeight);
float unitsTextSize = findMaxTextSizeForHeight("km/h", mCurrentSpeedUnitsTextPaint, unitsTextHeight);
mCurrentSpeedTextPaint.setTextSize(speedTextSize);
mCurrentSpeedUnitsTextPaint.setTextSize(unitsTextSize);
}
private float findMaxTextSizeForHeight(String text, Paint paint, float targetHeight) {
float lowerBound = 0;
float upperBound = targetHeight;
float textSize = targetHeight;
final Rect textBounds = new Rect();
while (upperBound - lowerBound > 1f) {
textSize = (lowerBound + upperBound) / 2;
paint.setTextSize(textSize);
paint.getTextBounds(text, 0, text.length(), textBounds);
if (textBounds.height() <= targetHeight)
lowerBound = textSize;
else
upperBound = textSize;
}
return lowerBound;
}
final String text = mSpeedLimitStr;
private void configureTextSizeForString(Paint textPaint) {
final float textRadius = mBorderRadius - mBorderWidth;
final float textMaxSize = 2 * textRadius;
final float textMaxSizeSquared = (float) Math.pow(textMaxSize, 2);
final float textMaxSizeSquared = textMaxSize * textMaxSize;
float lowerBound = 0;
float upperBound = textMaxSize;
@ -207,15 +499,15 @@ public class SpeedLimitView extends View
while (lowerBound <= upperBound)
{
textSize = (lowerBound + upperBound) / 2;
mTextPaint.setTextSize(textSize);
mTextPaint.getTextBounds(text, 0, text.length(), textBounds);
textPaint.setTextSize(textSize);
textPaint.getTextBounds("180", 0, 3, textBounds);
if (Math.pow(textBounds.width(), 2) + Math.pow(textBounds.height(), 2) <= textMaxSizeSquared)
if (textBounds.width() * textBounds.width() + textBounds.height() * textBounds.height() <= textMaxSizeSquared)
lowerBound = textSize + 1;
else
upperBound = textSize - 1;
}
mTextPaint.setTextSize(Math.max(1, textSize));
textPaint.setTextSize(Math.max(1, textSize));
}
}
}

View file

@ -33,9 +33,6 @@ public class NavMenu
private final View mHeaderFrame;
private final ImageView mTts;
private final View mSpeedViewContainer;
private final TextView mSpeedValue;
private final TextView mSpeedUnits;
private final TextView mTimeHourValue;
private final TextView mTimeHourUnits;
private final TextView mTimeMinuteValue;
@ -94,10 +91,6 @@ public class NavMenu
}
});
// Bottom frame
mSpeedViewContainer = bottomFrame.findViewById(R.id.speed_view_container);
mSpeedValue = bottomFrame.findViewById(R.id.speed_value);
mSpeedUnits = bottomFrame.findViewById(R.id.speed_dimen);
mTimeHourValue = bottomFrame.findViewById(R.id.time_hour_value);
mTimeHourUnits = bottomFrame.findViewById(R.id.time_hour_dimen);
mTimeMinuteValue = bottomFrame.findViewById(R.id.time_minute_value);
@ -213,20 +206,6 @@ public class NavMenu
return;
Pair<String, String> speedAndUnits = StringUtils.nativeFormatSpeedAndUnits(last.getSpeed());
mSpeedValue.setText(speedAndUnits.first);
if (info.speedLimitMps > 0.0 && last.getSpeed() > info.speedLimitMps)
{
if (info.isSpeedCamLimitExceeded())
mSpeedValue.setTextColor(ContextCompat.getColor(mActivity, R.color.white_primary));
else
mSpeedValue.setTextColor(ContextCompat.getColor(mActivity, R.color.base_red));
}
else
mSpeedValue.setTextColor(ThemeUtils.getColor(mActivity, android.R.attr.textColorPrimary));
mSpeedUnits.setText(speedAndUnits.second);
mSpeedViewContainer.setActivated(info.isSpeedCamLimitExceeded());
}
public void update(@NonNull RoutingInfo info)

View file

@ -25,29 +25,6 @@
android:padding="@dimen/nav_frame_padding"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
layout="@layout/map_buttons_search_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="@+id/btn_search"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/btn_search" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/margin_half"
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/map_buttons_inner_right"
@ -60,11 +37,37 @@
android:padding="@dimen/nav_frame_padding"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/zoom_buttons_margin"
app:layout_constraintBottom_toTopOf="@+id/zoom_buttons_container"
app:layout_constraintEnd_toEndOf="parent" />
<include
layout="@layout/map_buttons_search_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="@+id/btn_search"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/btn_search" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/margin_half"
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
app:layout_constraintEnd_toEndOf="parent" />
<include
layout="@layout/map_buttons_zoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginTop="@dimen/zoom_buttons_margin"
android:layout_marginBottom="@dimen/zoom_buttons_margin"
app:layout_constraintBottom_toTopOf="@+id/my_position"
app:layout_constraintEnd_toEndOf="parent" />

View file

@ -18,21 +18,6 @@
android:clipToPadding="false"
android:padding="@dimen/nav_frame_padding"
app:layout_constraintStart_toStartOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/margin_half"
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/map_buttons_inner_right"
@ -44,6 +29,24 @@
android:clipToPadding="false"
android:padding="@dimen/nav_frame_padding"
app:layout_constraintEnd_toEndOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/zoom_buttons_margin"
app:layout_constraintBottom_toTopOf="@+id/zoom_buttons_container"
app:layout_constraintEnd_toEndOf="parent" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/margin_half"
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
app:layout_constraintEnd_toEndOf="parent" />
<include
layout="@layout/map_buttons_zoom"
android:layout_width="wrap_content"

View file

@ -13,8 +13,8 @@
android:id="@+id/search_frame"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_alignParentStart="true"
android:layout_marginStart="-56dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="-56dp"
android:background="?searchLayoutBackground"
android:visibility="invisible"
tools:visibility="visible">
@ -23,7 +23,7 @@
style="@style/MwmWidget.MapButton.Search"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/margin_half_plus"
android:layout_marginEnd="@dimen/margin_half_plus"
android:layout_marginTop="@dimen/margin_half"
android:contentDescription="@string/category_fuel"
app:srcCompat="@drawable/ic_routing_fuel_on" />
@ -31,13 +31,13 @@
android:id="@+id/search_parking"
style="@style/MwmWidget.MapButton.Search"
android:layout_marginTop="@dimen/margin_base_plus"
android:layout_toEndOf="@id/search_fuel"
android:layout_toStartOf="@id/search_fuel"
android:contentDescription="@string/category_parking"
app:srcCompat="@drawable/ic_routing_parking_on" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_eat"
style="@style/MwmWidget.MapButton.Search"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/margin_half"
android:contentDescription="@string/category_eat"
@ -47,7 +47,7 @@
style="@style/MwmWidget.MapButton.Search"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/margin_base_plus"
android:layout_toEndOf="@id/search_fuel"
android:layout_toStartOf="@id/search_fuel"
android:contentDescription="@string/category_shopping"
app:srcCompat="@drawable/ic_routing_food_on" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
@ -55,7 +55,7 @@
style="@style/MwmWidget.MapButton.Search"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/margin_half_plus"
android:layout_marginEnd="@dimen/margin_half_plus"
android:layout_marginBottom="@dimen/margin_half"
android:contentDescription="@string/category_atm"
app:srcCompat="@drawable/ic_routing_atm_on" />

View file

@ -7,7 +7,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="#20FF0000"
tools:showIn="@layout/layout_nav">
tools:showIn="@layout/layout_nav"
android:clipToPadding="false"
android:clipChildren="false">
<FrameLayout
android:id="@+id/street_frame"
android:layout_width="match_parent"
@ -113,7 +115,7 @@
android:layout_margin="@dimen/margin_half"
android:padding="@dimen/margin_half"
android:visibility="gone"
app:layout_constraintStart_toEndOf="@+id/nav_speed_limit"
app:layout_constraintStart_toEndOf="@+id/nav_next_turn_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/street_frame"
app:activeLaneTintColor="?navLaneArrowActiveColor"
@ -124,12 +126,12 @@
tools:visibility="visible" />
<app.organicmaps.widget.SpeedLimitView
android:id="@+id/nav_speed_limit"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="@dimen/margin_half"
app:editModeCurrentSpeed="90"
app:editModeSpeedLimit="120"
app:layout_constraintStart_toEndOf="@id/nav_next_turn_container"
app:layout_constraintTop_toBottomOf="@id/street_frame" />
android:id="@+id/nav_speed_limit"
android:layout_width="110dp"
android:layout_height="60dp"
app:editModeCurrentSpeed="90"
app:editModeSpeedLimit="120"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="88dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -13,42 +13,6 @@
android:layout_height="match_parent"
android:layout_weight="0.5"/>
<!-- Speed -->
<LinearLayout
android:id="@+id/speed_view_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/speed_cams_bg"
android:gravity="center"
android:minWidth="@dimen/nav_numbers_side_min_width">
<TextView
android:id="@+id/speed_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:lines="1"
android:textAppearance="@style/MwmTextAppearance.NavMenu.Number"
tools:text="999" />
<!-- Speed -->
<TextView
android:id="@+id/speed_dimen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:includeFontPadding="false"
android:lines="1"
android:textAppearance="@style/MwmTextAppearance.NavMenu.Number.Dimension"
tools:background="#20FF0000"
tools:text="km/h" />
</LinearLayout>
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1.25"/>
<!-- Time -->
<LinearLayout
android:layout_width="wrap_content"

View file

@ -7,7 +7,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="#20FF0000"
tools:showIn="@layout/layout_nav">
tools:showIn="@layout/layout_nav"
android:clipToPadding="false"
android:clipChildren="false">
<FrameLayout
android:id="@+id/street_frame"
android:layout_width="match_parent"
@ -113,7 +115,7 @@
android:layout_marginEnd="@dimen/margin_half"
android:layout_marginTop="@dimen/margin_half"
android:padding="@dimen/margin_half"
android:visibility="gone"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/nav_next_turn_container"
app:layout_constraintTop_toBottomOf="@id/street_frame"
@ -127,11 +129,10 @@
<app.organicmaps.widget.SpeedLimitView
android:id="@+id/nav_speed_limit"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="@dimen/margin_half"
android:layout_height="110dp"
app:editModeCurrentSpeed="90"
app:editModeSpeedLimit="120"
app:layout_constraintEnd_toEndOf="@id/nav_next_turn_container"
app:layout_constraintStart_toStartOf="@id/nav_next_turn_container"
app:layout_constraintTop_toBottomOf="@id/nav_next_turn_container" />
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="140dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -25,29 +25,6 @@
android:padding="@dimen/nav_frame_padding"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginStart="@dimen/margin_half"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_search" />
<include
layout="@layout/map_buttons_search_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="@+id/btn_search"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/btn_search" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/map_buttons_inner_right"
@ -60,6 +37,30 @@
android:padding="@dimen/nav_frame_padding"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginEnd="@dimen/margin_half"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btn_search" />
<include
layout="@layout/map_buttons_search_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="@+id/btn_search"
app:layout_constraintEnd_toEndOf="@+id/btn_search"
app:layout_constraintTop_toTopOf="@+id/btn_search" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_half"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/my_position" />
<include
layout="@layout/map_buttons_zoom"
android:layout_width="wrap_content"

View file

@ -19,21 +19,7 @@
android:padding="@dimen/nav_frame_padding"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/margin_half"
app:layout_constraintBottom_toTopOf="@+id/btn_bookmarks"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/map_buttons_inner_right"
@ -46,14 +32,30 @@
android:padding="@dimen/nav_frame_padding"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<include
layout="@layout/map_buttons_bookmarks"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginEnd="@dimen/margin_half"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/btn_search" />
<include
layout="@layout/map_buttons_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginEnd="@dimen/margin_half"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/my_position" />
<include
layout="@layout/map_buttons_zoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginStart="@dimen/margin_half"
app:layout_constraintStart_toEndOf="@+id/my_position"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/margin_half"
app:layout_constraintBottom_toTopOf="@+id/my_position"
app:layout_constraintEnd_toEndOf="parent" />
<include
layout="@layout/map_buttons_myposition"
@ -61,6 +63,6 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -13,46 +13,46 @@
android:id="@+id/search_frame"
android:layout_width="286dp"
android:layout_height="48dp"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"
android:background="?searchLayoutBackground"
android:orientation="horizontal"
android:padding="@dimen/margin_quarter"
android:visibility="invisible"
tools:visibility="visible">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_fuel"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginStart="50dp"
android:contentDescription="@string/category_fuel"
app:srcCompat="@drawable/ic_routing_fuel_on" />
android:id="@+id/search_parking"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/margin_half"
android:contentDescription="@string/category_parking"
app:srcCompat="@drawable/ic_routing_parking_on" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_parking"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/margin_half"
android:contentDescription="@string/category_parking"
app:srcCompat="@drawable/ic_routing_parking_on" />
android:id="@+id/search_eat"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/margin_half"
android:contentDescription="@string/category_eat"
app:srcCompat="@drawable/ic_routing_eat_on" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_eat"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/margin_half"
android:contentDescription="@string/category_eat"
app:srcCompat="@drawable/ic_routing_eat_on" />
android:id="@+id/search_food"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/margin_half"
android:contentDescription="@string/category_shopping"
app:srcCompat="@drawable/ic_routing_food_on" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_food"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/margin_half"
android:contentDescription="@string/category_shopping"
app:srcCompat="@drawable/ic_routing_food_on" />
android:id="@+id/search_atm"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/margin_half"
android:contentDescription="@string/category_atm"
app:srcCompat="@drawable/ic_routing_atm_on" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/search_atm"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/margin_half"
android:contentDescription="@string/category_atm"
app:srcCompat="@drawable/ic_routing_atm_on" />
android:id="@+id/search_fuel"
style="@style/MwmWidget.MapButton.Search"
android:layout_gravity="center_vertical"
android:layout_marginEnd="50dp"
android:contentDescription="@string/category_fuel"
app:srcCompat="@drawable/ic_routing_fuel_on" />
</LinearLayout>
</RelativeLayout>

View file

@ -2,7 +2,9 @@
<resources>
<declare-styleable name="SpeedLimitView">
<attr name="BackgroundColor" format="color" />
<attr name="WidgetBackgroundColor" format="color" />
Review

What's the difference between BackgroundColor and WidgetBackgroundColor?
It should be possible to understand it from the name.
Btw, why do these values have names starting from upper case and values below have names starting from lower case?
They all should start from lower case letters like any other android's XML value.

What's the difference between `BackgroundColor` and `WidgetBackgroundColor`? It should be possible to understand it from the name. Btw, why do these values have names starting from upper case and values below have names starting from lower case? They all should start from lower case letters like any other android's XML value.
<attr name="borderColor" format="color" />
<attr name="unlimitedBorderColor" format="color" />
<attr name="alertColor" format="color" />
<attr name="textColor" format="color" />
<attr name="textAlertColor" format="color" />