Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a50f13b36 |
No files matched your search
@@ -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",
|
||||
{
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user