Rocket.Chat.ReactNative/app/lib/methods/audioPlayer.ts

147 lines
3.6 KiB
TypeScript
Raw Normal View History

feat: new audio player (#5160) * feat: media auto-download view * media auto download view completed and saving the settings in mmkv * audio download preference * audio auto download when the user who sent the audio is the same logged on mobile * creation of isAutoDownloadEnabled, evaluate hist hook, Image Full Size preload done * minor tweak audio show play button after download * refactor audioFile to handleMediaDownload and fixed the audio download * desestructured params to download too * image download and autoDownload, algo fix the formatAttachmentUrl to show the image from local * add the possibility to cancel image download and clear local images * refactor blur component * video download and auto download, also keeped the behavior to download unsuportted videos to the gallery * add the possibility to start downloading a video, then exit the room, back again to room and cancel the video previously downloading * remove the custom hook for autoDownload * remove blurcomponent, fix the blur style in image.tsx, minor tweak video function name * send messageId to video * introducing the reducer to keep the downloads in progress * create a media download selector * remove all the redux stuff and do the same as file upload * video download behavior * done for image and audio * fix the try catch download media * clean up * image container uiKit * fix lint * change rn-fetch-blob to expo-filesystem * add pt-br * pass the correct message id when there is an attachment on reply * refactor some changes requested * fix audio and move the netInfo from autoDownloadPreference to redux * variable isAutoDownloadEnable name and handleMediaDownload getExtension * message/Image refactored, change the component to show the image from FastImage to Image * refactor handleMediaDownload and deleteMedia * minor tweak * refactor audio * refactor video * fix the type on the messagesView(the view of files) * minor tweak * fix the name of searchMediaFIleAsync's result * minor tweak, add the default behavior, add the OFF as label * minor tweaks * verify if the media auto download exists on settings view * fix media auto download view layout and minor tweak wifi * avoid auto download from reply * minor tweak at comment * tweak list.section * change the name to netInfoState and Local_document_directory * remove mediaType and refactor audio and image * separate blurview * thumbnail video and video behavior * add Audio to i18n and minor tweak * set the blur as always dark and add the possibility to overlay * don't need to controle the filepath in the view * fix the loading in image and video at begin * save the file with a similar filename as expected * removed the necessity of messageId or id * minor tweak * switch useLayoutEffect to useEffect * avoid onpress do some edge case because of cached at video * minor tweak * tweak at audio comment extension * minor tweak type userpreferences * remove test id from mediaAutoDownloadView * change action's name to SET_NET_INFO_STATE * caching and deleting video's thumbnails * remove generate thumbnail * minor tweak in image * update camera-roll and save the file from local url * remove local_cache_directory and deleteThumbnail * update blur to fix error on android * first commit * fix togglePlayPause * separate audio to a folder inside components and minor tweak attachment * created the slider with text * play/pause button, currentTime equal the sound, can change the slider and reflect to the sound * play/pause, track is working and onEnd * update the icons with play-shaped-filled, pause-shape-filled, loading * start the tweaks on layout * can play multiple audios, pausing the previous to execute the new one * loading animated * added the audio rate * layout fixed * removed the sound manipulation from Slider to manipulate only in the index * fix time margin horizontal * fix play 2 audios and play/pause properly * change the way we treat the audio * remove audio copy * minor tweak * fix rate state * remove the PAUSE_AUDIO * fix unloadAll, add hit slop to slider, show the duration on the first render * refactor colors to be the same as figmas name * change the class' name and add the method pauseCurrentAudio * pause audio when unmount a RoomView and unloadAll when focusing at RoomsListView * pause audio when entering a thread * fix where call the pauseCurrentAudio * moved the player from messageAudio to audioPlayer * refactor audio component * remove loading * update snapshot * fix colors name * pauseAudio when roomview is blur * moved audio from message/component/audio to message/Audio * add navigation focus to AudioPlayer component and fix the jest * add the { androidImplementation: 'MediaPlayer' } * fix action sheet swipe 02-room * fix action sheet swipe 05-threads * tweak touchable * remove react.memo from playbutton * hitSlop * speed playback from array * textinputprops * tweak at names * minor tweak at onEnd * minor tweak at names * update styles * thumb seek size * change marginBottom * add the clamp, adjust the thumb position, remove the necessity of OnEndGestureHandler * change the utils to constants * change to audioState * fix the seek for android * TDownloadState * speed array * pause audio from messagesView when open the files * update test * minor tweak * change the time after ony one click, fixes the thumb to move sync with the click * Fix seek * minor tweak Sound to Audio.Sound * name of Icon * enable PlaybackSpeed only when playing the audio * playbackSpeed to mmkv * mock implementation * create native button * minor tweak * minor tweaks * playbackSpeed after loadAudio * avoid show the error when try to setRate without audio * add messageID to differ audios inside a quote/forward from original one * unloadRoomAudios instead of unloadAllAudios inside the roomsListView * minor tweak --------- Co-authored-by: Diego Mello <diegolmello@gmail.com> Co-authored-by: Gleidson Daniel Silva <gleidson10daniel@hotmail.com>
2023-11-20 14:13:15 +00:00
import { AVPlaybackStatus, Audio, InterruptionModeAndroid, InterruptionModeIOS } from 'expo-av';
const AUDIO_MODE = {
allowsRecordingIOS: false,
playsInSilentModeIOS: true,
staysActiveInBackground: true,
shouldDuckAndroid: true,
playThroughEarpieceAndroid: false,
interruptionModeIOS: InterruptionModeIOS.DoNotMix,
interruptionModeAndroid: InterruptionModeAndroid.DoNotMix
};
class AudioPlayer {
private audioQueue: { [audioKey: string]: Audio.Sound };
private audioPlaying: string;
constructor() {
this.audioQueue = {};
this.audioPlaying = '';
}
async loadAudio({ msgId, rid, uri }: { rid: string; msgId?: string; uri: string }): Promise<string> {
const audioKey = `${msgId}-${rid}-${uri}`;
if (this.audioQueue[audioKey]) {
return audioKey;
}
const { sound } = await Audio.Sound.createAsync({ uri }, { androidImplementation: 'MediaPlayer' });
this.audioQueue[audioKey] = sound;
return audioKey;
}
onPlaybackStatusUpdate(audioKey: string, status: AVPlaybackStatus, callback: (status: AVPlaybackStatus) => void) {
if (status) {
callback(status);
this.onEnd(audioKey, status);
}
}
async onEnd(audioKey: string, status: AVPlaybackStatus) {
if (status.isLoaded) {
if (status.didJustFinish) {
try {
await this.audioQueue[audioKey]?.stopAsync();
this.audioPlaying = '';
} catch {
// do nothing
}
}
}
}
setOnPlaybackStatusUpdate(audioKey: string, callback: (status: AVPlaybackStatus) => void): void {
return this.audioQueue[audioKey]?.setOnPlaybackStatusUpdate(status =>
this.onPlaybackStatusUpdate(audioKey, status, callback)
);
}
async playAudio(audioKey: string) {
if (this.audioPlaying) {
await this.pauseAudio(this.audioPlaying);
}
await Audio.setAudioModeAsync(AUDIO_MODE);
await this.audioQueue[audioKey]?.playAsync();
this.audioPlaying = audioKey;
}
async pauseAudio(audioKey: string) {
await this.audioQueue[audioKey]?.pauseAsync();
this.audioPlaying = '';
}
async pauseCurrentAudio() {
if (this.audioPlaying) {
await this.pauseAudio(this.audioPlaying);
}
}
async setPositionAsync(audioKey: string, time: number) {
try {
await this.audioQueue[audioKey]?.setPositionAsync(time);
} catch {
// Do nothing
}
}
async setRateAsync(audioKey: string, value = 1.0) {
try {
await this.audioQueue[audioKey].setRateAsync(value, true);
} catch {
// Do nothing
}
}
async unloadAudio(audioKey: string) {
await this.audioQueue[audioKey]?.stopAsync();
await this.audioQueue[audioKey]?.unloadAsync();
delete this.audioQueue[audioKey];
this.audioPlaying = '';
}
async unloadCurrentAudio() {
if (this.audioPlaying) {
await this.unloadAudio(this.audioPlaying);
}
}
async unloadRoomAudios(rid?: string) {
if (!rid) {
return;
}
const regExp = new RegExp(rid);
const roomAudioKeysLoaded = Object.keys(this.audioQueue).filter(audioKey => regExp.test(audioKey));
const roomAudiosLoaded = roomAudioKeysLoaded.map(key => this.audioQueue[key]);
try {
await Promise.all(
roomAudiosLoaded.map(async audio => {
await audio?.stopAsync();
await audio?.unloadAsync();
})
);
} catch {
// Do nothing
}
roomAudioKeysLoaded.forEach(key => delete this.audioQueue[key]);
this.audioPlaying = '';
}
async unloadAllAudios() {
const audiosLoaded = Object.values(this.audioQueue);
try {
await Promise.all(
audiosLoaded.map(async audio => {
await audio?.stopAsync();
await audio?.unloadAsync();
})
);
} catch {
// Do nothing
}
this.audioPlaying = '';
this.audioQueue = {};
}
}
const audioPlayer = new AudioPlayer();
export default audioPlayer;