# Bridge Approaches — Detailed Comparison

## Tier 1: NotificationListenerService (READ-ONLY STATE)

### How it works
1. APK declares `NotificationListenerService` in manifest
2. User grants "Notification access" in Android Settings
3. Service receives `onNotificationPosted()` whenever Hevy updates its notification
4. Parse notification extras to extract: exercise name, set number, weight/reps target, rest timer
5. POST structured state to Pipo backend
6. Poll on a timer (every 1s) to track rest timer countdown if not in notification text

### Pros
- ✅ No root needed
- ✅ Standard Android API, stable since API 18
- ✅ Low battery impact (event-driven + light polling)
- ✅ Google Play policy: "notification management" is accepted use case
- ✅ Hevy notification is persistent during workout — won't be missed
- ✅ Can also click notification action buttons (e.g., skip rest timer)

### Cons
- ❌ Read-only for workout data (can only click notification buttons, not log sets)
- ❌ Notification content format is undocumented and may change
- ❌ Rest timer countdown may not be in text — may need custom polling
- ❌ Cannot start a workout in Hevy — only observe an active one

### Implementation sketch
```kotlin
class HevyNotificationListener : NotificationListenerService() {
    override fun onNotificationPosted(sbn: StatusBarNotification) {
        if (sbn.packageName != "com.hevy") return
        val extras = sbn.notification.extras
        val title = extras.getString(Notification.EXTRA_TITLE) ?: ""
        val text = extras.getString(Notification.EXTRA_TEXT) ?: ""
        // text example: "Bench Press (Barbell)\nSet 3 of 4\n100 kg × 8 reps\nRest 1:45"

        val state = parseHevyState(title, text)
        postToBackend(state)
    }

    // Also poll getActiveNotifications() every second for rest timer
}
```

### Verdict
**Best first step.** Low effort, low risk, yields live workout state. The read-only limitation is acceptable because the Fitbit app is the **control surface** — the watch is where Chicho logs sets. Hevy on the phone is just the "source of routine" and "sink for completed workout."

## Tier 2: AccessibilityService (READ + WRITE)

### How it works
1. APK declares `AccessibilityService` in manifest targeting `com.hevy`
2. User grants Accessibility permission in Settings
3. Service receives `onAccessibilityEvent()` when Hevy's UI changes
4. Walk the `AccessibilityNodeInfo` tree to read all visible fields
5. Use `performAction(ACTION_CLICK)` to tap Hevy buttons
6. Use `ACTION_SET_TEXT` to fill weight/reps fields
7. Use `dispatchGesture()` for swipe/tap at coordinates as fallback

### Pros
- ✅ Can read **all** UI details (not just notification summary)
- ✅ Can **control** Hevy: start workout, log sets, adjust values, finish workout
- ✅ Can access by resource ID for stability (if Hevy uses stable IDs)
- ✅ Works even if notification format changes
- ✅ Can take screenshots for debugging

### Cons
- ❌ Google Play policy: "AccessibilityService should only be used to assist users with disabilities." Risk of app rejection if published on Play Store. **Mitigated if sideloaded/not published on Play.**
- ❌ Fragile: UI changes (layout, resource IDs, text) break the automation
- ❌ Timing-dependent: must wait for UI to settle after actions
- ❌ More battery than notification listener (tree walking)
- ❌ User sees UI automation happening on screen (can be weird)
- ❌ Some actions may not work via accessibility (custom views, WebViews)

### Implementation sketch
```kotlin
class HevyAccessibilityService : AccessibilityService() {
    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        if (event.packageName != "com.hevy") return
        if (event.eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) return

        val root = rootInActiveWindow ?: return
        // Find current exercise name
        val exerciseNodes = root.findAccessibilityNodeInfosByViewId("com.hevy:id/exercise_name")
        val exerciseName = exerciseNodes.firstOrNull()?.text?.toString()

        // Find weight/rep inputs
        val weightInput = root.findAccessibilityNodeInfosByViewId("com.hevy:id/weight_input")
        val repsInput = root.findAccessibilityNodeInfosByViewId("com.hevy:id/reps_input")

        // Find finish set button
        val finishBtn = root.findAccessibilityNodeInfosByViewId("com.hevy:id/finish_set")

        // Execute command (from backend)
        when (pendingCommand) {
            "log_set" -> {
                weightInput.firstOrNull()?.performAction(ACTION_SET_TEXT, ...)
                finishBtn.firstOrNull()?.performAction(ACTION_CLICK)
            }
        }
    }
}
```

### Verdict
**Valuable fallback for control.** If we need to programmatically log sets in Hevy (e.g., to keep Hevy in sync as backup), Accessibility is the way. But it's fragile and should be secondary to notification-based reading.

## Tier 3: Tasker + AutoNotification (NO-CODE / LOW-CODE)

### How it works
1. Install Tasker + AutoNotification (full version)
2. Create Tasker profile: AutoNotification Intercept → package `com.hevy`
3. Parse `%antitle` and `%antext` variables
4. Use AutoNotification Actions to click notification buttons
5. Use Tasker HTTP Request action to POST to Pipo backend
6. Use Tasker Media Control or AutoInput (accessibility plugin) for deeper control

### Pros
- ✅ No custom APK development needed
- ✅ Visual, no-code configuration
- ✅ Fast to prototype
- ✅ AutoInput plugin adds accessibility-like control
- ✅ Active community, lots of examples

