feat: continue music when app is closed
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
@@ -9,6 +10,7 @@
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<queries>
|
||||
<intent>
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
],
|
||||
"bundleIdentifier": "com.omedis.musicland.ai",
|
||||
"infoPlist": {
|
||||
"UIBackgroundModes": [
|
||||
"audio"
|
||||
],
|
||||
"NSMicrophoneUsageDescription": "MusicLand uses the microphone to record your voice and audio so you can create and collaborate on music projects.",
|
||||
"UISupportedInterfaceOrientations": [
|
||||
"UIInterfaceOrientationPortrait",
|
||||
@@ -73,7 +76,9 @@
|
||||
"permissions": [
|
||||
"android.permission.CAMERA",
|
||||
"android.permission.MODIFY_AUDIO_SETTINGS",
|
||||
"android.permission.POST_NOTIFICATIONS"
|
||||
"android.permission.POST_NOTIFICATIONS",
|
||||
"android.permission.WAKE_LOCK",
|
||||
"android.permission.FOREGROUND_SERVICE"
|
||||
]
|
||||
},
|
||||
"web": {
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
"expo-linear-gradient": "~14.0.2",
|
||||
"expo-linking": "~7.0.5",
|
||||
"expo-location": "~18.0.10",
|
||||
"expo-minuit-shake-report": "^0.1.1",
|
||||
"expo-notifications": "~0.29.14",
|
||||
"expo-sharing": "~13.0.1",
|
||||
"expo-speech": "~13.0.1",
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { useAudioPlayer, useAudioPlayerStatus } from "expo-audio";
|
||||
import {
|
||||
setAudioModeAsync,
|
||||
useAudioPlayer,
|
||||
useAudioPlayerStatus,
|
||||
} from "expo-audio";
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
@@ -63,8 +67,8 @@ const HIDDEN_ROUTE_NAMES = new Set([
|
||||
Routes.Register,
|
||||
]);
|
||||
|
||||
const noopAsync = async () => {};
|
||||
const noop = () => {};
|
||||
const noopAsync = async () => { };
|
||||
const noop = () => { };
|
||||
|
||||
const DEFAULT_CONTEXT = {
|
||||
currentTrack: null,
|
||||
@@ -274,36 +278,36 @@ const PlayerProvider = ({ children }) => {
|
||||
(items = [], options = {}) => {
|
||||
const normalized = Array.isArray(items)
|
||||
? items
|
||||
.map((item) => {
|
||||
if (!item) return null;
|
||||
if (typeof item === "object") {
|
||||
const { metadata, context, ...rest } = item;
|
||||
const safeMetadata =
|
||||
metadata && typeof metadata === "object"
|
||||
? { ...metadata }
|
||||
: undefined;
|
||||
if (safeMetadata) {
|
||||
delete safeMetadata.queue;
|
||||
}
|
||||
const safeContext =
|
||||
context && typeof context === "object"
|
||||
? { ...context }
|
||||
: undefined;
|
||||
if (safeContext) {
|
||||
delete safeContext.queue;
|
||||
}
|
||||
return normalizeTrack(
|
||||
{
|
||||
...rest,
|
||||
...(safeMetadata ? { metadata: safeMetadata } : {}),
|
||||
...(safeContext ? { context: safeContext } : {}),
|
||||
},
|
||||
{}
|
||||
);
|
||||
.map((item) => {
|
||||
if (!item) return null;
|
||||
if (typeof item === "object") {
|
||||
const { metadata, context, ...rest } = item;
|
||||
const safeMetadata =
|
||||
metadata && typeof metadata === "object"
|
||||
? { ...metadata }
|
||||
: undefined;
|
||||
if (safeMetadata) {
|
||||
delete safeMetadata.queue;
|
||||
}
|
||||
return normalizeTrack(item, {});
|
||||
})
|
||||
.filter((track) => !!track?.source)
|
||||
const safeContext =
|
||||
context && typeof context === "object"
|
||||
? { ...context }
|
||||
: undefined;
|
||||
if (safeContext) {
|
||||
delete safeContext.queue;
|
||||
}
|
||||
return normalizeTrack(
|
||||
{
|
||||
...rest,
|
||||
...(safeMetadata ? { metadata: safeMetadata } : {}),
|
||||
...(safeContext ? { context: safeContext } : {}),
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
return normalizeTrack(item, {});
|
||||
})
|
||||
.filter((track) => !!track?.source)
|
||||
: [];
|
||||
|
||||
queueRef.current = normalized;
|
||||
@@ -374,6 +378,18 @@ const PlayerProvider = ({ children }) => {
|
||||
return Platform.OS === "web" ? ms : ms / 1000;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setAudioModeAsync({
|
||||
playsInSilentMode: true,
|
||||
shouldPlayInBackground: true,
|
||||
interruptionMode: "doNotMix",
|
||||
allowsRecording: false,
|
||||
shouldRouteThroughEarpiece: false,
|
||||
}).catch((err) =>
|
||||
console.warn("PlayerProvider: impossible de configurer le mode audio", err)
|
||||
);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!status) return;
|
||||
|
||||
@@ -450,7 +466,7 @@ const PlayerProvider = ({ children }) => {
|
||||
if (!player) return;
|
||||
try {
|
||||
player.loop = !!isLooping;
|
||||
} catch (_err) {}
|
||||
} catch (_err) { }
|
||||
}, [player, isLooping]);
|
||||
|
||||
const seekDebounceRef = useRef(null);
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from "react";
|
||||
import { ShakeReportProvider } from "expo-minuit-shake-report";
|
||||
import SharedProviders from "./SharedProviders";
|
||||
|
||||
export default ({ children }) => {
|
||||
return (
|
||||
<ShakeReportProvider projectID="musicland" defaultEmail="user@email.com">
|
||||
<SharedProviders>{children}</SharedProviders>
|
||||
</ShakeReportProvider>
|
||||
// <ShakeReportProvider projectID="musicland" defaultEmail="user@email.com">
|
||||
<SharedProviders>{children}</SharedProviders>
|
||||
// </ShakeReportProvider>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user