Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ runs:
- name: Install ktlint
shell: bash
run: |
curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.5.0/ktlint
curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.7.0/ktlint
chmod a+x ktlint
sudo mv ktlint /usr/local/bin/

Expand Down
99 changes: 64 additions & 35 deletions apps/common-app/src/examples/AudioFile/AudioFile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useCallback, useEffect, useState, FC } from 'react';
import { ActivityIndicator, View, StyleSheet } from 'react-native';
import { AudioManager } from 'react-native-audio-api';
import { AudioManager, PlaybackNotificationManager } from 'react-native-audio-api';
import { Container, Button, Spacer } from '../../components';
import AudioPlayer from './AudioPlayer';
import { colors } from '../../styles';
Expand All @@ -13,6 +13,7 @@ const AudioFile: FC = () => {
const [isPlaying, setIsPlaying] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [positionPercentage, setPositionPercentage] = useState(0);
const [shouldResume, setShouldResume] = useState(false);

const togglePlayPause = async () => {
if (isPlaying) {
Expand Down Expand Up @@ -41,76 +42,104 @@ const AudioFile: FC = () => {
}, []);

useEffect(() => {
AudioManager.setLockScreenInfo({
title: 'Audio file',
artist: 'Software Mansion',
album: 'Audio API',
duration: 10,
});

AudioManager.enableRemoteCommand('remotePlay', true);
AudioManager.enableRemoteCommand('remotePause', true);
AudioManager.enableRemoteCommand('remoteSkipForward', true);
AudioManager.enableRemoteCommand('remoteSkipBackward', true);
// Register notification first
const setupNotification = async () => {
try {
await PlaybackNotificationManager.register();

// Load audio buffer first
await fetchAudioBuffer();

// Show notification with correct duration after buffer is loaded
const duration = AudioPlayer.getDuration();
await PlaybackNotificationManager.show({
title: 'Audio file',
artist: 'Software Mansion',
album: 'Audio API',
duration: duration,
state: 'paused',
speed: 1.0,
elapsedTime: 0,
});
} catch (error) {
console.error('Failed to setup notification:', error);
}
};

setupNotification();

AudioManager.observeAudioInterruptions(true);
AudioManager.activelyReclaimSession(true);

const remotePlaySubscription = AudioManager.addSystemEventListener(
'remotePlay',
// Listen to notification control events
const playListener = PlaybackNotificationManager.addEventListener(
'playbackNotificationPlay',
() => {
AudioPlayer.play();
setIsPlaying(true);
}
);

const remotePauseSubscription = AudioManager.addSystemEventListener(
'remotePause',
const pauseListener = PlaybackNotificationManager.addEventListener(
'playbackNotificationPause',
() => {
AudioPlayer.pause();
setIsPlaying(false);
}
);

const remoteSkipForwardSubscription = AudioManager.addSystemEventListener(
'remoteSkipForward',
const skipForwardListener = PlaybackNotificationManager.addEventListener(
'playbackNotificationSkipForward',
(event) => {
AudioPlayer.seekBy(event.value);
}
);

const remoteSkipBackwardSubscription = AudioManager.addSystemEventListener(
'remoteSkipBackward',
const skipBackwardListener = PlaybackNotificationManager.addEventListener(
'playbackNotificationSkipBackward',
(event) => {
AudioPlayer.seekBy(-event.value);
}
);

// Keep interruption handling through AudioManager
const interruptionSubscription = AudioManager.addSystemEventListener(
'interruption',
async (event) => {
if (event.type === 'began') {
await AudioPlayer.pause();
setIsPlaying(false);
} else if (event.type === 'ended' && event.shouldResume) {
BackgroundTimer.setTimeout(async () => {
AudioManager.setAudioSessionActivity(true);
await AudioPlayer.play();
setIsPlaying(true);
}, 1000);
// Store whether we were playing before interruption
setShouldResume(isPlaying && event.isTransient);

if (isPlaying) {
await AudioPlayer.pause();
setIsPlaying(false);
}
} else if (event.type === 'ended') {

if (shouldResume) {
BackgroundTimer.setTimeout(async () => {
AudioManager.setAudioSessionActivity(true);
await AudioPlayer.play();
setIsPlaying(true);
console.log('Auto-resumed after transient interruption');
}, 500);
}

// Reset the flag
setShouldResume(false);
}
}
);

fetchAudioBuffer();

return () => {
remotePlaySubscription?.remove();
remotePauseSubscription?.remove();
remoteSkipForwardSubscription?.remove();
remoteSkipBackwardSubscription?.remove();
playListener.remove();
pauseListener.remove();
skipForwardListener.remove();
skipBackwardListener.remove();
interruptionSubscription?.remove();
AudioManager.resetLockScreenInfo();
PlaybackNotificationManager.unregister();
AudioPlayer.reset();
console.log('Cleanup AudioFile component');
};
}, [fetchAudioBuffer]);

Expand Down
16 changes: 11 additions & 5 deletions apps/common-app/src/examples/AudioFile/AudioPlayer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AudioContext, AudioManager } from 'react-native-audio-api';
import { AudioContext, PlaybackNotificationManager } from 'react-native-audio-api';
import type {
AudioBufferSourceNode,
AudioBuffer,
Expand Down Expand Up @@ -58,8 +58,9 @@ class AudioPlayer {

this.sourceNode.start(this.audioContext.currentTime, this.offset);

AudioManager.setLockScreenInfo({
state: 'state_playing',
PlaybackNotificationManager.update({
state: 'playing',
elapsedTime: this.offset,
});
};

Expand All @@ -71,8 +72,9 @@ class AudioPlayer {

this.sourceNode?.stop(this.audioContext.currentTime);

AudioManager.setLockScreenInfo({
state: 'state_paused',
PlaybackNotificationManager.update({
state: 'paused',
elapsedTime: this.offset,
});

await this.audioContext.suspend();
Expand Down Expand Up @@ -133,6 +135,10 @@ class AudioPlayer {
) => {
this.onPositionChanged = callback;
};

getDuration = (): number => {
return this.audioBuffer?.duration ?? 0;
};
}

export default new AudioPlayer();
Loading