From 961bfa1cc78e1a315fa06a867f10b1455e30fd15 Mon Sep 17 00:00:00 2001 From: chumv Date: Thu, 19 Sep 2024 17:24:37 +0300 Subject: [PATCH 1/2] Move MapButtonTouchListener to MapButton --- .../controls/maphudbuttons/Map3DButton.java | 16 -------- .../controls/maphudbuttons/MapButton.java | 22 ++++++++++- .../maphudbuttons/MapButtonTouchListener.java | 38 ++++++++++++------- .../maphudbuttons/QuickActionButton.java | 12 ------ 4 files changed, 46 insertions(+), 42 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/Map3DButton.java b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/Map3DButton.java index 33fc547cd5b..46fae21e972 100644 --- a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/Map3DButton.java +++ b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/Map3DButton.java @@ -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; @@ -41,7 +39,6 @@ public Map3DButton(@NonNull Context context, @Nullable AttributeSet attrs, int d animateDraggingMapThread = getMapView().getAnimatedDraggingThread(); setOnClickListener(getOnCLickListener()); - setOnLongClickListener(getLongClickListener()); } @Nullable @@ -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(); diff --git a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java index b94bf1d0096..b91bf182481 100644 --- a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java +++ b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java @@ -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; @@ -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; @@ -165,6 +168,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(mapActivity, marginPreference)); + return true; + }); + } } public void setUseDefaultAppearance(boolean useDefaultAppearance) { @@ -351,9 +368,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 margins = preference.getPortraitFabMargins(); Pair defMargins = preference.getDefaultPortraitMargins(); diff --git a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java index aacfe92149a..84be97cd9f5 100644 --- a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java +++ b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java @@ -44,24 +44,26 @@ 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); + + updatePreference(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; } @@ -72,14 +74,24 @@ public boolean onTouch(View view, MotionEvent event) { return false; } + private void updatePreference(@NonNull View view) { + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams(); + if (AndroidUiHelper.isOrientationPortrait(activity)) { + preference.setPortraitFabMargin(params.rightMargin, params.bottomMargin); + } else { + preference.setLandscapeFabMargin(params.rightMargin, params.bottomMargin); + } + } + 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) { diff --git a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/QuickActionButton.java b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/QuickActionButton.java index d7e6cdd5094..4bfb0cd97b8 100644 --- a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/QuickActionButton.java +++ b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/QuickActionButton.java @@ -1,9 +1,6 @@ package net.osmand.plus.views.controls.maphudbuttons; -import static net.osmand.plus.views.layers.ContextMenuLayer.VIBRATE_SHORT; - import android.content.Context; -import android.os.Vibrator; import android.util.AttributeSet; import androidx.annotation.NonNull; @@ -50,15 +47,6 @@ public QuickActionButton(@NonNull Context context, @Nullable AttributeSet attrs, } setInvalidated(true); }); - 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(mapActivity, buttonState.getFabMarginPref())); - return true; - }); } @Nullable From d82e1719b10a10dcfe660ae7d102759a40046abb Mon Sep 17 00:00:00 2001 From: chumv Date: Mon, 23 Sep 2024 12:38:44 +0300 Subject: [PATCH 2/2] Add MapButtonsLayout --- OsmAnd/res/layout/map_hud_quick_actions.xml | 4 +- .../plus/views/controls/MapButtonsLayout.java | 130 ++++++++++++++++++ .../controls/maphudbuttons/MapButton.java | 29 +++- .../maphudbuttons/MapButtonTouchListener.java | 28 +--- .../views/layers/MapQuickActionLayer.java | 2 +- .../views/layers/base/OsmandMapLayer.java | 30 ++-- 6 files changed, 185 insertions(+), 38 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/views/controls/MapButtonsLayout.java diff --git a/OsmAnd/res/layout/map_hud_quick_actions.xml b/OsmAnd/res/layout/map_hud_quick_actions.xml index 7c68f69dfcf..85610c4afe5 100644 --- a/OsmAnd/res/layout/map_hud_quick_actions.xml +++ b/OsmAnd/res/layout/map_hud_quick_actions.xml @@ -4,8 +4,8 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - 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 initBoundIntersections(@NonNull MapButton button) { + QuadTree 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 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; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java index b91bf182481..780a1954658 100644 --- a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java +++ b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButton.java @@ -47,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; @@ -178,7 +179,7 @@ public void setUseCustomPosition(boolean useCustomPosition) { setScaleX(1.5f); setScaleY(1.5f); setAlpha(0.95f); - setOnTouchListener(new MapButtonTouchListener(mapActivity, marginPreference)); + setOnTouchListener(new MapButtonTouchListener()); return true; }); } @@ -271,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; @@ -280,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(); @@ -384,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 diff --git a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java index 84be97cd9f5..44a55dcb372 100644 --- a/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java +++ b/OsmAnd/src/net/osmand/plus/views/controls/maphudbuttons/MapButtonTouchListener.java @@ -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) { @@ -45,8 +36,10 @@ public boolean onTouch(View view, MotionEvent event) { view.setScaleY(1); view.setAlpha(1f); - updatePreference(view); - + ViewParent parent = view.getParent(); + if (parent instanceof MapButtonsLayout layout) { + layout.updateButton((MapButton) view); + } return true; } case MotionEvent.ACTION_MOVE -> { @@ -74,15 +67,6 @@ public boolean onTouch(View view, MotionEvent event) { return false; } - private void updatePreference(@NonNull View view) { - FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams(); - if (AndroidUiHelper.isOrientationPortrait(activity)) { - preference.setPortraitFabMargin(params.rightMargin, params.bottomMargin); - } else { - preference.setLandscapeFabMargin(params.rightMargin, params.bottomMargin); - } - } - private int interpolate(int value, int divider, int boundsSize) { if (value <= divider && value > 0) { return value * value / divider; diff --git a/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java index d33b3f82782..e624951cec9 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/MapQuickActionLayer.java @@ -120,7 +120,7 @@ private void updateButtons() { if (activity != null) { boolean nightMode = app.getDaynightHelper().isNightMode(); LayoutInflater inflater = UiUtilities.getInflater(activity, nightMode); - ViewGroup container = activity.findViewById(R.id.quick_actions_container); + ViewGroup container = activity.findViewById(R.id.map_buttons_container); container.removeAllViews(); List buttons = new ArrayList<>(); diff --git a/OsmAnd/src/net/osmand/plus/views/layers/base/OsmandMapLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/base/OsmandMapLayer.java index 203c3caef1c..d7b5fb00156 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/base/OsmandMapLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/base/OsmandMapLayer.java @@ -234,7 +234,7 @@ public void onPrepareBufferImage(Canvas canvas, RotatedTileBox tileBox, DrawSett } } - protected void updateResources(){ + protected void updateResources() { } @@ -339,23 +339,33 @@ public float getDensity() { } @NonNull - public static QuadTree initBoundIntersections(RotatedTileBox tileBox) { - QuadRect bounds = new QuadRect(0, 0, tileBox.getPixWidth(), tileBox.getPixHeight()); + public static QuadTree initBoundIntersections(@NonNull RotatedTileBox tileBox) { + return initBoundIntersections(tileBox.getPixWidth(), tileBox.getPixHeight()); + } + + @NonNull + public static QuadTree initBoundIntersections(float width, float height) { + QuadRect bounds = new QuadRect(0, 0, width, height); bounds.inset(-bounds.width() / 4, -bounds.height() / 4); return new QuadTree<>(bounds, 4, 0.6f); } - public static boolean intersects(QuadTree boundIntersections, float x, float y, float width, float height) { - List result = new ArrayList<>(); + public static boolean intersects(@NonNull QuadTree boundIntersections, float x, float y, float width, float height) { QuadRect visibleRect = calculateRect(x, y, width, height); - boundIntersections.queryInBox(new QuadRect(visibleRect.left, visibleRect.top, visibleRect.right, visibleRect.bottom), result); - for (QuadRect r : result) { - if (QuadRect.intersects(r, visibleRect)) { + return intersects(boundIntersections, visibleRect, true); + } + + public static boolean intersects(@NonNull QuadTree boundIntersections, @NonNull QuadRect visibleRect, boolean insert) { + List result = new ArrayList<>(); + boundIntersections.queryInBox(new QuadRect(visibleRect), result); + for (QuadRect rect : result) { + if (QuadRect.intersects(rect, visibleRect)) { return true; } } - boundIntersections.insert(visibleRect, - new QuadRect(visibleRect.left, visibleRect.top, visibleRect.right, visibleRect.bottom)); + if (insert) { + boundIntersections.insert(visibleRect, new QuadRect(visibleRect)); + } return false; }