capacitor-community / capacitor-community/native-audio
Android loop() does not start playback and play() resets looping flag
- 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
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