Cancel overlay PTT on gesture faults and ignore callbacks from retired windows
This commit is contained in:
1 parent
b3d70ea92e
commit
982315bacc
1 file changed
+54
-29
@@ -392,7 +392,7 @@ class RadioOverlayService : Service() {
|
||||
.build()
|
||||
|
||||
private fun addOverlay() {
|
||||
val wm = windowManager ?: return
|
||||
val wm = requireNotNull(windowManager) { "window manager unavailable" }
|
||||
val v = TextView(this).apply {
|
||||
text = "\uD83C\uDF99"
|
||||
textSize = 21f
|
||||
@@ -430,6 +430,12 @@ class RadioOverlayService : Service() {
|
||||
private var secondTap = false
|
||||
|
||||
override fun onTouch(view: View, event: MotionEvent): Boolean {
|
||||
if (destroyed || currentService !== this@RadioOverlayService || overlayView !== v || !overlayReady || pendingStop) return false
|
||||
if (!MicrophonePermission.granted() || !ChatApplication.instance.configProvider.getRadioOverlay() || !canDrawOverlays(this@RadioOverlayService)) {
|
||||
removeOverlay()
|
||||
return false
|
||||
}
|
||||
try {
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
startX = params?.x ?: lp.x
|
||||
@@ -482,46 +488,58 @@ class RadioOverlayService : Service() {
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
latched = false
|
||||
lastTapUpTime = 0L
|
||||
endTalk()
|
||||
endTalk(immediate = true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
} catch (e: Exception) {
|
||||
latched = false; lastTapUpTime = 0L
|
||||
removeOverlay()
|
||||
setStatus(R.string.overlay_show_failed)
|
||||
LogManager.addLog("ERROR", "RadioOverlay", "Overlay touch failed action=${event.action} x=${event.rawX} " +
|
||||
"y=${event.rawY}; own PTT cancelled: $e")
|
||||
return true
|
||||
}
|
||||
}
|
||||
})
|
||||
wm.addView(v, lp)
|
||||
overlayView = v
|
||||
params = lp
|
||||
overlayReady = false
|
||||
_isShown.value = true // addView может бросить после добавления окна: до rollback считаем окно возможным.
|
||||
wm.addView(v, lp)
|
||||
overlayReady = true
|
||||
positionDragged = false
|
||||
placeAtDefaultCenter()
|
||||
_isShown.value = true
|
||||
LogManager.addLog("INFO", "RadioOverlay", "overlay shown")
|
||||
LogManager.addLog("INFO", "RadioOverlay", "overlay shown view=${System.identityHashCode(v)} x=${lp.x} y=${lp.y}")
|
||||
}
|
||||
|
||||
/* Поправка по фактическим координатам окна учитывает системные отступы оверлея. */
|
||||
private fun placeAtDefaultCenter() {
|
||||
if (destroyed || currentService !== this || !overlayReady) return
|
||||
val v = overlayView ?: return
|
||||
if (positionDragged) return
|
||||
if (!v.isLaidOut) {
|
||||
v.doOnLayout { placeAtDefaultCenter() }
|
||||
v.doOnLayout { if (overlayView === v && !destroyed) placeAtDefaultCenter() }
|
||||
return
|
||||
}
|
||||
if (positioningPending) return
|
||||
positioningPending = true
|
||||
v.postOnAnimation {
|
||||
if (destroyed || currentService !== this || overlayView !== v || !overlayReady) return@postOnAnimation
|
||||
positioningPending = false
|
||||
val target = defaultCenter ?: return@postOnAnimation
|
||||
val lp = params ?: return@postOnAnimation
|
||||
if (overlayView !== v || positionDragged) return@postOnAnimation
|
||||
val actual = IntArray(2)
|
||||
v.getLocationOnScreen(actual)
|
||||
val dx = target.x - actual[0] - v.width / 2
|
||||
val dy = target.y - actual[1] - v.height / 2
|
||||
if (dx == 0 && dy == 0) return@postOnAnimation
|
||||
lp.x += dx
|
||||
lp.y += dy
|
||||
try {
|
||||
windowManager?.updateViewLayout(v, lp)
|
||||
val actual = IntArray(2)
|
||||
v.getLocationOnScreen(actual)
|
||||
val dx = target.x - actual[0] - v.width / 2
|
||||
val dy = target.y - actual[1] - v.height / 2
|
||||
if (dx == 0 && dy == 0) return@postOnAnimation
|
||||
lp.x += dx
|
||||
lp.y += dy
|
||||
requireNotNull(windowManager) { "window manager unavailable" }.updateViewLayout(v, lp)
|
||||
LogManager.addLog("DEBUG", "RadioOverlay", "default position center=${target.x},${target.y} x=${lp.x} y=${lp.y}")
|
||||
} catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "RadioOverlay", "default position failed: ${e.message}")
|
||||
@@ -536,10 +554,11 @@ class RadioOverlayService : Service() {
|
||||
updateButtonColor()
|
||||
}
|
||||
|
||||
private fun endTalk() {
|
||||
if (!talking) return
|
||||
private fun endTalk(immediate: Boolean = false) {
|
||||
if (!talking && !immediate) return
|
||||
talking = false
|
||||
RadioAudioEngine.instance().talkEnd(RadioPttSource.OVERLAY)
|
||||
if (immediate) RadioAudioEngine.instance().cancelTalk(RadioPttSource.OVERLAY)
|
||||
else RadioAudioEngine.instance().talkEnd(RadioPttSource.OVERLAY)
|
||||
updateButtonColor()
|
||||
}
|
||||
|
||||
@@ -552,21 +571,27 @@ class RadioOverlayService : Service() {
|
||||
private fun dp(v: Int): Int = (v * resources.displayMetrics.density).toInt()
|
||||
|
||||
override fun onDestroy() {
|
||||
checkMain()
|
||||
if (destroyed) return
|
||||
destroyed = true
|
||||
val owner = currentService === this
|
||||
serviceScope.cancel()
|
||||
removeOverlay()
|
||||
starting = false
|
||||
running = false
|
||||
pendingStop = false
|
||||
if (currentService === this) currentService = null
|
||||
_isShown.value = false
|
||||
if (_status.value == R.string.overlay_active || _status.value == R.string.overlay_starting) {
|
||||
setStatus(if (ChatApplication.instance.configProvider.getRadioOverlay()) R.string.overlay_stopped else null)
|
||||
}
|
||||
LogManager.addLog("INFO", "RadioOverlay", "overlay destroyed")
|
||||
super.onDestroy()
|
||||
if (restartAfterStop) {
|
||||
var restart = false
|
||||
if (owner) {
|
||||
starting = false; running = false; pendingStop = false
|
||||
if (overlayView == null) { currentService = null; windowManager = null }
|
||||
if (_status.value == R.string.overlay_active || _status.value == R.string.overlay_starting) {
|
||||
setStatus(if (ChatApplication.instance.configProvider.getRadioOverlay()) R.string.overlay_stopped else null)
|
||||
}
|
||||
try { stopForeground(STOP_FOREGROUND_REMOVE) } catch (e: Exception) {
|
||||
LogManager.addLog("ERROR", "RadioOverlay", "Foreground notification removal failed id=$NOTIF_ID: $e")
|
||||
}
|
||||
restart = restartAfterStop
|
||||
restartAfterStop = false
|
||||
sync(ChatApplication.instance)
|
||||
}
|
||||
LogManager.addLog("INFO", "RadioOverlay", "overlay destroyed owner=$owner windowRetained=${overlayView != null} restart=$restart")
|
||||
super.onDestroy()
|
||||
if (restart) sync(ChatApplication.instance)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user