### Cons
- ❌ Requires paid apps (Tasker $3.49, AutoNotification $2.50)
- ❌ Tasker task logic is limited compared to real code
- ❌ Complex state management is hard in flow-based system
- ❌ Debugging is harder than with real logging
- ❌ Variable names are cryptic (`%antitle`, `%antext`, `%anbutton1action`)
- ❌ Not suitable for production-grade reliability

### Verdict
**Excellent for rapid prototyping.** Use Tasker + AutoNotification to quickly validate that Hevy notifications contain useful data. Once the data format is confirmed, migrate to a custom APK for reliability.

## Tier 4: Automate (LlamaLab) (ALTERNATIVE TO TASKER)

### How it works
Similar to Tasker but with flowcharts instead of profile/task model.

### Pros
- ✅ Free (with limits)
- ✅ Visual flowchart model — easier to understand than Tasker
- ✅ 400+ building blocks
- ✅ "Notification posted?" block with Package filter
- ✅ "Interact" block for accessibility-based UI automation
- ✅ "HTTP request" block
- ✅ Can execute shell commands (if ADB available)

### Cons
- ❌ Less community/plugin ecosystem than Tasker
- ❌ Still limited vs. real code
- ❌ Same Tasker-like variable management headaches

### Verdict
**Alternative to Tasker.** Slightly different paradigm (flowcharts vs profiles). Either works for prototyping.

## Tier 5: ADB-Based Automation (LAB / PROTOTYPE ONLY)

### How it works
1. Enable USB debugging on Pixel 9a
2. Connect to Linux machine (or run ADB over TCP/IP locally)
3. Dump UI: `adb shell uiautomator dump`
4. Parse XML, find coordinates
5. Tap: `adb shell input tap X Y`
6. Read screen state from XML dump

### Pros
- ✅ No app installation on phone
- ✅ Full control: any tap, swipe, key event
- ✅ UI dump gives exact coordinates and text
- ✅ Great for exploration and debugging

### Cons
- ❌ USB connection impractical during gym workout
- ❌ WiFi ADB requires same network, adds latency
- ❌ UI dump is slow (~500ms-1s) — not real-time
- ❌ Not suitable for production use
- ❌ ADB over WiFi drops on network changes

### Verdict
**Lab/debugging tool only.** Use ADB to explore Hevy's UI hierarchy, discover resource IDs, and test coordinate-based taps. Not for live bridge.

## Tier 6: Hevy Internal API Client (STRETCH GOAL)

### How it works
1. Capture Hevy app network traffic via mitmproxy
2. Discover internal live-workout endpoints (if they exist)
3. Extract auth token from Hevy app storage or capture login
4. Bridge APK polls internal API for live state
5. Bridge APK calls internal API to log sets

### Pros
- ✅ Cleanest integration if it works — direct data access
- ✅ No UI dependency, no notification parsing
- ✅ Fast, reliable, structured JSON
- ✅ Works even if Hevy is in background

### Cons
- ❌ Requires Hevy login credentials in bridge APK
- ❌ Unofficial API — may change or be blocked
- ❌ May violate Hevy ToS
- ❌ Token management: refresh, expiration, revocation
- ❌ High effort to discover and maintain
- ❌ The live workout endpoint may not exist at all

### Discovery approach
```bash
# 1. Set up mitmproxy on your Linux machine
mitmweb --mode regular --listen-port 8080

# 2. On Pixel, set proxy to your machine's IP:8080
# Settings > WiFi > network > Advanced > Proxy

# 3. Install mitmproxy CA cert on Pixel
# Browse to mitm.it on Pixel, download cert, install

# 4. Start Hevy, log in, start a workout

# 5. In mitmweb, filter for api.hevyapp.com
# Look for requests during:
#   - Workout start
#   - Each set logged
#   - Rest timer updates
#   - Workout finish
```

### Verdict
**High-reward, high-risk.** Worth the discovery effort (a few hours of mitmproxy). If a live endpoint exists, this becomes the preferred approach. If not, no time wasted — we have notification + accessibility as reliable fallbacks.

## Comparison Matrix

| Approach | Read State | Write Control | Dev Effort | Reliability | Production Ready | Google Play Safe |
|----------|-----------|---------------|------------|-------------|-----------------|-----------------|
| NotificationListener | ✅ Partial | ❌ (buttons only) | Low | Medium | ✅ | ✅ |
| AccessibilityService | ✅ Full | ✅ Full | Medium | Low-Medium | ⚠️ Fragile | ❌ (sideload OK) |
| Tasker + AutoNotification | ✅ Partial | ❌ (buttons only) | Very Low | Medium | ⚠️ Not robust | ✅ |
| Automate | ✅ Partial | ❌ (buttons only) | Very Low | Medium | ⚠️ Not robust | ✅ |
| ADB | ✅ Full | ✅ Full | Low | Very Low | ❌ | ✅ (dev only) |
| Internal API | ✅ Full | ✅ Full | High | High | ✅ (if exists) | ⚠️ ToS risk |

## Recommended Strategy: Layered

```
Layer 1 (Primary): NotificationListenerService
  → Read Hevy live workout state
  → Post to Pipo backend
  → Click notification buttons for rest timer control

Layer 2 (Fallback): AccessibilityService
  → If notification content is insufficient
  → Read full UI state
  → Execute set-logging commands (if needed)

Layer 3 (Stretch): Internal API Client
  → Discover live endpoints via mitmproxy
  → If found, replace Layer 1+2 with direct API calls
  → Keeps Hevy fully in sync automatically

Prototype: Tasker + AutoNotification
  → Quick validation that the data flow works
  → Test notification content richness
  → Before committing to custom APK development
```
