Skip to content

fix(mobile): patch SwiftAudioEx insert index for one-item queues - #14650

Merged
dylanjeffers merged 1 commit into
chore/mobile-native-release-1.5.187from
fix/mobile-swiftaudioex-insert-index
Oct 9, 2026
Merged

dylanjeffers merged 1 commit into
chore/mobile-native-release-1.5.187from
fix/mobile-swiftaudioex-insert-index

Conversation

@dylanjeffers

Copy link
Copy Markdown
Contributor

Ships in native release 1.5.187. It targets chore/mobile-native-release-1.5.187 (#14645).

What

A post_install step in the Podfile patches one line of the SwiftAudioEx pod source, Sources/SwiftAudioEx/QueueManager.swift:

-            if (self.items.count > 1 && currentIndex >= index) {
+            if (self.items.count > 0 && currentIndex >= index) {

SwiftAudioEx 1.0.0 (our Podfile.lock) and 1.1.0 (RNTP 4.1.2's podspec) only shift the current index for an insert in front of the current item when the queue already has more than one item. With one item, inserting at 0 leaves the index one slot early, and when the track ends the player replays it instead of advancing. Upstream main has the > 0 fix, but it isn't released yet.

The step replaces only that exact line. If the file is already patched it does nothing. If the line is missing (for example after a SwiftAudioEx update), it raises and pod install fails, so the patch can't be skipped silently.

Relation to #14649

#14649 is the JS fix, which ships OTA. It avoids the bad insert by appending the later tracks first. When the tapped track is the last one in the list, it uses a temporary placeholder instead. This PR fixes the root cause natively, which covers the last-item case without the placeholder. It also covers any other single-item insert.

Testing

  • RCT_NEW_ARCH_ENABLED=0 bundle exec pod install prints the patch message, and Pods/SwiftAudioEx/Sources/SwiftAudioEx/QueueManager.swift:148 now reads items.count > 0.
  • I checked the patch logic on three versions of the file: unpatched gets patched, already patched is left alone, and a changed line raises.
  • Confirmed the same line is in SwiftAudioEx tag 1.0.0 and in 1.1.0.
  • Podfile.lock is not changed. The local install showed the known RNTP 4.0.1→4.1.2 / SwiftAudioEx 1.0.0→1.1.0 drift, which I left out.
  • I haven't rebuilt the native app with this patch. The simulator check of the last-item case was done with the JS fix (fix(mobile): advance to next track on iOS when starting mid-list #14649).

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 1cbaf22

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

dylanjeffers added a commit that referenced this pull request Oct 8, 2026
)

## Bug
On iOS, starting a track that isn't the first one in a list (feed,
profile, collection) and letting it finish doesn't advance. The player
stays on the same track instead of moving to the next one.

`resetQueue` adds the tapped track on its own, then inserts the earlier
tracks at index 0. SwiftAudioEx (RNTP's iOS player, 1.0.0 and 1.1.0)
only shifts its current index for an insert in front of the current item
when the queue already has more than one item:

```swift
if (self.items.count > 1 && currentIndex >= index) {
    currentIndex += items.count
}
```

So the first insert in front of the lone tapped track leaves
`currentIndex` one slot too early. When the track ends, "next" is the
tapped track again.

## Fix
Queue building moves into `addTracksAroundCurrent`:
- the later tracks are appended first, so the queue already has more
than one item when the earlier tracks go in
- the earlier tracks go in with a single insert at index 0
- if there are no later tracks (the tapped track is last), a placeholder
copy of the previous track is appended for the insert and removed
afterwards. Removing an item after the current one doesn't touch the
current index or playback.

The final RNTP order is still the redux order, so Android (KotlinAudio)
ends up with the same queue as before.

This is JS only and OTA-safe. The native fix (`> 0`) is in #14650 for
the 1.5.187 release.

## Testing
- New unit test `addTracksAroundCurrent.test.ts` uses a fake queue that
copies SwiftAudioEx's index logic. The old insert order fails 4 of its 6
cases and the new one passes all 6.
- `npx tsc --noEmit -p packages/mobile`, eslint and `npm test` (8
suites, 25 tests) pass.
- iOS simulator, Release build (old arch) with a production bundle:
- before the fix: starting the 2nd or 3rd feed track and letting it
finish stalls on that track at the end
  - after the fix:
- 2nd feed track → advances to the 3rd, and the now playing drawer and
the highlighted tile match
    - previous goes back to the earlier track
    - first item advances
- last item of a 2-track album: RNTP reports active index 1 with the
queue in order, and playback ends at the end of the queue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@dylanjeffers
dylanjeffers merged commit 37ec8b2 into chore/mobile-native-release-1.5.187 Oct 9, 2026
3 checks passed
@dylanjeffers
dylanjeffers deleted the fix/mobile-swiftaudioex-insert-index branch October 9, 2026 00:10
dylanjeffers added a commit that referenced this pull request Oct 9, 2026
…14653)

## Bug
On iOS, after a list plays to its end:
- the play button keeps showing pause until it's tapped
- pressing Previous and letting the track "play" to the end stalls. The
scrubber moves, but the native player is paused the whole time, so it
never sends an end event (no `PlaybackQueueEnded`, no
`PlaybackActiveTrackChanged`).

Both symptoms come from the same cause. They aren't related to #14649 or
to the SwiftAudioEx index bug in #14650: native `currentIndex` is
correct at every step.

## Root cause
When the queue ends, SwiftAudioEx stops with `playWhenReady = false` and
RNTP emits `PlaybackQueueEnded`. `AudioPlayer.tsx` subscribes to that
event but has never handled it. It was a `// TODO` before the #14220
refactor, and it was silently dropped after. So redux keeps `playing:
true`.

`TrackPlayer.play()` is only called from `handleTogglePlay`, when redux
`playing` changes. After the queue ends it is already `true`, so:
- **Previous** (position > 3s) dispatches `reset({ shouldAutoplay: true
})`, which seeks to 0 and sets `playing: true` again. That is not a
change, so `play()` is never called.
- **Previous twice / tapping a track** calls `skip(index)`. RNTP iOS
calls `jumpToItem(atIndex:, playWhenReady: playerState == .playing)`,
which is `false` here, so the track loads paused.

The same `skip` behavior also stalls without a queue end: **Next then
Previous quickly** skips while the player is still buffering, so the
track loads paused while redux says playing.

Simulator logs from an instrumented build (native state read through
RNTP after each event):
```
queue end    playback-queue-ended  state=paused pwr=false idx=1    redux playing=true   <- out of sync
Previous     redux counter++ (reset)  state=ready pwr=false pos=0    redux playing=true   <- never plays
Prev, Prev   skip(0) from paused   state=ready pwr=false idx=0                          <- never plays
Next, Prev   skip(0) from buffering  playWhenReady -> false, state=ready                <- never plays
```

## Fix
- On `PlaybackQueueEnded`, dispatch `pause({ onlySetState: true })` so
redux matches the stopped player. The next play is then a real `false ->
true` change and reaches `TrackPlayer.play()`.
- After `TrackPlayer.skip()` in `handleQueueIdxChange`, call
`TrackPlayer.play()` if redux is playing. On Android, skip already keeps
`playWhenReady`, so this does nothing there.

JS only, so it can ship OTA.

## Testing
iOS 26.5 simulator, Release build, old arch (JS thread is
`com.facebook.react.JavaScript`), production env, with this branch's
bundle. I used a 2-track album and an instrumented bundle that logs
native state on each RNTP event and auto-seeks near the end of each
track.
- Before:
- queue end leaves `pwr=false` with redux `playing=true`, and the album
and mini player show Pause
  - Previous leaves native paused at 0 while the scrubber advances
  - Previous ×2 and Next→Previous load the track paused
- After:
- queue end sets redux `playing=false`, and the album and mini player
show Play
  - Previous restarts and plays to the end, then pauses cleanly
- Next→Previous during buffering: skip still drops `playWhenReady`, then
`play()` brings it back, and playback continues and auto-advances
  - tapping a track row from the paused state plays it
- `eslint` is clean and the mobile tests pass (8 suites, 25 tests). `tsc
--noEmit -p packages/mobile` shows the same 4 errors with and without
this change, in unrelated files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
dylanjeffers added a commit that referenced this pull request Oct 9, 2026
Phase 1 of the React Native upgrade: 0.79.5 → 0.81.6 with the old
architecture still on.

**Base: `chore/mobile-native-release-1.5.188` (#14654).** Rebased onto
it after 1.5.187 (#14645, which squashed #14627, #14640 and #14650)
merged to main; the two lib-swap commits are gone from this branch.
Merge #14647 and #14651 into this branch first, then this into #14654,
then #14654 into main as one push (see #14654 for the full order).

## Rebase onto 1.5.187 (2026-10-08)
- `patches/react-native+0.79.5.patch` (the ExceptionsManager Hermes fix
from #14643) is renamed to `react-native+0.81.6.patch`. The anchors are
unchanged in 0.81.6, and `scripts/patch-package.sh` applies it with
`--error-on-fail` during `npm install` (checked: `getErrorStackSafe` is
in the installed `ExceptionsManager.js`).
- notifee and Firebase from #14640 are kept as they are:
`@notifee/react-native` 9.1.8 is the latest release and
`@react-native-firebase/app`/`messaging` 24.1.1 build on 0.81.6 on the
old arch (both declare `react-native: *`). No bump needed.
- The Firebase Android BoM stays pinned to 33.16.0. This branch moves
Kotlin to 2.1.20, and BoM 34 needs 2.2, so the pin still applies; the
comment now says so.
- The UIScene and notifee lines in `AppDelegate.mm` (#14626, #14640) are
untouched by this branch.
- `package-lock.json` and `Podfile.lock` were regenerated (`npm
install`, then `RCT_NEW_ARCH_ENABLED=0 bundle exec pod install`); the
release xcconfig has no `RCT_NEW_ARCH_ENABLED=1`. The SwiftAudioEx
post_install patch (#14650) still applies.

**Needs a native release (1.5.188).** Don't OTA this JS onto older
binaries. Old architecture stays on: Android `newArchEnabled=false`, iOS
`RCTNewArchEnabled=false`, pods installed with `RCT_NEW_ARCH_ENABLED=0`.

Two commits, one per step. Each step builds on iOS and Android:
1. 0.79.5 → 0.80.3
2. 0.80.3 → 0.81.6 (0.81.6 is the latest 0.81 patch on npm)

## Version bumps

| Package | From | To | Why |
|---|---|---|---|
| react-native | 0.79.5 | 0.81.6 | the upgrade |
| `@react-native/*` (babel-preset, eslint-config, metro-config,
metro-babel-transformer, typescript-config) | 0.79.5 | 0.81.6 | must
match RN |
| `@react-native-community/cli`, `cli-platform-android`,
`cli-platform-ios` | 18.0.0 | 20.1.3 | CLI line for 0.81; the platform
packages were left at 18 on the old ladder branch |
| react, react-dom, react-test-renderer (whole monorepo, root
`overrides` too) | 19.0.0 | **19.1.4** | RN's bundled renderer throws at
startup unless `react` is **exactly** its version: 19.1.0 for 0.80.3,
19.1.4 for 0.81.6. The old ladder branch used 19.1.8, which would throw
"Incompatible React versions" on launch. |
| @types/react / @types/react-dom | 19.0.0 | 19.1.17 / 19.1.11 | match
React 19.1 |
| react-native-gesture-handler | 2.25.0 | 2.28.0 | 2.28 is the first 2.x
with official 0.81 support (2.25's table stops at 0.79). Stays on 2.x;
v3 drops the old architecture. |
| @amplitude/analytics-react-native | 1.4.11 | 1.4.14 | Kotlin 2.1 (RN
0.80+) turns the deprecated `toUpperCase(Locale)` in 1.4.11 into a
compile error |
| tiktok-opensdk-react-native | ^0.10.7 | 0.10.7 (pinned) | it now
carries a patch. 0.10.9 fixes the Android signature, but drops the
`handleOpenURL`/`handleUserActivity` declarations that SceneDelegate
imports |

Patches added (`patches/`):
- `react-native-track-player+4.1.2.patch`: Kotlin 2.1 rejects
`Arguments.fromBundle(Bundle?)`. Now resolves `null` when there is no
item. 4.1.2 is the last 4.x and has no fixed release.
- `tiktok-opensdk-react-native+0.10.7.patch`: `onNewIntent(intent:
Intent)` (RN 0.80 made `ActivityEventListener` non-null) and
`reactApplicationContext.currentActivity`.

Checked against 0.81 on the old architecture and left alone: Reanimated
3.19.5 (the official table lists 3.19.x for 0.78–0.81 on Paper;
Reanimated 4 is out of scope), track-player 4.1.2 (patched above),
collapsible-tab-view 8.0.1 (JS only), screens 4.18.0 (0.81 support since
4.14; `react-native-screens+4.18.0.patch` still applies; 4.25+ drops the
old arch), pager-view 6.7.1 (7+ drops the old arch), video 6.18.0, svg
15.15.0 (0.81 support since 15.12.1), google-cast 4.6.2 (5.x is new-arch
only), notifications 5.1.0, code-push 12.3.2 (README lists 0.77–0.86;
13.x needs a new OTA history), bootsplash 6.3.11, safe-area-context
5.6.2, keyboard-controller 1.19.0, flash-list 1.8.3, datetimepicker
8.3.0 (builds against 0.81.6).

## Template changes (rn-diff-purge 0.79.5 → 0.81.6)
- Android: Kotlin 2.0.21 → 2.1.20, Gradle 8.13 → 8.14.3, new
`gradlew`/`gradlew.bat`/wrapper jar, `MainApplication` uses
`loadReactNative(this)` in place of `SoLoader.init` plus `load()`, and
`edgeToEdgeEnabled=false` is added as in the template. The app already
draws edge-to-edge through react-native-bars and the SDK 36 target, so
this flag changes nothing.
- AGP comes from RN: 8.8 → 8.11.0, which supports compileSdk 36, so
`android.suppressUnsupportedCompileSdk=36` is removed.
- `settings.gradle` resolves `@react-native/gradle-plugin` through
`require.resolve` from `react-native`, not a hard-coded hoisted path.
With this lockfile npm nests it under
`node_modules/react-native/node_modules`.
- Kept: `newArchEnabled=false`, the `AudiusScrypt` module
(`ScryptPackage` is still registered), the debug manifest (it adds
`SYSTEM_ALERT_WINDOW`), and
`android:enableOnBackInvokedCallback="false"`.
- **Back handling:** since 0.80, `ReactActivity` registers an
`OnBackPressedCallback` when targetSdk ≥ 36, which calls
`onBackPressed()` → JS `BackHandler`. Our `invokeDefaultOnBackPressed`
override (`moveTaskToBack`) still runs. The manifest opt-out is kept as
asked and can probably go in a later release.
- iOS: no template change between 0.79.5 and 0.81.6. `Info.plist` and
`RichPushExtension/Info.plist` now set `RCTNewArchEnabled=false`, which
RN 0.80+ reads **at runtime**; a missing key means new arch. `pod
install` writes the key and re-sorts the plist each time, so the sorted
version is committed.

## AppDelegate / UIScene
No code change needed. `RCTAppDelegate` is marked deprecated in 0.81 in
favour of `RCTReactNativeFactory`, so there is a
`-Wdeprecated-declarations` warning, but every API we use is unchanged:
`automaticallyLoadReactNativeWindow`, `rootViewFactory`,
`createRootViewController`, `setRootView:toRootViewController:`,
`customizeRootView:`, `bundleURL`, and `dependencyProvider`.
`didFinishLaunching` still creates the factory and skips the window.
Moving to `RCTReactNativeFactory` can wait for the 0.82+ phase.

Verified on a Release build: cold launch, a deep link while killed, and
a deep link while backgrounded, on iOS 26.5 and iOS 27. iOS 27 needs
UIScene and showed no launch crash.

## Web / React 19.1
- Typecheck: web, common and harmony are clean, and so is mobile.
- `vite build` (prod env): succeeds.
- web vitest: 178 passed, 9 skipped. `TrackTile › Public Premium
(non-owner)` timed out once while the machine was under heavy load, then
passed 3 out of 3 when run alone.

## Verification matrix (before the rebase; see the bottom for the
rebased stack)
| | iOS 26.5 sim (Release) | iOS 27 sim (Release) | Android API 36 emu
(prodRelease, arm64) |
|---|---|---|---|
| Build | ✅ Xcode 27, `generic/platform=iOS Simulator` | same binary | ✅
debug keystore |
| Old arch at runtime | ✅ JS thread `com.facebook.react.JavaScript` | –
| ✅ `ReactRootView` + "Legacy Architecture" warning |
| Launch past splash | ✅ | ✅ | ✅ `assets://index.android.bundle` |
| Sign-up / sign-in screens | (signed in) | ✅ | ✅ including Create
Password |
| Signed-in feed, scroll | ✅ | – | – |
| Playback + now playing drawer (open, drag closed) | ✅ | – | – |
| Background → foreground while playing | ✅ audio kept going (0:09 →
0:41) | – | – |
| Search | ✅ | – | – |
| Profile: collapsible header + tab swipe | ✅ | – | – |
| Deep link, warm | ✅ `audius://audius` | ✅ link delivered | – |
| Deep link, cold | ✅ `audius://deadmau5` | ✅ (signed out → sign-on) | –
|
| Hardware Back | – | – | ✅ pops Create Password; at root backgrounds
the app without killing it |
| Sign-on footer above nav bar | – | – | ✅ |
| Red JS errors | none, apart from a recurring `JSON Parse error:
Unexpected character: <` (not yet traced) | none | none |

The CodePush folder was moved aside or cleared before every cold launch,
so all results above are from the bundled JS.

## Still needs a real device
- background audio with the screen locked for more than 2 minutes
- lock-screen and notification controls
- Chromecast
- push notifications

## Notes
- A Release build that downloads a production OTA built for 1.5.186 or
1.5.187 runs a 0.79 bundle on a 0.81 binary. 1.5.188 ships with its own
package.json version (#14654) so it never receives those OTAs.
- The Hermes `ExceptionsManager` fix now applies through patch-package
(#14643), renamed for 0.81.6 above.

## Verification of the rebased stack (2026-10-08)
Built from a local, unpushed integration branch: #14652's tip (which
contains #14647) merged with #14651, on top of #14654.

| Check | Result |
|---|---|
| `npx tsc --noEmit -p packages/mobile` | pass |
| eslint on the touched files / `turbo run verify` lint | pass (1
existing warning in `HostRemixContestDrawer.tsx`) |
| `cd packages/mobile && npm test` | 9 suites, 34 tests pass |
| iOS Release, `generic/platform=iOS Simulator`, arm64,
`ENVFILE=.env.prod` | builds; Info.plist 1.1.201,
`RCTNewArchEnabled=false`; bundle carries 1.5.188 |
| Android `assembleProdRelease`, arm64-v8a, debug keystore | builds;
versionName 1.1.537 |
| iOS 26.5 sim, installed over the signed-in app, CodePush moved aside |
launch to feed; JS thread `com.facebook.react.JavaScript` (old arch) |
| Playback from the 2nd feed track | plays; play bar tracker moves |
| Auto-advance | seek to 2:43/2:45 on track 2, advanced to track 3 |
| Now playing drawer | opens, scrubber advances, tap-to-seek works |
| Profile tabs | swipe Tracks → Albums, tap Reposts |
| notifee/RNFB APNs token | a temporary console log (not committed,
rebuilt without it afterwards) printed an 80-byte lowercase APNs token
at startup |
| Android 16 emulator, `pm clear` | `Loading JS bundle from
"assets://index.android.bundle"`, "Legacy Architecture" warning, sign-up
and sign-in screens render |
| Android hardware Back | **not verified**: the emulator's system_server
was killed by its watchdog three times under host load, so Back never
got a clean run |
| JS errors on iOS | only `Could not cache profile images` (content node
timeout) |

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
dylanjeffers added a commit that referenced this pull request Oct 9, 2026
## Summary

Version bump for native release **1.5.188**. Merging it starts the RC
and production native builds.

- `packages/mobile/package.json`: 1.5.187 → 1.5.188 (and the matching
`package-lock.json` entry)
- iOS `CFBundleShortVersionString`: 1.1.200 → 1.1.201
- Android `versionName`: 1.1.536 → 1.1.537

1.5.188 contains (all still on the old architecture):
- #14642: React Native 0.79.5 → 0.81.6, React 19.1.4 monorepo-wide
- #14647: RNTP 4.1.2 patch for bridgeless events and `Unit` module
methods (no change on old arch)
- #14651: playback progress and eq bars on the native driver
- #14652: react-native-svg 15.15.1 and the Lottie `shouldBeRecycled NO`
patch (no change on old arch)

1.5.189 is then only the new-architecture flag flip.

## Stack

```
main
└── chore/mobile-native-release-1.5.188   (this PR)
    └── chore/mobile-rn-0.81              #14642
        ├── fix/mobile-rntp-bridgeless    #14647
        │   └── fix/mobile-new-arch-prereqs  #14652
        └── fix/mobile-native-driver-progress  #14651
```

## Verification of the rebased stack (2026-10-08)
Built from a local, unpushed integration branch: #14652's tip (which
contains #14647) merged with #14651, on top of #14654.

| Check | Result |
|---|---|
| `npx tsc --noEmit -p packages/mobile` | pass |
| eslint on the touched files / `turbo run verify` lint | pass (1
existing warning in `HostRemixContestDrawer.tsx`) |
| `cd packages/mobile && npm test` | 9 suites, 34 tests pass |
| iOS Release, `generic/platform=iOS Simulator`, arm64,
`ENVFILE=.env.prod` | builds; Info.plist 1.1.201,
`RCTNewArchEnabled=false`; bundle carries 1.5.188 |
| Android `assembleProdRelease`, arm64-v8a, debug keystore | builds;
versionName 1.1.537 |
| iOS 26.5 sim, installed over the signed-in app, CodePush moved aside |
launch to feed; JS thread `com.facebook.react.JavaScript` (old arch) |
| Playback from the 2nd feed track | plays; play bar tracker moves |
| Auto-advance | seek to 2:43/2:45 on track 2, advanced to track 3 |
| Now playing drawer | opens, scrubber advances, tap-to-seek works |
| Profile tabs | swipe Tracks → Albums, tap Reposts |
| notifee/RNFB APNs token | a temporary console log (not committed,
rebuilt without it afterwards) printed an 80-byte lowercase APNs token
at startup |
| Android 16 emulator, `pm clear` | `Loading JS bundle from
"assets://index.android.bundle"`, "Legacy Architecture" warning, sign-up
and sign-in screens render |
| Android hardware Back | **not verified**: the emulator's system_server
was killed by its watchdog three times under host load, so Back never
got a clean run |
| JS errors on iOS | only `Could not cache profile images` (content node
timeout) |

## Merge order

1. **Wait until 1.5.187 is live in both stores.** Until then, any native
change on main would go into a 1.5.187 rebuild.
2. **Dispatch a production OTA from main before merging anything
below**, for every store version that can run main's JS:
   ```
gh workflow run mobile.yml --repo AudiusProject/apps --ref main -f
ota_channel=production
   ```
With main at 1.5.187 this publishes to `production/1.5.187`. **Do not**
dispatch `-f binary_version=1.5.186` or `1.5.185` from main any more:
main now carries #14627, which calls
`TurboModuleRegistry.getEnforcing('ReactNativeFs')` at import, so that
JS crashes 1.5.186 and older binaries. If 1.5.186 users still need a
fix, dispatch from a commit before 5c4ee7e.
3. **Land everything on main in one push.** Merge the stack into this
branch from the top down: #14652 into #14647, #14647 and #14651 into
#14642, #14642 into this branch. Then merge this PR into main. Any push
to main that leaves the version unchanged publishes an RC OTA to the
current binary's history, and RN 0.81 JS on a 0.79 binary will not
start. With one push that also changes the version, the version check
skips the OTA and starts the native builds. After that, OTAs route to
`1.5.188` histories.
4. **Device-test the RC builds before submitting:**
- cold launch on both platforms, old arch at runtime (iOS JS thread
`com.facebook.react.JavaScript`; Android "Legacy Architecture" warning)
   - background audio with the screen locked for more than 2 minutes
   - lock-screen and notification controls, Bluetooth, Chromecast
- auto-advance, including starting from the 2nd track of a list
(#14649/#14650)
- play bar and scrubber position: seek, drag, pause/resume, background
then foreground
   - first play does not pause itself (Lottie)
- push: token registered after the 1.5.187 → 1.5.188 update and taps
open the right screen (#14640)
   - Android hardware Back on a pushed screen and at the root
   - gradients and offline downloads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant