Files
fluffytrix/.claude/agent-memory/android-bug-hunter/MEMORY.md
2026-03-03 20:32:53 +00:00

3.1 KiB

Android Bug Hunter — Project Memory

Recurring Patterns Found

NotificationHelper: runBlocking on Service Thread

NotificationHelper.show() calls runBlocking three times (notificationsEnabled, mutedRooms, roomNameCache). This blocks the PushService callback thread. Fix: pass pre-collected values in or make show() a suspend fun. See: push/NotificationHelper.kt

FluffytrixPushService: Leaking CoroutineScope

FluffytrixPushService creates its own CoroutineScope(SupervisorJob() + Dispatchers.IO) but never cancels it when the service is destroyed. Coroutines launched from onNewEndpoint/onUnregistered can outlive the service. Fix: cancel scope in onDestroy().

PushRegistrationManager: OkHttp response body not closed

registerPusher() stores response.body?.string() (which auto-closes), but the execute() call itself is never inside a use {} block — if body?.string() throws, the response is not closed. Fix: wrap in response.use { ... }.

build.gradle.kts: API key in plain BuildConfig

TENOR_API_KEY hardcoded in buildConfigField — visible in plaintext in the APK's BuildConfig class.

MainViewModel: onUpdate listener launches on Dispatchers.Default inside viewModelScope

The timeline listener's onUpdate callback launches a coroutine with viewModelScope.launch(Dispatchers.Default). If the ViewModel is cleared mid-flight the scope is cancelled but the onUpdate lambda (captured by the SDK via JNI) can still fire and attempt launch on a cancelled scope — harmless but noisy.

MainViewModel: CoroutineScope inside loadTimeline shared across closures

A Mutex local to loadTimeline is captured by the TimelineListener lambda. When loadTimeline is called again for a new room, the old listener still holds the old mutex — correct, but produces confusing parallelism.

MainViewModel: sendGif creates a new OkHttpClient per call

A brand-new OkHttpClient() is constructed inside sendGif on every invocation. Should reuse a shared client (like the one in PushRegistrationManager).

SettingsScreen: new OkHttpClient per API-key test

A new OkHttpClient() is created on every "Save" button press inside SettingsScreen. Leaks the connection pool after the composable is disposed.

DeepLinkState: global singleton StateFlow — never clears on logout

DeepLinkState is a process-level object. After logout/re-login the stale pendingRoomId can navigate the new session to the old room.

PreferencesManager: notificationsEnabled default logic is inverted-looking

prefs[KEY_NOTIFICATIONS_ENABLED] != false — treats missing key as true (desired), but any corruption storing a non-boolean type would also return true. Low risk but worth noting.

Key File Paths

  • Push infra: app/src/main/java/com/example/fluffytrix/push/
  • ViewModel: app/src/main/java/com/example/fluffytrix/ui/screens/main/MainViewModel.kt
  • Settings UI: app/src/main/java/com/example/fluffytrix/ui/screens/settings/SettingsScreen.kt
  • DI: app/src/main/java/com/example/fluffytrix/di/AppModule.kt
  • Prefs: app/src/main/java/com/example/fluffytrix/data/local/PreferencesManager.kt