|
| 1 | +# Architecture Overview |
| 2 | + |
| 3 | +This document describes the high-level architecture of DebugOverlay. For code locations and common tasks, see [AGENTS.md](../AGENTS.md). |
| 4 | + |
| 5 | +## Module Structure |
| 6 | + |
| 7 | +``` |
| 8 | +┌─────────────────┐ ┌────────────────────┐ ┌────────────────────┐ |
| 9 | +│ debugoverlay │ │ extension-okhttp │ │ extension-timber │ |
| 10 | +│ (auto-start) │ │ (OkHttp intercept) │ │ (Timber capture) │ |
| 11 | +└────────┬────────┘ └─────────┬──────────┘ └─────────┬──────────┘ |
| 12 | + │ │ │ |
| 13 | + └────────────────────────┼──────────────────────────┘ |
| 14 | + │ |
| 15 | + ▼ |
| 16 | + ┌──────────────────────┐ |
| 17 | + │ debugoverlay-core │ |
| 18 | + │ │ |
| 19 | + │ • Data collection │ |
| 20 | + │ • UI components │ |
| 21 | + │ • Bug reporting │ |
| 22 | + │ • Public interfaces │ |
| 23 | + └──────────────────────┘ |
| 24 | +``` |
| 25 | + |
| 26 | +| Module | Purpose | |
| 27 | +|--------|---------| |
| 28 | +| `debugoverlay-core` | All core functionality: metrics, UI, bug reports, extension interfaces | |
| 29 | +| `debugoverlay` | Auto-installation via AndroidX Startup | |
| 30 | +| `debugoverlay-extension-okhttp` | HTTP traffic capture for OkHttp clients | |
| 31 | +| `debugoverlay-extension-timber` | Timber log capture with auto-plant | |
| 32 | + |
| 33 | +## Data Flow |
| 34 | + |
| 35 | +``` |
| 36 | +┌─────────────────────────────────────────────────────────────────┐ |
| 37 | +│ Data Sources │ |
| 38 | +│ (LogcatDataSource, DeviceInfoDataSource, JankStatsDataSource) │ |
| 39 | +└──────────────────────────┬──────────────────────────────────────┘ |
| 40 | + │ |
| 41 | + ▼ |
| 42 | + ┌─────────────────────────┐ |
| 43 | + │ DebugOverlayDataRepository │ |
| 44 | + │ (Central Hub) │ |
| 45 | + │ │ |
| 46 | + │ Aggregates all data │ |
| 47 | + │ Exposes as Flows │ |
| 48 | + └────────────┬─────────────┘ |
| 49 | + │ |
| 50 | + ▼ |
| 51 | + ┌───────────────────────────────────┐ |
| 52 | + │ Compose UI Layer │ |
| 53 | + │ │ |
| 54 | + │ collectAsStateWithLifecycle() │ |
| 55 | + └───────────────────────────────────┘ |
| 56 | +``` |
| 57 | + |
| 58 | +**Key components:** |
| 59 | +- `DebugOverlayDataRepository` — Central hub aggregating all data sources |
| 60 | +- `OverlayViewManager` — Window attachment via Curtains, overlay lifecycle |
| 61 | +- `BugReportGenerator` — Orchestrates capture → preview → ZIP flow |
| 62 | + |
| 63 | +## Extension Model |
| 64 | + |
| 65 | +Extensions integrate via interfaces in `debugoverlay-core`: |
| 66 | + |
| 67 | +```kotlin |
| 68 | +interface LogSource { |
| 69 | + val sourceName: String |
| 70 | + val logs: Flow<List<LogEntry>> |
| 71 | +} |
| 72 | + |
| 73 | +interface NetworkRequestSource { |
| 74 | + val requests: Flow<List<NetworkRequest>> |
| 75 | +} |
| 76 | + |
| 77 | +interface BugReportDataContributor { |
| 78 | + val filename: String |
| 79 | + fun writeTo(outputStream: OutputStream) |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +**Auto-registration pattern:** Extensions self-register in their `init` block by calling `DebugOverlay.configure {}`. AndroidX Startup provides zero-config initialization for core and Timber extension (via manifest-declared initializers); OkHttp extension requires manual interceptor registration. |
| 84 | + |
| 85 | +## UI Architecture |
| 86 | + |
| 87 | +``` |
| 88 | +DraggableOverlayPanel (attached via WindowManager) |
| 89 | +├── FullMetrics mode → Compact metrics panel |
| 90 | +│ └── Tap → DebugPanelDialog (tabbed interface) |
| 91 | +└── BugReporterOnly mode → DraggableBugReporterFab |
| 92 | +``` |
| 93 | + |
| 94 | +The overlay uses a synthetic `OverlayLifecycleOwner` to provide Compose lifecycle APIs outside the activity hierarchy. Lifecycle mirrors the target activity's lifecycle events (onResume, onPause, etc.) |
| 95 | + |
| 96 | +## Architectural Invariants |
| 97 | + |
| 98 | +| Invariant | Rationale | |
| 99 | +|-----------|-----------| |
| 100 | +| **No SYSTEM_ALERT_WINDOW** | Uses Curtains to attach to app windows; no special permission needed | |
| 101 | +| **Bounded collections** | `EvictingQueue` prevents OOM from unbounded log/request accumulation | |
| 102 | +| **Main process only** | Overlay is per-process singleton; no multi-process complexity | |
| 103 | +| **Extensions depend on core** | Loose coupling via interfaces; core has no knowledge of extensions | |
| 104 | + |
| 105 | +## Key Architectural Decisions |
| 106 | + |
| 107 | +| Decision | Rationale | |
| 108 | +|----------|------------------------------------------------------------------------| |
| 109 | +| **AndroidX Startup** | Zero configuration for consumers; runs before `Application.onCreate()` | |
| 110 | +| **Flow-based reactive data** | Natural fit for Compose; lifecycle-aware collection | |
| 111 | +| **Curtains for window management** | Robust overlay attachment without system permissions | |
| 112 | +| **Extension pattern** | Extensions self-register with core for convenience | |
| 113 | +| **Synthetic LifecycleOwner** | Enables Compose lifecycle APIs for overlay outside activity hierarchy | |
| 114 | +| **PixelCopy for screenshots** | Hardware-accelerated capture (API 26+); Canvas fallback for older | |
| 115 | +| **No-backup storage for drafts** | Bug report drafts persist across launches but don't sync to cloud | |
| 116 | + |
| 117 | +## Third-Party Dependencies |
| 118 | + |
| 119 | +| Library | Purpose | |
| 120 | +|---------|---------| |
| 121 | +| [Curtains](https://github.com/square/curtains) | Window attachment and activity tracking | |
| 122 | +| [Radiography](https://github.com/square/radiography) | View hierarchy inspection | |
| 123 | +| [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serialization) | JSON serialization for bug reports | |
| 124 | + |
| 125 | +*See `gradle/libs.versions.toml` for current versions.* |
0 commit comments