Compare commits

...
Author SHA1 Message Date
Arthur Jamet 3a50f13b36 tmp 2026-08-04 12:14:12 +02:00
6 changed files with 179 additions and 132 deletions

No files matched your search

+8 -7
View File
@@ -25,6 +25,7 @@ export default (_: ConfigContext): ExpoConfig => ({
supportsTablet: true,
config: { usesNonExemptEncryption: false },
infoPlist: {
UIBackgroundModes: "audio",
UIViewControllerBasedStatusBarAppearance: true,
CFBundleShortVersionString: "$(MARKETING_VERSION)",
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)",
@@ -65,13 +66,13 @@ export default (_: ConfigContext): ExpoConfig => ({
},
},
],
[
"expo-media-control",
{
enableBackgroundAudio: true,
audioSessionCategory: "playback",
},
],
// [
// "expo-media-control",
// {
// enableBackgroundAudio: true,
// audioSessionCategory: "playback",
// },
// ],
[
"expo-asset",
{
+1 -1
View File
@@ -33,7 +33,6 @@
"expo-image": "^56.0.11",
"expo-linear-gradient": "^56.0.4",
"expo-linking": "~56.0.15",
"expo-media-control": "patch:expo-media-control@npm%3A1.0.12#~/.yarn/patches/expo-media-control-npm-1.0.12-d7bddfea85.patch",
"expo-router": "~56.2.14",
"expo-screen-orientation": "~56.0.5",
"expo-splash-screen": "~56.0.12",
@@ -56,6 +55,7 @@
"react-native-edge-to-edge": "^1.8.1",
"react-native-gesture-handler": "~2.31.2",
"react-native-keyboard-controller": "1.22.2",
"react-native-media-notification": "0.5.0-beta.6",
"react-native-mmkv": "4.3.2",
"react-native-nitro-modules": "0.36.4",
"react-native-reanimated": "~4.5.1",
@@ -1,12 +1,7 @@
import {
Command,
MediaControl,
type MediaControlEvent,
PlaybackState,
} from "expo-media-control";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import { useEffect, useRef, useState } from "react";
import { Platform } from "react-native";
import * as MediaControls from "react-native-media-notification";
import uuid from "react-native-uuid";
import {
type onLoadData,
@@ -78,7 +73,6 @@ export const PlayerContext = () => {
const { data: settings } = useQuery(getSettings);
const isSwitchingTrack = useRef(false);
const controlsRegistered = useRef(false);
const controlsListener = useRef<(() => void) | null>(null);
const ready = useRef(false);
const { download } = useDownloadManager();
@@ -93,90 +87,53 @@ export const PlayerContext = () => {
(controlsRegistered.current &&
ready.current &&
!isSwitchingTrack.current)
)
await MediaControl.updatePlaybackState(
(isPlaying ?? store.get(isPlayingAtom))
? PlaybackState.PLAYING
: PlaybackState.PAUSED,
progress ?? store.get(progressAtom),
1,
);
) {
await MediaControls.updateMetadata({
isPlaying,
position: progress,
});
}
};
const enableControls = async (enable: boolean) => {
if (enable === false) {
await MediaControl.removeAllListeners();
await MediaControl.disableMediaControls();
controlsListener.current?.();
controlsListener.current = null;
if (controlsRegistered.current === false) {
return;
}
controlsRegistered.current = false;
MediaControls.removeAllListeners();
await MediaControls.stopMediaNotification();
return;
}
if (controlsRegistered.current === false) {
await MediaControl.enableMediaControls({
capabilities: [
Command.PLAY,
Command.PAUSE,
Command.STOP,
Command.NEXT_TRACK,
Command.PREVIOUS_TRACK,
Command.SEEK,
],
compactCapabilities: [
Command.PREVIOUS_TRACK,
Command.PLAY,
Command.NEXT_TRACK,
],
notification: { showWhenClosed: true },
ios: { skipInterval: 15 },
android: { skipInterval: 15 },
// MediaControls.enableBackgroundMode(true);
// MediaControls.enableAudioInterruption(true);
MediaControls.setControlEnabled("play", true);
MediaControls.setControlEnabled("pause", true);
MediaControls.setControlEnabled("skipToNext", true);
MediaControls.setControlEnabled("skipToPrevious", true);
MediaControls.setControlEnabled("stop", true);
MediaControls.setControlEnabled("shuffle", false);
MediaControls.setControlEnabled("repeatMode", false);
MediaControls.addEventListener("play", () => store.set(playAtom));
MediaControls.addEventListener("pause", () => store.set(pauseAtom));
MediaControls.addEventListener("seek", (payload) => {
if (payload?.seekPosition !== undefined)
store.set(requestedProgressAtom, payload.seekPosition);
});
controlsRegistered.current = true;
controlsListener.current = MediaControl.addListener(
(event: MediaControlEvent) => {
const progress = store.get(progressAtom);
switch (event.command) {
case Command.PLAY:
playerRef.current?.play();
break;
case Command.PAUSE:
playerRef.current?.pause();
break;
case Command.STOP:
MediaControl.updatePlaybackState(
PlaybackState.STOPPED,
0,
1,
);
store.set(emptyPlaylistAtom);
break;
case Command.NEXT_TRACK:
store.set(skipTrackAtom, queryClient);
break;
case Command.PREVIOUS_TRACK:
store.set(rewindTrackAtom);
break;
case Command.SEEK:
store.set(
requestedProgressAtom,
event.data.position,
);
break;
case Command.SKIP_FORWARD:
store.set(
requestedProgressAtom,
progress + (event.data?.interval || 15),
);
break;
case Command.SKIP_BACKWARD:
store.set(
requestedProgressAtom,
progress - (event.data?.interval || 15),
);
break;
}
},
MediaControls.addEventListener("skipToNext", () =>
store.set(skipTrackAtom, queryClient),
);
MediaControls.addEventListener("skipToPrevious", () =>
store.set(rewindTrackAtom),
);
MediaControls.addEventListener("stop", () =>
store.set(emptyPlaylistAtom),
);
//NOTE: RepeatMode button is broken on android
controlsRegistered.current = true;
}
};
@@ -243,8 +200,6 @@ export const PlayerContext = () => {
playerRef.current.ignoreSilentSwitchMode = "ignore";
// NOTE: On ios, When the player is paused, the 'focus' is lost and the media controls are flushed (cannot press play again + no metadata)
// Setting this opt to true preserves the focus.
playerRef.current.showNotificationControls =
Platform.OS === "ios";
playerRef.current.playInBackground = true;
playerRef.current.addEventListener(
"onPlaybackStateChange",
@@ -286,30 +241,30 @@ export const PlayerContext = () => {
ready.current = false;
enableControls(true).then(async () => {
const currentTrack = store.get(currentTrackAtom)!;
await MediaControl.updatePlaybackState(
PlaybackState.PLAYING,
0,
1,
const cursor = store.get(cursorAtom);
const playlist = store.get(playlistAtom);
MediaControls.setControlEnabled(
"skipToNext",
cursor < playlist.length - 1 && cursor >= 0,
);
await MediaControl.updateMetadata({
trackNumber: store.get(cursorAtom) + 1,
albumTrackCount: store.get(playlistAtom).length,
MediaControls.setControlEnabled(
"skipToPrevious",
cursor > 0,
);
await MediaControls.updateMetadata({
title: currentTrack.track.name,
elapsedTime: 0,
position: 0,
duration: duration ?? 1,
artist: formatArtists(
currentTrack.artist,
currentTrack.featuring,
),
artwork: currentTrack.track.illustration
? {
uri: getAPI().getIllustrationURL(
currentTrack.track.illustration
.url,
"original",
true,
),
}
? getAPI().getIllustrationURL(
currentTrack.track.illustration.url,
"original",
true,
)
: undefined,
});
ready.current = true;
@@ -0,0 +1 @@
export const useMediaControls = () => {};
@@ -0,0 +1,6 @@
export type MediaControls = {
updateMediaControlsState: (
isPlaying?: boolean,
progress?: number,
) => Promise<void>;
};
+109 -25
View File
@@ -4755,7 +4755,7 @@ __metadata:
languageName: node
linkType: hard
"asap@npm:~2.0.6":
"asap@npm:~2.0.3, asap@npm:~2.0.6":
version: 2.0.6
resolution: "asap@npm:2.0.6"
checksum: 10c0/c6d5e39fe1f15e4b87677460bd66b66050cd14c772269cee6688824c1410a08ab20254bb6784f9afb75af9144a9f9a7692d49547f4d19d715aeb7c0318f3136d
@@ -5540,6 +5540,15 @@ __metadata:
languageName: node
linkType: hard
"cross-fetch@npm:^3.1.5":
version: 3.2.0
resolution: "cross-fetch@npm:3.2.0"
dependencies:
node-fetch: "npm:^2.7.0"
checksum: 10c0/d8596adf0269130098a676f6739a0922f3cc7b71cc89729925411ebe851a87026171c82ea89154c4811c9867c01c44793205a52e618ce2684650218c7fbeeb9f
languageName: node
linkType: hard
"cross-spawn@npm:^7.0.0":
version: 7.0.3
resolution: "cross-spawn@npm:7.0.3"
@@ -6283,28 +6292,6 @@ __metadata:
languageName: node
linkType: hard
"expo-media-control@npm:1.0.12":
version: 1.0.12
resolution: "expo-media-control@npm:1.0.12"
peerDependencies:
expo: "*"
react: "*"
react-native: "*"
checksum: 10c0/02acf4a8eeb1075f2ee907fed30d14fb8e8c82b7e8e5ad02227f6e9b881be652ccbd5c5179d83defd77571e2e6a1145f2ad6e0324568471c98ac194c6a8beff1
languageName: node
linkType: hard
"expo-media-control@patch:expo-media-control@npm%3A1.0.12#~/.yarn/patches/expo-media-control-npm-1.0.12-d7bddfea85.patch":
version: 1.0.12
resolution: "expo-media-control@patch:expo-media-control@npm%3A1.0.12#~/.yarn/patches/expo-media-control-npm-1.0.12-d7bddfea85.patch::version=1.0.12&hash=3733d9"
peerDependencies:
expo: "*"
react: "*"
react-native: "*"
checksum: 10c0/6eea48dda39a59bffb8c1ce1de766fdd40dc2c570cbe9d36139e81dc62d277c33c9bb09d111cd30ac7e3c8b5e490e8a98e8966bb6ec6e3512d2d6401ce3c77ad
languageName: node
linkType: hard
"expo-modules-autolinking@npm:~56.0.19":
version: 56.0.19
resolution: "expo-modules-autolinking@npm:56.0.19"
@@ -6604,6 +6591,37 @@ __metadata:
languageName: node
linkType: hard
"fbemitter@npm:^3.0.0":
version: 3.0.0
resolution: "fbemitter@npm:3.0.0"
dependencies:
fbjs: "npm:^3.0.0"
checksum: 10c0/f130dd8e15dc3fc6709a26586b7a589cd994e1d1024b624f2cc8ef1b12401536a94bb30038e68150a24f9ba18863e9a3fe87941ade2c87667bfbd17f4848d5c7
languageName: node
linkType: hard
"fbjs-css-vars@npm:^1.0.0":
version: 1.0.2
resolution: "fbjs-css-vars@npm:1.0.2"
checksum: 10c0/dfb64116b125a64abecca9e31477b5edb9a2332c5ffe74326fe36e0a72eef7fc8a49b86adf36c2c293078d79f4524f35e80f5e62546395f53fb7c9e69821f54f
languageName: node
linkType: hard
"fbjs@npm:^3.0.0":
version: 3.0.5
resolution: "fbjs@npm:3.0.5"
dependencies:
cross-fetch: "npm:^3.1.5"
fbjs-css-vars: "npm:^1.0.0"
loose-envify: "npm:^1.0.0"
object-assign: "npm:^4.1.0"
promise: "npm:^7.1.1"
setimmediate: "npm:^1.0.5"
ua-parser-js: "npm:^1.0.35"
checksum: 10c0/66d0a2fc9a774f9066e35ac2ac4bf1245931d27f3ac287c7d47e6aa1fc152b243c2109743eb8f65341e025621fb51a12038fadb9fd8fda2e3ddae04ebab06f91
languageName: node
linkType: hard
"fdir@npm:^6.5.0":
version: 6.5.0
resolution: "fdir@npm:6.5.0"
@@ -8418,7 +8436,6 @@ __metadata:
expo-image: "npm:^56.0.11"
expo-linear-gradient: "npm:^56.0.4"
expo-linking: "npm:~56.0.15"
expo-media-control: "patch:expo-media-control@npm%3A1.0.12#~/.yarn/patches/expo-media-control-npm-1.0.12-d7bddfea85.patch"
expo-router: "npm:~56.2.14"
expo-screen-orientation: "npm:~56.0.5"
expo-splash-screen: "npm:~56.0.12"
@@ -8441,6 +8458,7 @@ __metadata:
react-native-edge-to-edge: "npm:^1.8.1"
react-native-gesture-handler: "npm:~2.31.2"
react-native-keyboard-controller: "npm:1.22.2"
react-native-media-notification: "npm:0.5.0-beta.6"
react-native-mmkv: "npm:4.3.2"
react-native-nitro-modules: "npm:0.36.4"
react-native-reanimated: "npm:~4.5.1"
@@ -8657,6 +8675,20 @@ __metadata:
languageName: node
linkType: hard
"node-fetch@npm:^2.7.0":
version: 2.7.0
resolution: "node-fetch@npm:2.7.0"
dependencies:
whatwg-url: "npm:^5.0.0"
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8
languageName: node
linkType: hard
"node-forge@npm:^1.3.3":
version: 1.3.3
resolution: "node-forge@npm:1.3.3"
@@ -9091,6 +9123,15 @@ __metadata:
languageName: node
linkType: hard
"promise@npm:^7.1.1":
version: 7.3.1
resolution: "promise@npm:7.3.1"
dependencies:
asap: "npm:~2.0.3"
checksum: 10c0/742e5c0cc646af1f0746963b8776299701ad561ce2c70b49365d62c8db8ea3681b0a1bf0d4e2fe07910bf72f02d39e51e8e73dc8d7503c3501206ac908be107f
languageName: node
linkType: hard
"promise@npm:^8.3.0":
version: 8.3.0
resolution: "promise@npm:8.3.0"
@@ -9449,6 +9490,18 @@ __metadata:
languageName: node
linkType: hard
"react-native-media-notification@npm:0.5.0-beta.6":
version: 0.5.0-beta.6
resolution: "react-native-media-notification@npm:0.5.0-beta.6"
dependencies:
fbemitter: "npm:^3.0.0"
peerDependencies:
react: "*"
react-native: "*"
checksum: 10c0/0cdc03edfeeff43eb220b9ec01bf3064337b05a544be8f7d2a645cc68ecbb285441b8d20652a5033da033c400b1d76003a50ff9c9c19aaa09bc0e4bb75e3d2a3
languageName: node
linkType: hard
"react-native-mmkv@npm:4.3.2":
version: 4.3.2
resolution: "react-native-mmkv@npm:4.3.2"
@@ -10324,6 +10377,13 @@ __metadata:
languageName: node
linkType: hard
"setimmediate@npm:^1.0.5":
version: 1.0.5
resolution: "setimmediate@npm:1.0.5"
checksum: 10c0/5bae81bfdbfbd0ce992893286d49c9693c82b1bcc00dcaaf3a09c8f428fdeacf4190c013598b81875dfac2b08a572422db7df779a99332d0fce186d15a3e4d49
languageName: node
linkType: hard
"setprototypeof@npm:1.2.0":
version: 1.2.0
resolution: "setprototypeof@npm:1.2.0"
@@ -11049,6 +11109,13 @@ __metadata:
languageName: node
linkType: hard
"tr46@npm:~0.0.3":
version: 0.0.3
resolution: "tr46@npm:0.0.3"
checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11
languageName: node
linkType: hard
"ts-easing@npm:^0.2.0":
version: 0.2.0
resolution: "ts-easing@npm:0.2.0"
@@ -11136,7 +11203,7 @@ __metadata:
languageName: node
linkType: hard
"ua-parser-js@npm:^1.0.37":
"ua-parser-js@npm:^1.0.35, ua-parser-js@npm:^1.0.37":
version: 1.0.41
resolution: "ua-parser-js@npm:1.0.41"
bin:
@@ -11584,6 +11651,13 @@ __metadata:
languageName: unknown
linkType: soft
"webidl-conversions@npm:^3.0.0":
version: 3.0.1
resolution: "webidl-conversions@npm:3.0.1"
checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db
languageName: node
linkType: hard
"whatwg-fetch@npm:^3.0.0":
version: 3.6.20
resolution: "whatwg-fetch@npm:3.6.20"
@@ -11598,6 +11672,16 @@ __metadata:
languageName: node
linkType: hard
"whatwg-url@npm:^5.0.0":
version: 5.0.0
resolution: "whatwg-url@npm:5.0.0"
dependencies:
tr46: "npm:~0.0.3"
webidl-conversions: "npm:^3.0.0"
checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5
languageName: node
linkType: hard
"which@npm:^2.0.1":
version: 2.0.2
resolution: "which@npm:2.0.2"