Skip to content

fix: a swipe from the screen edge no longer opens the menu (#213) - #216

Merged
compot-dev merged 2 commits into
v2from
fix/edge-swipe-213
Oct 15, 2026
Merged

compot-dev merged 2 commits into
v2from
fix/edge-swipe-213

Conversation

@compot-dev

Copy link
Copy Markdown
Collaborator

Fixes #213

Cause. GestureDetector confirms a single tap from ACTION_DOWN and ACTION_UP with no ACTION_MOVE past the touch slop between them — it never looks at where the up is. When the system keeps the moves of its edge swipe (status bar pull), the app sees a down and a far-away up and opens the menu. Reproduced on the TECNO KM4 with input motionevent DOWN 800 0 + UP 800 400 (menu opened on 2.11).

Fix. TapGuard follows every touch sequence in Activity.dispatchTouchEvent (before the views see it). A touch opens the menu only when the sequence

  • started outside the edge zone: per side the larger of the system gesture insets (getInsets(systemGestures()) on Android 11+, getSystemGestureInsets() on 10, from the decor's insets listener) and a fixed band of 24 dp top/bottom, 16 dp left/right;
  • stayed within the touch slop, the up included;
  • had one finger (no pinch) and was not cancelled.

Applies to the single view (onSingleTapConfirmed), the grid cells and the U hint (their onClick). Pinch, pan, double tap (zoom reset) and long press are unchanged. Keys (remote, D-pad) do not pass through dispatchTouchEvent; a cell's click from a key (not in touch mode) still opens the menu.

startsInEdgeZone / edgeZone and the sequence logic are pure Java with unit tests (TapGuardTest).

Version 2.13 (38).

GestureDetector confirms a single tap from a down and an up with no move
between, wherever the up is: when the system keeps the moves of its edge
swipe (status bar), the app saw a tap and opened the menu.

TapGuard follows every touch sequence in Activity.dispatchTouchEvent; the
menu opens on a touch only when the sequence started outside the edge zone
(the larger of the system gesture insets, Android 10+, and a fixed band of
24 dp top/bottom and 16 dp left/right), the finger stayed within the touch
slop (up included), with one finger and no cancel. Applies to the single
view, the grid cells and the U hint; keys (remote, D-pad) are unchanged.

Version 2.13 (38).

Fixes #213
@compot-dev compot-dev added the awaiting device test PR ready, merge only after testing on a real device label Oct 15, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Prevent edge swipes from opening the decoder menu

🐞 Bug fix 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Prevent edge swipes and drags from opening the menu when Android omits move events.
• Apply tap validation across video, grid, and U-hint views while preserving key clicks and other
 gestures.
• Add unit tests for touch sequences and bump the app to version 2.13.
Diagram

graph TD
  Touch["Touch sequence"] --> Dispatch["Activity dispatch"] --> Guard["TapGuard"] --> Valid{"Valid tap?"} -->|Yes| Callbacks["Menu callbacks"] --> Menu["MenuManager"]
  Insets["Gesture insets"] --> Guard
  Key["Key click"] --> Callbacks
Loading
High-Level Assessment

Keep the activity-level guard: it observes the complete touch sequence before any of the menu-opening views handle it. Per-view touch checks would duplicate logic and risk interfering with the existing gesture detectors.

Files changed (4) +263 / -13

Bug fix (2) +159 / -11
Decoder.javaGate touch-driven menu opening across decoder views +81/-11

Gate touch-driven menu opening across decoder views

• Tracks touches in Activity.dispatchTouchEvent and derives edge zones from system gesture insets and fixed bands. Checks TapGuard before opening the menu from the single video view, grid cells, or U hint, while retaining key-driven cell clicks.

android/app/src/main/java/com/openipc/decoder/Decoder.java

TapGuard.javaAdd touch-sequence tap validation +78/-0

Add touch-sequence tap validation

• Introduces a pure-Java guard that rejects touches starting in edge zones, movement beyond touch slop, multi-finger sequences, and cancellations. It checks the up position even when no move event arrives.

android/app/src/main/java/com/openipc/decoder/TapGuard.java

Tests (1) +102 / -0
TapGuardTest.javaTest edge zones and rejected touch sequences +102/-0

Test edge zones and rejected touch sequences

• Covers edge boundaries, fixed and system-derived zone sizes, valid taps, missing move events, drags, pinches, cancellations, and sequence reset.

android/app/src/test/java/com/openipc/decoder/TapGuardTest.java

Other (1) +2 / -2
build.gradleBump app version to 2.13 +2/-2

Bump app version to 2.13

• Increases versionCode from 37 to 38 and versionName from 2.12 to 2.13.

android/app/build.gradle

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Edge swipes can still open the menu ✓ Resolved
Description
onSingleTapConfirmed reads tapGuard.wasTap() when the callback runs, rather than the result for
the touch sequence being confirmed. If an edge swipe is followed by a new central touch before
confirmation, dispatchTouchEvent resets the guard on that new down, allowing the earlier swipe's
confirmation to open the menu; a new invalid touch can likewise suppress a valid tap.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[308]

+                if (!tapGuard.wasTap()) return true;
Evidence
The surface forwards events to GestureDetector, whose single-tap confirmation checks the shared
guard. A subsequent ACTION_DOWN replaces that guard's result before the earlier gesture must be
confirmed.

android/app/src/main/java/com/openipc/decoder/Decoder.java[305-325]
android/app/src/main/java/com/openipc/decoder/Decoder.java[2169-2193]
android/app/src/main/java/com/openipc/decoder/TapGuard.java[52-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Delayed single-tap confirmation reads a guard that later touches can change, so it can accept an earlier edge swipe or reject an earlier valid tap.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[305-311]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[2169-2193]
- android/app/src/main/java/com/openipc/decoder/TapGuard.java[52-76]
## Recommended Fix
Record validity for the completed sequence and associate it with the gesture being confirmed, rather than reading the mutable current-sequence flag in the callback. Test an invalid edge swipe followed immediately by a central down, and the reverse.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Accessibility clicks fail after a drag ✓ Resolved
Description
touchTapOrKey treats v.isInTouchMode() as the source of a click, even though it describes the
window's input mode rather than whether this click came from a touch sequence. An accessibility or
programmatic click on a grid cell or U hint while the window remains in touch mode is rejected when
a preceding drag or cancellation left tapGuard.wasTap() false.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[R2199-2200]

+    private boolean touchTapOrKey(View v) {
+        return !v.isInTouchMode() || tapGuard.wasTap();
Evidence
Both click listeners now use a helper that substitutes window touch mode and the last touch result
for the source of the current click. The guard remains false after a drag or cancellation, so a
click not preceded by a new valid touch can be discarded.

android/app/src/main/java/com/openipc/decoder/Decoder.java[885-889]
android/app/src/main/java/com/openipc/decoder/Decoder.java[950-954]
android/app/src/main/java/com/openipc/decoder/Decoder.java[2195-2201]
android/app/src/main/java/com/openipc/decoder/TapGuard.java[59-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The grid and hint click listeners reject non-touch activations based on stale touch state whenever the window is in touch mode.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[885-889]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[950-954]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[2195-2201]
## Recommended Fix
Track whether the specific click was generated by a completed touch sequence, and apply TapGuard only to that case. Allow accessibility and programmatic click actions independently of the last touch result.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can hide the parts of a finding you never read, like the evidence or the agent prompt

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread android/app/src/main/java/com/openipc/decoder/Decoder.java
Comment thread android/app/src/main/java/com/openipc/decoder/Decoder.java Outdated
…bility click (Qodo on #216)

- TapGuard answers for the last finished sequence (up or cancel), so a tap
  confirmed late by GestureDetector, or a click posted after the up, is not
  judged by a new touch that has just begun.
- Cells and the U hint gate only clicks that come from a touch's up
  (clickFromTouch, cleared after the posted click); key, accessibility and
  programmatic clicks open the menu as before, also in touch mode.
@compot-dev

Copy link
Copy Markdown
Collaborator Author

Qodo findings 1 and 2 addressed in 8d51e2e (Qodo: both resolved, 0 open).

Device check, TECNO KM4 (Android 15, 3-button nav), CI APK of 8d51e2e (2.13), single view:

Input Result
input swipe 800 0 800 400 (top edge) status bar shown, no menu
motionevent DOWN 800 0 + UP 800 400, no moves (opened the menu on 2.11) no menu
DOWN 800 200 + UP 800 500, no moves no menu
swipe from bottom edge / right edge / left edge no menu
input tap 800 400 menu opens
double tap no menu (zoom reset)
edge swipe, then a quick tap in the middle menu opens (the tap)

Pinch/pan can't be simulated over adb; still needs a finger check.

@compot-dev

Copy link
Copy Markdown
Collaborator Author

Pinch-zoom and pan checked by hand on the TECNO KM4 with this build: they work as before.

@compot-dev compot-dev removed the awaiting device test PR ready, merge only after testing on a real device label Oct 15, 2026
@compot-dev
compot-dev merged commit 584c8e4 into v2 Oct 15, 2026
4 checks passed
@compot-dev
compot-dev deleted the fix/edge-swipe-213 branch October 15, 2026 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A swipe down from the very top edge opens the app menu instead of only showing the status bar

2 participants