fix problem with multiple WidgetOverlayViews

This commit is contained in:
Josia Pietsch 2025-05-10 16:04:33 +02:00
parent 24e90deb62
commit 061f3a1e77
Signed by: jrpie
GPG key ID: E70B571D66986A2D
3 changed files with 58 additions and 44 deletions

View file

@ -41,7 +41,7 @@ open class WidgetContainerView(
widgets.filter { it.panelId == widgetPanelId }.forEach { widget -> widgets.filter { it.panelId == widgetPanelId }.forEach { widget ->
widget.createView(activity)?.let { widget.createView(activity)?.let {
addView(it, LayoutParams(widget.position)) addView(it, LayoutParams(widget.position))
widgetViewById.put(widget.id, it) widgetViewById[widget.id] = it
} }
} }
} }

View file

@ -32,10 +32,8 @@ const val EXTRA_PANEL_ID = "widgetPanelId"
// We can't use AppCompatActivity, since some AppWidgets don't work there. // We can't use AppCompatActivity, since some AppWidgets don't work there.
class ManageWidgetsActivity : Activity(), UIObject { class ManageWidgetsActivity : Activity(), UIObject {
var panelId: Int = WidgetPanel.HOME.id private var panelId: Int = WidgetPanel.HOME.id
// We can't observe the livedata because this is not an AppCompatActivity
private var sharedPreferencesListener = private var sharedPreferencesListener =
SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey -> SharedPreferences.OnSharedPreferenceChangeListener { _, prefKey ->
if (prefKey == LauncherPreferences.widgets().keys().widgets()) { if (prefKey == LauncherPreferences.widgets().keys().widgets()) {

View file

@ -17,15 +17,12 @@ import android.view.ViewConfiguration
import androidx.core.graphics.contains import androidx.core.graphics.contains
import androidx.core.graphics.minus import androidx.core.graphics.minus
import androidx.core.graphics.toRect import androidx.core.graphics.toRect
import androidx.core.view.children
import de.jrpie.android.launcher.ui.widgets.WidgetContainerView import de.jrpie.android.launcher.ui.widgets.WidgetContainerView
import de.jrpie.android.launcher.widgets.GRID_SIZE import de.jrpie.android.launcher.widgets.GRID_SIZE
import de.jrpie.android.launcher.widgets.Widget import de.jrpie.android.launcher.widgets.Widget
import de.jrpie.android.launcher.widgets.WidgetPanel import de.jrpie.android.launcher.widgets.WidgetPanel
import de.jrpie.android.launcher.widgets.WidgetPosition import de.jrpie.android.launcher.widgets.WidgetPosition
import de.jrpie.android.launcher.widgets.updateWidget import de.jrpie.android.launcher.widgets.updateWidget
import kotlin.math.max
import kotlin.math.min
/** /**
* A variant of the [WidgetContainerView] which allows to manage widgets. * A variant of the [WidgetContainerView] which allows to manage widgets.
@ -38,6 +35,9 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
val TOUCH_SLOP_SQUARE: Int val TOUCH_SLOP_SQUARE: Int
val LONG_PRESS_TIMEOUT: Long val LONG_PRESS_TIMEOUT: Long
private var overlayViewById = HashMap<Int, WidgetOverlayView>()
init { init {
val configuration = ViewConfiguration.get(context) val configuration = ViewConfiguration.get(context)
TOUCH_SLOP = configuration.scaledTouchSlop TOUCH_SLOP = configuration.scaledTouchSlop
@ -47,36 +47,37 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
} }
enum class EditMode(val resize: (dx: Int, dy: Int, screenWidth: Int, screenHeight: Int, rect: Rect) -> Rect) { enum class EditMode(val resize: (dx: Int, dy: Int, screenWidth: Int, screenHeight: Int, rect: Rect) -> Rect) {
MOVE({ dx, dy, sw, sh, rect -> MOVE({ dx, dy, sw, sh, rect ->
val cdx = dx.coerceIn(-rect.left, sw - rect.right) val cdx = dx.coerceIn(-rect.left, sw - rect.right)
val cdy = dy.coerceIn(-rect.top, sh - rect.bottom) val cdy = dy.coerceIn(-rect.top, sh - rect.bottom)
Rect(rect.left + cdx, rect.top + cdy, rect.right + cdx, rect.bottom + cdy) Rect(rect.left + cdx, rect.top + cdy, rect.right + cdx, rect.bottom + cdy)
}), }),
TOP({ dx, dy, sw, sh, rect -> TOP({ _, dy, _, sh, rect ->
val cdy = dy.coerceIn(-rect.top, rect.bottom - rect.top - (2 * sh / GRID_SIZE) + 5) val cdy = dy.coerceIn(-rect.top, rect.bottom - rect.top - (2 * sh / GRID_SIZE) + 5)
Rect(rect.left, rect.top + cdy, rect.right, rect.bottom) Rect(rect.left, rect.top + cdy, rect.right, rect.bottom)
}), }),
BOTTOM({ dx, dy, sw, sh, rect -> BOTTOM({ _, dy, _, sh, rect ->
val cdy = dy.coerceIn((2 * sh / GRID_SIZE) + 5 + rect.top - rect.bottom, sh - rect.bottom) val cdy =
dy.coerceIn((2 * sh / GRID_SIZE) + 5 + rect.top - rect.bottom, sh - rect.bottom)
Rect(rect.left, rect.top, rect.right, rect.bottom + cdy) Rect(rect.left, rect.top, rect.right, rect.bottom + cdy)
}), }),
LEFT({ dx, dy, sw, sh, rect -> LEFT({ dx, _, sw, _, rect ->
val cdx = dx.coerceIn(-rect.left, rect.right - rect.left - (2 * sw / GRID_SIZE) + 5) val cdx = dx.coerceIn(-rect.left, rect.right - rect.left - (2 * sw / GRID_SIZE) + 5)
Rect(rect.left + cdx, rect.top, rect.right, rect.bottom) Rect(rect.left + cdx, rect.top, rect.right, rect.bottom)
}), }),
RIGHT({ dx, dy, sw, sh, rect -> RIGHT({ dx, _, sw, _, rect ->
val cdx = dx.coerceIn((2 * sw / GRID_SIZE) + 5 + rect.left - rect.right, sw - rect.right) val cdx =
dx.coerceIn((2 * sw / GRID_SIZE) + 5 + rect.left - rect.right, sw - rect.right)
Rect(rect.left, rect.top, rect.right + cdx, rect.bottom) Rect(rect.left, rect.top, rect.right + cdx, rect.bottom)
}), }),
} }
var selectedWidgetOverlayView: WidgetOverlayView? = null private var selectedWidgetOverlayView: WidgetOverlayView? = null
var selectedWidgetView: View? = null private var selectedWidgetView: View? = null
var currentGestureStart: Point? = null private var currentGestureStart: Point? = null
var startWidgetPosition: Rect? = null private var startWidgetPosition: Rect? = null
var lastPosition = Rect() private var lastPosition = Rect()
private val longPressHandler = Handler(Looper.getMainLooper()) private val longPressHandler = Handler(Looper.getMainLooper())
@ -94,17 +95,31 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
if (event.actionMasked == MotionEvent.ACTION_DOWN) { if (event.actionMasked == MotionEvent.ACTION_DOWN) {
val start = Point(event.x.toInt(), event.y.toInt()) val start = Point(event.x.toInt(), event.y.toInt())
currentGestureStart = start currentGestureStart = start
val view = children.mapNotNull { it as? WidgetOverlayView }.firstOrNull { val view = overlayViewById.asIterable()
RectF(it.x, it.y, it.x + it.width, it.y + it.height).toRect().contains(start) == true .map { it.value }.firstOrNull { overlayView ->
} ?: return false RectF(
overlayView.x,
overlayView.y,
overlayView.x + overlayView.width,
overlayView.y + overlayView.height
)
.toRect()
.contains(start)
} ?: return true
val position = (view.layoutParams as Companion.LayoutParams).position.getAbsoluteRect(width, height) val position =
(view.layoutParams as Companion.LayoutParams).position.getAbsoluteRect(
width,
height
)
selectedWidgetOverlayView = view selectedWidgetOverlayView = view
selectedWidgetView = widgetViewById.get(view.widgetId) ?: return true selectedWidgetView = widgetViewById[view.widgetId] ?: return true
startWidgetPosition = position startWidgetPosition = position
val positionInView = start.minus(Point(position.left, position.top)) val positionInView = start.minus(Point(position.left, position.top))
view.mode = view.getHandles().firstOrNull { it.position.contains(positionInView) }?.mode ?: EditMode.MOVE view.mode =
view.getHandles().firstOrNull { it.position.contains(positionInView) }?.mode
?: EditMode.MOVE
longPressHandler.postDelayed({ longPressHandler.postDelayed({
synchronized(this@WidgetManagerView) { synchronized(this@WidgetManagerView) {
@ -124,19 +139,20 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
} }
val view = selectedWidgetOverlayView ?: return true val view = selectedWidgetOverlayView ?: return true
val start = startWidgetPosition ?: return true val start = startWidgetPosition ?: return true
val absoluteNewPosition = view.mode?.resize( val absoluteNewPosition = (view.mode ?: return true).resize(
distanceX.toInt(), distanceX.toInt(),
distanceY.toInt(), distanceY.toInt(),
width, height, width, height,
start start
) ?: return true )
val newPosition = WidgetPosition.fromAbsoluteRect( val newPosition = WidgetPosition.fromAbsoluteRect(
absoluteNewPosition, width, height absoluteNewPosition, width, height
) )
if (newPosition != lastPosition) { if (absoluteNewPosition != lastPosition) {
lastPosition = absoluteNewPosition lastPosition = absoluteNewPosition
(view.layoutParams as Companion.LayoutParams).position = newPosition (view.layoutParams as Companion.LayoutParams).position = newPosition
(selectedWidgetView?.layoutParams as? Companion.LayoutParams)?.position = newPosition (selectedWidgetView?.layoutParams as? Companion.LayoutParams)?.position =
newPosition
requestLayout() requestLayout()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_PRESS) view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_PRESS)
@ -156,10 +172,9 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
} }
} }
} }
return true return true
} }
private fun endInteraction() { private fun endInteraction() {
startWidgetPosition = null startWidgetPosition = null
selectedWidgetOverlayView?.mode = null selectedWidgetOverlayView?.mode = null
@ -167,16 +182,17 @@ class WidgetManagerView(widgetPanelId: Int, context: Context, attrs: AttributeSe
override fun updateWidgets(activity: Activity, widgets: Collection<Widget>?) { override fun updateWidgets(activity: Activity, widgets: Collection<Widget>?) {
super.updateWidgets(activity, widgets) super.updateWidgets(activity, widgets)
if (widgets == null) {
return
}
children.filter { it is WidgetOverlayView }.forEach { removeView(it) }
widgets.filter { it.panelId == widgetPanelId }.forEach { widget -> synchronized(overlayViewById) {
WidgetOverlayView(activity).let { overlayViewById.forEach { removeView(it.value) }
addView(it) overlayViewById.clear()
it.widgetId = widget.id widgets?.filter { it.panelId == widgetPanelId }?.forEach { widget ->
(it.layoutParams as Companion.LayoutParams).position = widget.position WidgetOverlayView(activity).let {
it.widgetId = widget.id
addView(it)
(it.layoutParams as Companion.LayoutParams).position = widget.position
overlayViewById[widget.id] = it
}
} }
} }
} }