Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #19164 #20893

Merged
merged 4 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions OsmAnd/res/layout/map_hud_quick_actions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<FrameLayout
android:id="@+id/quick_actions_container"
<net.osmand.plus.views.controls.MapButtonsLayout
android:id="@+id/map_buttons_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
Expand Down
130 changes: 130 additions & 0 deletions OsmAnd/src/net/osmand/plus/views/controls/MapButtonsLayout.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package net.osmand.plus.views.controls;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import net.osmand.data.QuadRect;
import net.osmand.data.QuadTree;
import net.osmand.plus.utils.AndroidUtils;
import net.osmand.plus.views.controls.maphudbuttons.MapButton;
import net.osmand.plus.views.layers.base.OsmandMapLayer;

public class MapButtonsLayout extends FrameLayout {

private static final double[][] DIRECTIONS = getAvailableDirections();

private final int gridSize;

public MapButtonsLayout(@NonNull Context context) {
this(context, null);
}

public MapButtonsLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}

public MapButtonsLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}

public MapButtonsLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);

this.gridSize = AndroidUtils.dpToPx(context, 8);
}

public void updateButtons() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof MapButton button) {
updateButton(button);
}
}
}

public void updateButton(@NonNull MapButton button) {
QuadRect currentBounds = getRect(button);
QuadTree<QuadRect> intersections = initBoundIntersections(button);
LayoutParams params = (LayoutParams) button.getLayoutParams();

if (OsmandMapLayer.intersects(intersections, currentBounds, false)) {
params = updateButtonPosition(button, intersections);
}
params.rightMargin = snapToGrid(params.rightMargin);
params.bottomMargin = snapToGrid(params.bottomMargin);

button.setLayoutParams(params);
button.saveMargins();
}

@NonNull
private QuadTree<QuadRect> initBoundIntersections(@NonNull MapButton button) {
QuadTree<QuadRect> intersections = OsmandMapLayer.initBoundIntersections(getWidth(), getHeight());
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child != button && child instanceof MapButton) {
QuadRect rect = getRect((MapButton) child);
intersections.insert(rect, new QuadRect(rect));
}
}
return intersections;
}

@NonNull
private LayoutParams updateButtonPosition(@NonNull MapButton button, @NonNull QuadTree<QuadRect> intersections) {
LayoutParams originalParams = (LayoutParams) button.getLayoutParams();
LayoutParams params = new LayoutParams(originalParams);

int maxStepsX = getWidth() / gridSize;
int maxStepsY = getHeight() / gridSize;
int maxSteps = Math.max(maxStepsX, maxStepsY);
int minRightMargin = getWidth() - button.getWidth();
int minBottomMargin = getHeight() - button.getHeight();

for (int step = 1; step <= maxSteps; step++) {
for (double[] direction : DIRECTIONS) {
int newRightMargin = (int) (originalParams.rightMargin + direction[0] * step * gridSize);
int newBottomMargin = (int) (originalParams.bottomMargin + direction[1] * step * gridSize);

params.rightMargin = Math.max(0, Math.min(newRightMargin, minRightMargin));
params.bottomMargin = Math.max(0, Math.min(newBottomMargin, minBottomMargin));

QuadRect rect = getRect(button, params);
if (!OsmandMapLayer.intersects(intersections, rect, false)) {
return params;
}
}
}
return originalParams;
}

@NonNull
private static QuadRect getRect(@NonNull MapButton button) {
return getRect(button, (LayoutParams) button.getLayoutParams());
}

@NonNull
private static QuadRect getRect(@NonNull MapButton button, @NonNull LayoutParams params) {
int size = button.getSize();
return new QuadRect(params.rightMargin + size, params.bottomMargin + size, params.rightMargin, params.bottomMargin);
}

private int snapToGrid(int margin) {
return Math.round((float) margin / gridSize) * gridSize;
}

private static double[][] getAvailableDirections() {
double[][] directions = new double[16][2];
for (int i = 0; i < 16; i++) {
double angle = Math.toRadians(i * 22.5);
directions[i][0] = Math.cos(angle);
directions[i][1] = Math.sin(angle);
}
return directions;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import static net.osmand.plus.views.OsmandMapTileView.DEFAULT_ELEVATION_ANGLE;
import static net.osmand.plus.views.OsmandMapTileView.ElevationListener;
import static net.osmand.plus.views.layers.ContextMenuLayer.VIBRATE_SHORT;

import android.content.Context;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.view.View;

Expand Down Expand Up @@ -41,7 +39,6 @@ public Map3DButton(@NonNull Context context, @Nullable AttributeSet attrs, int d
animateDraggingMapThread = getMapView().getAnimatedDraggingThread();

setOnClickListener(getOnCLickListener());
setOnLongClickListener(getLongClickListener());
}

@Nullable
Expand Down Expand Up @@ -93,19 +90,6 @@ public void onStopChangingElevation(float angle) {
};
}

