capacitor-community / capacitor-community/native-audio

Android loop() does not start playback and play() resets looping flag

Open
#171 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
154
Forks
78
PR merge metrics
No merged PRs in 30d

Description

# Bug Report: Android loop() doesn't start playback and play() resets looping flag

**Describe the bug**
On Android, the `NativeAudio.loop()` method sets the internal looping flag but does not actually initiate audio playback. Conversely, the `NativeAudio.play()` method initiates playback but explicitly resets the looping flag to `false`. This design makes it impossible to start a looping audio track (like BGM) with a single method call when using the Android platform.

**To Reproduce**
Steps to reproduce the behavior:
1. Preload an audio asset using `NativeAudio.preload()`.
2. Call `NativeAudio.loop({ assetId: 'my-bgm' })`.
3. Observed result: **Silence**. The audio never starts playing.
4. Alternatively, call `NativeAudio.play({ assetId: 'my-bgm' })`.
5. Observed result: The audio plays once and **stops**, even if it was intended to loop.

**Expected behavior**
- `NativeAudio.loop()` should initiate playback if the asset is not already playing.
- Alternatively, `NativeAudio.play()` should accept a `loop: boolean` option, or there should be a way to ensure the looping flag persists during playback initialization.

**Screenshots**
N/A (Audio behavior issue)

**Desktop (please complete the following information):**
N/A (Plugin specific behavior on Native Android)

**Smartphone (please complete the following information):**
- Device: Android Device/Emulator
- OS: Android (Tested on various versions)
- Plugin Version: `@capacitor-community/native-audio` ^8.0.0

**Additional context**

### Technical Analysis (Android implementation)

Analysis of the `AudioDispatcher.java` and `AudioAsset.java` source code reveals the root cause:

1. **`loop()` lack of `start()`**:
In `AudioDispatcher.java`, `loop()` only calls `mediaPlayer.setLooping(true)` without calling `mediaPlayer.start()`.
```java
public void loop() throws Exception {
mediaPlayer.setLooping(true);
}
```

2. **`play()` forces `loop = false`**:
The `play()` method hardcodes the loop parameter to `false` when calling `invokePlay`.
```java
// AudioDispatcher.java
public void play(Double time, Callable callable) throws Exception {
invokePlay(time, false); // 'false' is hardcoded here
callable.call();
}

private void invokePlay(Double time, Boolean loop) {
...
mediaPlayer.setLooping(loop); // Forces looping to false
...
}
```

### Known Workaround
Users currently have to chain calls, which might lead to inconsistent timing:
```typescript
await NativeAudio.play({ assetId: 'bgm' });
await NativeAudio.loop({ assetId: 'bgm' }); // Force looping flag while playing
```

Contributor guide

Open the contributing guide

Research direction

Start by reading the loop() and play() methods in AudioDispatcher.java, then trace how AudioAsset.java participates in playback initialization. Verify the Android behavior with a preloaded asset: loop() should start playback and looping should remain enabled rather than being reset by play().

Written by the indexing model from the issue text.

Assessment

Tech stack
android, java
Domain
audio-video-rtc, mobile
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.