Skip to content
Draft
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
47 changes: 46 additions & 1 deletion apps/common-app/src/examples/Record/Record.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
RecorderAdapterNode,
AudioBufferSourceNode,
AudioBuffer,
UiMode,
} from 'react-native-audio-api';

import { Container, Button } from '../../components';
Expand All @@ -20,6 +21,8 @@ const Record: FC = () => {
const recorderAdapterRef = useRef<RecorderAdapterNode | null>(null);
const audioBuffersRef = useRef<AudioBuffer[]>([]);
const sourcesRef = useRef<AudioBufferSourceNode[]>([]);
const isRecordingRef = useRef<boolean>(false);
const isPausedRef = useRef<boolean>(false);

useEffect(() => {
const setup = async () => {
Expand Down Expand Up @@ -49,13 +52,52 @@ const Record: FC = () => {
iosMode: 'spokenAudio',
iosOptions: ['defaultToSpeaker', 'allowBluetoothA2DP'],
});

AudioManager.setUiMode(UiMode.RECORDING);

const pauseResumeSubscription =
AudioManager.addRecordingNotificationButtonListener(
'pause_resume',
(buttonId: string) => {
console.log('Pause/Resume button clicked:', buttonId);
// dummy
}
);

const stopSubscription =
AudioManager.addRecordingNotificationButtonListener(
'stop',
(buttonId: string) => {
console.log('Stop button clicked:', buttonId);
stopRecorder();
}
);

AudioManager.setRecordingLockScreenInfo({
title: 'Recording Audio',
description: 'Recording in progress...',
notificationColor: '#FF6B35',
});

setupRecording.subscriptions = [pauseResumeSubscription, stopSubscription];
};

const stopRecorder = () => {
if (recorderRef.current) {
recorderRef.current.stop();
console.log('Recording stopped');
// advised, but not required
isRecordingRef.current = false;
isPausedRef.current = false;

const subscriptions = setupRecording.subscriptions || [];
subscriptions.forEach((subscriptionId: string) => {
AudioManager.removeRecordingNotificationButtonListener(subscriptionId);
});

AudioManager.resetRecordingLockScreenInfo();

AudioManager.setUiMode(UiMode.PLAYBACK);

AudioManager.setAudioSessionOptions({
iosCategory: 'playback',
iosMode: 'default',
Expand All @@ -78,6 +120,7 @@ const Record: FC = () => {
recorderRef.current.connect(recorderAdapterRef.current);

recorderRef.current.start();
isRecordingRef.current = true;
console.log('Recording started');
console.log('Audio context state:', aCtxRef.current.state);
if (aCtxRef.current.state === 'suspended') {
Expand All @@ -91,6 +134,7 @@ const Record: FC = () => {
stopRecorder();
aCtxRef.current = null;
recorderAdapterRef.current = null;
isRecordingRef.current = false;
};

const startRecordReplay = () => {
Expand All @@ -109,6 +153,7 @@ const Record: FC = () => {
});

recorderRef.current.start();
isRecordingRef.current = true;

setTimeout(() => {
stopRecorder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

<application
android:name=".MainApplication"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ class AudioAPIModule(
MediaSessionManager.resetLockScreenInfo()
}

override fun setRecordingLockScreenInfo(info: ReadableMap?) {
MediaSessionManager.setRecordingLockScreenInfo(info)
}

override fun resetRecordingLockScreenInfo() {
MediaSessionManager.resetRecordingLockScreenInfo()
}

override fun setUiMode(mode: String?) {
MediaSessionManager.setUiMode(mode!!)
}

override fun enableRemoteCommand(
name: String?,
enabled: Boolean,
Expand Down Expand Up @@ -153,4 +165,13 @@ class AudioAPIModule(
override fun getDevicesInfo(promise: Promise) {
promise.resolve(MediaSessionManager.getDevicesInfo())
}

override fun addRecordingNotificationButtonListener(
buttonId: String?,
callback: com.facebook.react.bridge.Callback?,
): String = MediaSessionManager.addRecordingNotificationButtonListener(buttonId!!, callback!!)

override fun removeRecordingNotificationButtonListener(subscriptionId: String?) {
MediaSessionManager.removeRecordingNotificationButtonListener(subscriptionId!!)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,37 @@ import java.util.HashMap
class AudioFocusListener(
private val audioManager: WeakReference<AudioManager>,
private val audioAPIModule: WeakReference<AudioAPIModule>,
private val lockScreenManager: WeakReference<LockScreenManager>,
) : AudioManager.OnAudioFocusChangeListener {
private var playOnAudioFocus: Boolean = false
private var focusRequest: AudioFocusRequest? = null

override fun onAudioFocusChange(focusChange: Int) {
Log.d("AudioFocusListener", "onAudioFocusChange: $focusChange")
when (focusChange) {
AudioManager.AUDIOFOCUS_LOSS -> {
playOnAudioFocus = false
val body =
HashMap<String, Any>().apply {
put("type", "began")
put("shouldResume", false)
}
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
}

AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
playOnAudioFocus = lockScreenManager.get()?.isPlaying == true
val body =
HashMap<String, Any>().apply {
put("type", "began")
put("shouldResume", playOnAudioFocus)
put("shouldResume", false)
}
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
}
AudioManager.AUDIOFOCUS_GAIN -> {
if (playOnAudioFocus) {
val body =
HashMap<String, Any>().apply {
put("type", "ended")
put("shouldResume", true)
}
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
} else {
val body =
HashMap<String, Any>().apply {
put("type", "ended")
put("shouldResume", false)
}
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
}

playOnAudioFocus = false
AudioManager.AUDIOFOCUS_GAIN -> {
val body =
HashMap<String, Any>().apply {
put("type", "ended")
put("shouldResume", true)
}
audioAPIModule.get()?.invokeHandlerWithEventNameAndEventBody("interruption", body)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ class LockScreenManager(
private var playbackState: Int = PlaybackStateCompat.STATE_PAUSED

init {
pb.setActions(controls)
nb.setPriority(NotificationCompat.PRIORITY_HIGH)
nb.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
this.pb.setActions(controls)

this.nb.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
this.nb.setPriority(NotificationCompat.PRIORITY_HIGH)

updateNotificationMediaStyle()

mediaNotificationManager.get()?.updateActions(controls)
}

Expand Down Expand Up @@ -172,31 +174,8 @@ class LockScreenManager(
if (artworkThread != null && artworkThread!!.isAlive) artworkThread!!.interrupt()
artworkThread = null

title = null
artist = null
album = null
description = null
duration = 0L
speed = 1.0F
elapsedTime = 0L
artwork = null
playbackState = PlaybackStateCompat.STATE_PAUSED
isPlaying = false

val emptyMetadata = MediaMetadataCompat.Builder().build()
mediaSession.get()?.setMetadata(emptyMetadata)

pb.setState(PlaybackStateCompat.STATE_NONE, 0, 0f)
pb.setActions(controls)
state = pb.build()
mediaSession.get()?.setPlaybackState(state)
mediaNotificationManager.get()?.cancelNotification()
mediaSession.get()?.setActive(false)

nb.setContentTitle("")
nb.setContentText("")
nb.setContentInfo("")

mediaNotificationManager.get()?.updateNotification(nb, isPlaying)
}

fun enableRemoteCommand(
Expand Down
Loading