Skip to content

Commit 2f6a068

Browse files
undisputedPclaude
andauthored
v1.0.8: throttle accessibility tree walks to fix frame-rate drop (#12)
User reported visible frame-rate drop while scrolling YouTube with the accessibility service active. Cause: YouTube fires TYPE_WINDOW_CONTENT_CHANGED at ~30 Hz during scroll, and v1.0.7 ran the full 1200-node tree walk on every event. That's ~36k node inspections per second while scrolling — enough to push the system past the 16ms frame budget. Two changes: 1. Throttle walks for content-changed events to one every 250ms. State-changed events (rare; fire when entering Shorts / navigating screens) keep being processed immediately so detection latency on the path that matters is unchanged. 2. Reduce MAX_NODES_PER_WALK from 1200 to 600. Empirically the relevant Shorts UI lives in the first few hundred nodes; the 1200 cap was just safety margin we don't need. Net effect for the user: while on YouTube home / scrolling the feed, we walk ~4×/sec instead of ~30×/sec, and each walk visits half as many nodes. Total CPU work in the steady state is about 1/15th of v1.0.7. Detection on Shorts open is unaffected since that triggers a STATE_CHANGED event which still walks immediately. Bump versionCode 8 → 9, versionName 1.0.7 → 1.0.8. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 345afd4 commit 2f6a068

2 files changed

Lines changed: 23 additions & 5 deletions

File tree

‎ShortsBlocker/app/build.gradle‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ android {
1111
applicationId "com.shortsBlocker"
1212
minSdk 21
1313
targetSdk 34
14-
versionCode 8
15-
versionName "1.0.7"
14+
versionCode 9
15+
versionName "1.0.8"
1616
}
1717

1818
buildTypes {

‎ShortsBlocker/app/src/main/java/com/shortsBlocker/service/ShortsAccessibilityService.kt‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,14 @@ class ShortsAccessibilityService : AccessibilityService() {
4343
companion object {
4444
const val TAG = "ShortsA11y"
4545
private const val MIN_DISMISS_INTERVAL_MS = 600L
46-
private const val MAX_NODES_PER_WALK = 1200
46+
// Cap how often we walk the tree for content-changed events.
47+
// YouTube fires TYPE_WINDOW_CONTENT_CHANGED at ~30 Hz during
48+
// normal scrolling; without throttle we ate enough CPU to drop
49+
// user-visible frame rate. State-changed events (entering Shorts,
50+
// navigating screens) bypass this throttle so detection latency
51+
// stays low when it matters.
52+
private const val MIN_CONTENT_WALK_INTERVAL_MS = 250L
53+
private const val MAX_NODES_PER_WALK = 600
4754

4855
// STRONG view-ID suffixes (matches "<package>:id/<suffix>" exactly).
4956
private val STRONG_VIEW_ID_SUFFIXES = setOf(
@@ -93,6 +100,7 @@ class ShortsAccessibilityService : AccessibilityService() {
93100
}
94101

95102
private var lastDismissAt: Long = 0L
103+
private var lastWalkAt: Long = 0L
96104

97105
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
98106
if (event == null) return
@@ -102,11 +110,21 @@ class ShortsAccessibilityService : AccessibilityService() {
102110
val pkg = event.packageName?.toString() ?: return
103111
if (!pkg.contains("youtube", ignoreCase = true)) return
104112

113+
val now = System.currentTimeMillis()
105114
lastPackage = pkg
106115
lastEventType = AccessibilityEvent.eventTypeToString(event.eventType)
107-
lastEventAtMs = System.currentTimeMillis()
116+
lastEventAtMs = now
108117

109-
if (lastEventAtMs - lastDismissAt < MIN_DISMISS_INTERVAL_MS) return
118+
// Recently dismissed → don't even consider another walk.
119+
if (now - lastDismissAt < MIN_DISMISS_INTERVAL_MS) return
120+
121+
// State changes (rare, fire on screen transitions like opening
122+
// Shorts) always processed. Content changes (very frequent) get
123+
// throttled so we don't burn CPU on every scroll tick.
124+
val isStateChange = event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
125+
if (!isStateChange && now - lastWalkAt < MIN_CONTENT_WALK_INTERVAL_MS) return
126+
127+
lastWalkAt = now
110128

111129
val root = rootInActiveWindow ?: return
112130
try {

0 commit comments

Comments
 (0)