# Hevy Android App — Integration Surfaces

## 1. Live Activity Notification (PRIMARY SURFACE)

Hevy posts a persistent notification during active workouts. This is the **most reliable and lowest-friction** integration surface.

### What the notification contains (inferred from Hevy docs + typical Android notification layout):

```
Title: "Hevy - Workout in Progress"
Content:
  • Current exercise name (e.g., "Bench Press (Barbell)")
  • Current set: "Set 3 of 4"
  • Target: "100 kg × 8 reps"  (if routine specifies targets)
  • Rest timer: "Rest 1:45" (countdown during rest)
  • Elapsed workout time

Actions (notification buttons):
  • [+15s rest] or [Skip rest]  — extends or skips rest timer
  • [✓ Complete set] — marks current set as done
  • [Finish workout] — ends the workout

Notification style: Likely MediaStyle or BigTextStyle with custom RemoteViews
Persistent: Yes (ongoing notification, non-dismissible during workout)
```

### How to read it

**NotificationListenerService** (standard Android API, API 18+):
- `onNotificationPosted(StatusBarNotification sbn)` — fires when Hevy posts/updates the notification
- `sbn.getNotification().extras` — contains title, text, and custom extras
- `sbn.getPackageName()` — filter for `"com.hevy"`
- `getActiveNotifications()` — poll current state if needed

**Tasker + AutoNotification plugin**:
- AutoNotification Intercept event → triggers on Hevy notification change
- Variables: `%antitle`, `%antext`, `%anapp`, action buttons available
- Can also click notification buttons programmatically

**Automate (LlamaLab)**:
- "Notification posted?" block with Package filter `com.hevy`
- Outputs: Title, Message, Action labels, Extras dictionary
- "Notification action" block to click notification buttons

### Limitations
- Notification content is **not a documented API** — Hevy may change layout, add/remove fields
- Rich content may be in custom `RemoteViews` not accessible via `extras`
- Rest timer countdown may not be in text — may be a custom view
- Need to **verify actual notification structure** on a real device (step 1 of prototype)

## 2. In-App UI (via Accessibility Service)

If the notification doesn't expose enough detail, an `AccessibilityService` can read the Hevy UI directly.

### What's visible in the active workout screen
- Current exercise name (TextView, likely `contentDescription` set)
- Completed sets with weights/reps (list items)
- Current set input fields (weight, reps, RPE)
- Rest timer display
- Buttons: "Finish Set", "+ Add Set", "Finish Workout", exercise navigation

### Accessibility node tree
- `AccessibilityService.getRootInActiveWindow()` → full UI tree
- `findAccessibilityNodeInfosByViewId("com.hevy:id/...")` → find by resource ID
- `findAccessibilityNodeInfosByText("Bench Press")` → find by visible text
- `node.performAction(ACTION_CLICK)` → tap buttons
- `node.performAction(ACTION_SET_TEXT)` → input weight/reps
- `dispatchGesture()` → swipe, tap at coordinates (fallback)

### Resource IDs (need discovery on real device)
- Must dump UI hierarchy: `adb shell uiautomator dump`
- Common patterns: `exercise_name`, `set_weight_input`, `set_reps_input`, `rest_timer`, `finish_set_btn`

## 3. Hevy Internal API (Reverse Engineering)

Hevy has an **internal/unofficial API** used by the mobile app that is separate from the public `api.hevyapp.com/v1/` endpoints. Several projects have documented parts of it.

### Known internal endpoints (from dmzoneill/hevyapp-api and gab-es21/hevy-workout-generator)

```
POST /login                          — email/password auth → bearer token
GET  /account                        — user profile
GET  /workout_count                  — total workout count
GET  /feed_workouts_paged            — paged workout feed (full details)
POST /routines_sync_batched          — batch routine sync
GET  /user_preferences               — user settings
GET  /body_measurements              — body measurements
GET  /set_personal_records           — PR data
GET  /user_subscription              — subscription status
```

### Key unknowns (what we really need for live bridge)