@NonNull
private View.OnLongClickListener getLongClickListener() {
return view -> {
Vibrator vibrator = (Vibrator) mapActivity.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_SHORT);
setScaleX(1.5f);
setScaleY(1.5f);
setAlpha(0.95f);
setOnTouchListener(new MapButtonTouchListener(mapActivity, buttonState.getFabMarginPref()));
return true;
};
}

@Override
protected boolean shouldShow() {
boolean shouldShowFabButton = mapActivity.getWidgetsVisibilityHelper().shouldShowMap3DButton();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static net.osmand.plus.quickaction.ButtonAppearanceParams.ROUND_RADIUS_DP;
import static net.osmand.plus.quickaction.ButtonAppearanceParams.TRANSPARENT_ALPHA;
import static net.osmand.plus.settings.backend.preferences.FabMarginPreference.setFabButtonMargin;
import static net.osmand.plus.views.layers.ContextMenuLayer.VIBRATE_SHORT;

import android.content.Context;
import android.graphics.Canvas;
Expand All @@ -19,11 +20,13 @@
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.ImageView;

Expand All @@ -44,6 +47,7 @@
import net.osmand.plus.utils.ColorUtilities;
import net.osmand.plus.utils.UiUtilities;
import net.osmand.plus.views.OsmandMapTileView;
import net.osmand.plus.views.controls.MapButtonsLayout;
import net.osmand.plus.views.layers.base.OsmandMapLayer;
import net.osmand.plus.views.mapwidgets.WidgetsVisibilityHelper;
import net.osmand.plus.views.mapwidgets.configure.buttons.MapButtonState;
Expand Down Expand Up @@ -165,6 +169,20 @@ public void setAlwaysVisible(boolean alwaysVisible) {

public void setUseCustomPosition(boolean useCustomPosition) {
this.useCustomPosition = useCustomPosition;

MapButtonState buttonState = getButtonState();
FabMarginPreference marginPreference = buttonState != null ? buttonState.getFabMarginPref() : null;
if (useCustomPosition && marginPreference != null) {
setOnLongClickListener(v -> {
Vibrator vibrator = (Vibrator) mapActivity.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_SHORT);
setScaleX(1.5f);
setScaleY(1.5f);
setAlpha(0.95f);
setOnTouchListener(new MapButtonTouchListener());
return true;
});
}
}

public void setUseDefaultAppearance(boolean useDefaultAppearance) {
Expand Down Expand Up @@ -254,7 +272,7 @@ protected void updateIcon() {
}

protected void updateSize() {
int size = AndroidUtils.dpToPx(getContext(), appearanceParams.getSize()) + shadowPadding;
int size = getSize() + shadowPadding;
ViewGroup.LayoutParams params = getLayoutParams();
params.height = size;
params.width = size;
Expand All @@ -263,8 +281,8 @@ protected void updateSize() {

protected void updateBackground() {
Context context = getContext();
int size = getSize();
float opacity = appearanceParams.getOpacity();
int size = AndroidUtils.dpToPx(context, appearanceParams.getSize());
int cornerRadius = AndroidUtils.dpToPx(context, appearanceParams.getCornerRadius());

GradientDrawable normal = new GradientDrawable();
Expand Down Expand Up @@ -351,9 +369,12 @@ protected boolean updateVisibility(boolean visible) {
}

public void updateMargins() {
if (mapActivity == null) {
return;
}
MapButtonState buttonState = getButtonState();
FabMarginPreference preference = buttonState != null ? buttonState.getFabMarginPref() : null;
if (mapActivity != null && useCustomPosition && preference != null) {
if (preference != null && useCustomPosition) {
if (AndroidUiHelper.isOrientationPortrait(mapActivity)) {
Pair<Integer, Integer> margins = preference.getPortraitFabMargins();
Pair<Integer, Integer> defMargins = preference.getDefaultPortraitMargins();
Expand All @@ -364,6 +385,28 @@ public void updateMargins() {
setFabButtonMargin(mapActivity, this, margins, defMargins.first, defMargins.second);
}
}
ViewParent parent = getParent();
if (parent instanceof MapButtonsLayout layout) {
layout.updateButton(this);
}
}

public void saveMargins() {
MapButtonState buttonState = getButtonState();
FabMarginPreference preference = buttonState != null ? buttonState.getFabMarginPref() : null;
if (mapActivity != null && useCustomPosition && preference != null) {
MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
if (AndroidUiHelper.isOrientationPortrait(mapActivity)) {
preference.setPortraitFabMargin(params.rightMargin, params.bottomMargin);
} else {
preference.setLandscapeFabMargin(params.rightMargin, params.bottomMargin);
}
}
}

public int getSize() {
ButtonAppearanceParams params = appearanceParams != null ? appearanceParams : getAppearanceParams();
return AndroidUtils.dpToPx(getContext(), params.getSize());
}

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,22 @@
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewParent;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;

import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.settings.backend.preferences.FabMarginPreference;
import net.osmand.plus.utils.AndroidUtils;
import net.osmand.plus.views.controls.MapButtonsLayout;

public class MapButtonTouchListener implements OnTouchListener {

private final MapActivity activity;
private final FabMarginPreference preference;

private int initialMarginX = 0;
private int initialMarginY = 0;
private float initialTouchX = 0;
private float initialTouchY = 0;

public MapButtonTouchListener(@NonNull MapActivity activity, @NonNull FabMarginPreference preference) {
this.activity = activity;
this.preference = preference;
}

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View view, MotionEvent event) {
Expand All @@ -44,24 +35,28 @@ public boolean onTouch(View view, MotionEvent event) {
view.setScaleX(1);
view.setScaleY(1);
view.setAlpha(1f);
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams();
if (AndroidUiHelper.isOrientationPortrait(activity))
preference.setPortraitFabMargin(params.rightMargin, params.bottomMargin);
else preference.setLandscapeFabMargin(params.rightMargin, params.bottomMargin);

ViewParent parent = view.getParent();
if (parent instanceof MapButtonsLayout layout) {
layout.updateButton((MapButton) view);
}
return true;
}
case MotionEvent.ACTION_MOVE -> {
if (initialMarginX == 0 && initialMarginY == 0 && initialTouchX == 0 && initialTouchY == 0)
if (initialMarginX == 0 && initialMarginY == 0 && initialTouchX == 0 && initialTouchY == 0) {
setUpInitialValues(view, event);
int padding = AndroidUtils.calculateTotalSizePx(activity, R.dimen.map_button_margin);
}
int padding = AndroidUtils.calculateTotalSizePx(view.getContext(), R.dimen.map_button_margin);
FrameLayout parent = (FrameLayout) view.getParent();
FrameLayout.LayoutParams param = (FrameLayout.LayoutParams) view.getLayoutParams();
int deltaX = (int) (initialTouchX - event.getRawX());
int deltaY = (int) (initialTouchY - event.getRawY());
int newMarginX = interpolate(initialMarginX + deltaX, view.getWidth(), parent.getWidth() - padding * 2);
int newMarginY = interpolate(initialMarginY + deltaY, view.getHeight(), parent.getHeight() - padding * 2);
if (view.getHeight() + newMarginY <= parent.getHeight() - padding * 2 && newMarginY > 0)

if (view.getHeight() + newMarginY <= parent.getHeight() - padding * 2 && newMarginY > 0) {
param.bottomMargin = newMarginY;
}
if (view.getWidth() + newMarginX <= parent.getWidth() - padding * 2 && newMarginX > 0) {
param.rightMargin = newMarginX;
}
Expand All @@ -73,13 +68,14 @@ public boolean onTouch(View view, MotionEvent event) {
}

private int interpolate(int value, int divider, int boundsSize) {
if (value <= divider && value > 0) return value * value / divider;
else {
int leftMargin = boundsSize - value - divider;
if (leftMargin <= divider && value < boundsSize - divider)
return leftMargin - (leftMargin * leftMargin / divider) + value;
else return value;
if (value <= divider && value > 0) {
return value * value / divider;
}
int leftMargin = boundsSize - value - divider;
if (leftMargin <= divider && value < boundsSize - divider) {
return leftMargin - (leftMargin * leftMargin / divider) + value;
}
return value;
}

private void setUpInitialValues(@NonNull View view, @NonNull MotionEvent event) {
Expand Down
Loading
Loading