# Path C — Hevy Notification / Foreground Service Bridge — Live Findings

Date: 2026-05-27 00:09 ART (during Chicho's active D2 Upper workout)

## Executive Verdict

**Hevy exposes a rich foreground-service notification during active workouts with rest timer, exercise state, and actionable buttons. A Pixel-side bridge is FULLY viable for Path C. The notification can be intercepted, parsed, and controlled from outside Hevy.**

---

## Live Capture (during D2 Upper W3, rest timer at 4:34)

### Foreground Service Notification

```
Package: com.hevy
ID: 860663589
Channel: FOREGROUND_SERVICE_NOTIFICATION_CHANNEL_ID
Channel name: "Rest Timer"
Channel importance: HIGH (4)
Flags: ONGOING_EVENT | NO_CLEAR | FOREGROUND_SERVICE | LOCAL_ONLY | SILENT
Category: workout
Bypass DND: true
```

### Notification Content

The notification uses **custom RemoteViews** (`contentView=com.hevy/0x7f0d0337`, `bigContentView=com.hevy/0x7f0d0338`) — the text is NOT in `android.title`/`android.text` extras like standard notifications. Instead it's rendered through custom layouts.

**What's visible in the user screenshot:**
- "No exercise selected" (current exercise/state)
- "Rest 4:34" (timer countdown)
- Progress bar (rest timer visual)

### Action Buttons

Three broadcast intents available:

| Button | Intent type | Target |
|--------|------------|--------|
| "Skip" | `broadcastIntent` | `com.hevy` |
| "-15s" | `broadcastIntent` | `com.hevy` |
| "+15s" | `broadcastIntent` | `com.hevy` |

The `contentIntent` (tapping the notification) launches `com.hevy.MainActivity`.

### Aggregated Stats (all-time)

```
key='com.hevy'
numEnqueuedByApp = 3,281
numPostedByApp = 25
numUpdatedByApp = 3,300
numForegroundService = 3,259
numOngoing = 3,303
numWithActions = 3,257
numWithBigPicture = 0
importance distribution: 3,325 at level 5 (HIGH)
```

---

## Bridge Architecture (Path C viable)

### Option 1: NotificationListenerService (recommended)

A custom Android app with `NotificationListenerService` permission:

```kotlin
class HevyNotificationListener : NotificationListenerService() {
    override fun onNotificationPosted(sbn: StatusBarNotification?) {
        if (sbn?.packageName != "com.hevy") return
        val notification = sbn.notification
        
        // Extract visible text from RemoteViews
        // Parse exercise name, rest timer
        
        // POST to Pipo backend
        fetch("https://miopenclaw-vnic.tail9799d2.ts.net/api/hevy/live", ...)
    }
    
    override fun onNotificationRemoved(sbn: StatusBarNotification?) {
        // Workout ended — finalize
    }
}
```

### Option 2: AccessibilityService

Reads Hevy's main UI tree for richer data (exercise name, sets, reps, weights). Works when Hevy is in foreground.

### Option 3: Broadcast Control

Send the same broadcast intents from an external app to control the rest timer:

```kotlin
// Skip rest
val skipIntent = Intent().apply {
    setClassName("com.hevy", "com.hevy.SomeReceiver") // need to reverse
    action = "com.hevy.ACTION_SKIP_REST" // need to find exact action
}
sendBroadcast(skipIntent)
```

### Option 4: Tasker + AutoNotification

Ultra-fast MVP: AutoNotification intercepts Hevy notification, Tasker parses it, sends HTTP to Pipo, Pipo relays to Fitbit.

---

## Limitations

1. **Custom RemoteViews**: Text ("No exercise selected", "Rest 4:34") is in custom layouts, NOT in standard `extras`. A NotificationListenerService can access the RemoteViews and extract text, or we can parse via AccessibilityService on the notification shade.

2. **No standard live activity API**: Unlike iOS Live Activities or Wear OS complications, Hevy doesn't expose a public API for live workout state — it's all internal to the app's notification mechanism.

3. **Exercise metadata not in notification extras**: Exercise name, set number, weight, reps, RIR are in the app UI, not in the notification. The notification only shows rest timer state.

---

## Path C vs Path A decision

### Path C wins on:
- **Speed to MVP**: AutoNotification + Tasker can be configured in minutes
- **No Fitbit app needed**: Works with existing Hevy app
- **Notification bridge can drive Fitbit companion**: Pipo backend receives Hevy state, Fitbit companion fetches it

### Path A wins on:
- **Richer workout display on wrist**: Custom UI for exercises, sets, reps, RIR
- **No dependency on Hevy app being open**: Fitbit app is standalone

### Recommendation: **HYBRID**

1. **Path C first** (fast): AutoNotification → Pipo → Fitbit companion showing current exercise and rest timer.
2. **Path A second** (rich): Full Fitbit app with exercise details, set logging, RIR selectors.
3. **Path B third** (moonshot): If Hevy internal live API is discoverable, add direct control.

---

## Actionable Commands (to test broadcast control)

These need the exact broadcast intent action strings, which can be extracted via:

```bash
adb shell dumpsys package com.hevy | grep -A5 'Receiver\|broadcast\|intent-filter'
```

Or by decompiling the APK resources to find the notification layout and corresponding receiver classes.

## Key Files

- `/tmp/hevy-apk/extract/index.android.bundle` — 22MB React Native bundle with function names
- `/home/ubuntu/.hermes/image_cache/img_420f3ea65cff.png` — Hevy notification screenshot
- `/tmp/hevy-capture-now/` — earlier capture data