Does Hevy have an internal **live workout session endpoint**? This is the critical question. Possibilities:

1. **REST endpoint for active workout state**: `GET /active_workout` or `GET /workout_session/current`
   - If this exists and returns current exercise/sets/rest timer, this is the cleanest path.
   - We'd capture the bearer token from the Hevy app, then poll this endpoint.

2. **WebSocket for live sync**: Used by Wear OS companion to sync live state.
   - If we can discover the WebSocket protocol, bridge can connect directly.
   - Watch → phone sync likely uses this path (Bluetooth or local WebSocket).

3. **No live endpoint** — state is purely local in the app:
   - Then fall back to notification + accessibility.

### Discovery method
```
1. Install mitmproxy/cert on Pixel 9a
2. Route Hevy app traffic through proxy
3. Start a workout in Hevy
4. Observe all HTTPS requests during:
   - Workout start
   - Set completion
   - Rest timer events
   - Workout finish/save
5. Look for endpoints like /active_workout, /workout_session, /live, /sync
```

### Auth model
- Internal API uses `auth-token` header (not `api-key`)
- Token obtained from `/login` with email/password or OAuth
- Token refresh mechanism exists (`/refresh_token`)
- Bearer token format: `Bearer eyJ...` (JWT)

## 4. Wear OS Sync (Potential Side Channel)

Hevy has a Wear OS companion app that **live-syncs** with the phone app:
- Phone app → Wear OS: sends routine, exercise, set data
- Wear OS → Phone app: sends set completions, weight/reps logged

The sync protocol between phone Hevy and Wear OS Hevy is relevant because:
- It proves a **live data channel exists** (at least locally)
- If we can intercept or replicate this channel, we could read/write workout state
- The Wear OS app uses `com.hevy` package on Wear OS → same internal API

Possible approaches:
- Capture Wear OS ↔ phone sync traffic (likely Bluetooth or local network)
- Check if a companion Wear OS APK can be installed on non-Wear device
- Check Wear OS app permissions/manifest for exported services

## 5. Intent / Deep Link Surface

**Hevy does NOT appear to expose documented Android intents or deep links** for:
- Starting a specific routine: `hevy://routine/{id}` — NOT CONFIRMED
- Starting an empty workout: `hevy://workout/start` — NOT CONFIRMED
- Opening to a specific screen: `hevy://...` — NOT CONFIRMED

What we can try:
```bash
# Check registered intent filters
adb shell dumpsys package com.hevy | grep -A 20 "intent-filter"

# Check for deep link schemes
adb shell dumpsys package com.hevy | grep -i "scheme"

# Try launching activities
adb shell am start -n com.hevy/.MainActivity
adb shell am start -n com.hevy/.WorkoutActivity
adb shell am start -a android.intent.action.VIEW -d "hevy://workout"
```

The web share links (e.g., `https://hevy.com/routine/...`) open routines in the browser, not the app. There may be Android App Links configured.

## 6. Hevy ContentProvider / Database

Possibility: Hevy stores current workout state in a local SQLite database.
- Located at `/data/data/com.hevy/databases/`
- Root not needed if we can access via content provider or ADB backup
- `adb backup com.hevy` → extract `.ab` file → read database
- But: backup is offline batch, not live

## Summary: Integration Surface Ranking

| Surface | Capability | Reliability | Effort | Risk |
|---------|-----------|-------------|--------|------|
| **Notification** | Read-only state | ⭐⭐⭐ Medium | Low | Hevy changes format |
| **Accessibility UI** | Read + Write (tap) | ⭐⭐ Fragile | Medium | UI changes, policy |
| **Internal API** | Read + Write (if exists) | ⭐⭐⭐⭐ Best | High | Unofficial, may break |
| **ADB** | Read + Write (shell) | ⭐⭐ Fragile | Low | Not practical live |
| **Tasker/Automate** | Read + limited write | ⭐⭐⭐ Good | Low | Limited control |
| **Wear OS channel** | Read + Write (if replicable) | ⭐⭐ Unknown | High | Protocol unknown |
