feat: android prototype (#3376)
* fix(mobile): mock webview module for Android * fix(mobile): disable new architecture for track player workaround * chore(mobile): add @react-native-menu/menu dependency * fix(mobile): handle undefined index in PagerList onIndexChange * fix(mobile): add error handling for stream fetch response * fix(mobile): update aspect ratio handling for video components * fix(mobile): mock Galeria component * fixup! fix(mobile): update aspect ratio handling for video components * chore(ci): add GitHub Actions workflow for building Android app * fix(mobile): improve CocoaPods pre-install script to handle missing CocoaPods * fix(mobile): move splash screen image configuration to Android-specific settings * fix(mobile): image in android * fix: patch react-native-track-player patch * fix: build profile
This commit is contained in:
parent
e2cc31d4ba
commit
25d253ff33
|
|
@ -0,0 +1,78 @@
|
|||
name: Build Android
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [dev]
|
||||
paths:
|
||||
- "apps/mobile/web-app/**"
|
||||
- "apps/mobile/native/**"
|
||||
- "apps/mobile/package.json"
|
||||
- "apps/mobile/app.config.ts"
|
||||
pull_request:
|
||||
branches: [dev]
|
||||
paths:
|
||||
- "apps/mobile/web-app/**"
|
||||
- "apps/mobile/native/**"
|
||||
- "apps/mobile/package.json"
|
||||
- "apps/mobile/app.config.ts"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
profile:
|
||||
type: choice
|
||||
default: preview
|
||||
options:
|
||||
- preview
|
||||
- production
|
||||
description: "Build profile"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Android apk for device
|
||||
if: github.secret_source != 'None'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 📦 Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 📦 Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: 🏗 Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: "17"
|
||||
distribution: "zulu"
|
||||
|
||||
- name: Setup Android SDK
|
||||
uses: android-actions/setup-android@v3
|
||||
|
||||
- name: 📱 Setup EAS
|
||||
uses: expo/expo-github-action@v8
|
||||
with:
|
||||
eas-version: latest
|
||||
token: ${{ secrets.EXPO_TOKEN }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: 🔨 Build Android app
|
||||
working-directory: apps/mobile
|
||||
run: eas build --platform android --profile ${{ github.event.inputs.profile || 'preview' }} --local --output=${{ github.workspace }}/build.apk
|
||||
|
||||
- name: 📤 Upload apk Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: app-android
|
||||
path: ${{ github.workspace }}/build.apk
|
||||
retention-days: 90
|
||||
|
|
@ -78,6 +78,8 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
|
|||
googleServicesFile: "./build/GoogleService-Info.plist",
|
||||
},
|
||||
android: {
|
||||
// Workaround for https://github.com/doublesymmetry/react-native-track-player/issues/2293
|
||||
newArchEnabled: false,
|
||||
package: "is.follow",
|
||||
adaptiveIcon: {
|
||||
foregroundImage: adaptiveIconPath,
|
||||
|
|
@ -106,6 +108,10 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
|
|||
dark: {
|
||||
backgroundColor: "#000000",
|
||||
},
|
||||
android: {
|
||||
image: iconPath,
|
||||
imageWidth: 200,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"db:generate": "drizzle-kit generate",
|
||||
"dev": "npm run start",
|
||||
"eas-build-post-install": "rm -rf $TMPDIR/metro-cache",
|
||||
"eas-build-pre-install": "pod repo update",
|
||||
"eas-build-pre-install": "command -v pod >/dev/null 2>&1 && pod repo update || echo 'CocoaPods not found, skipping pod repo update'",
|
||||
"ios": "expo run:ios",
|
||||
"ios:device": "expo run:ios --device",
|
||||
"start": "expo start --dev-client",
|
||||
|
|
@ -38,6 +38,7 @@
|
|||
"@react-native-firebase/app": "21.12.3",
|
||||
"@react-native-firebase/app-check": "21.12.3",
|
||||
"@react-native-firebase/crashlytics": "21.12.3",
|
||||
"@react-native-menu/menu": "1.2.3",
|
||||
"@react-native-picker/picker": "2.11.0",
|
||||
"@shopify/flash-list": "1.7.3",
|
||||
"@tanstack/query-sync-storage-persister": "5.70.0",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
import { useEffect } from "react"
|
||||
|
||||
import { prepareEntryRenderWebView } from "./index"
|
||||
|
||||
export const usePrepareEntryRenderWebView = () => {
|
||||
useEffect(() => {
|
||||
prepareEntryRenderWebView()
|
||||
}, [])
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
export const SharedWebViewModule = {
|
||||
load: () => {
|
||||
console.warn("SharedWebViewModule.load is not implemented on Android")
|
||||
},
|
||||
evaluateJavaScript: () => {
|
||||
console.warn("SharedWebViewModule.evaluateJavaScript is not implemented on Android")
|
||||
},
|
||||
}
|
||||
|
||||
export const prepareEntryRenderWebView = () => {}
|
||||
|
|
@ -1,9 +1,43 @@
|
|||
import { useMemo } from "react"
|
||||
|
||||
import { GaleriaContext } from "./context"
|
||||
import type { Galeria as GaleriaInterface } from "./index.ios"
|
||||
import type { GaleriaViewProps } from "./types"
|
||||
|
||||
const noop = () => {}
|
||||
|
||||
const Galeria: typeof GaleriaInterface = Object.assign(
|
||||
{},
|
||||
function Galeria({
|
||||
children,
|
||||
urls,
|
||||
theme = "dark",
|
||||
ids,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
} & Partial<Pick<GaleriaContext, "theme" | "ids" | "urls">>) {
|
||||
return (
|
||||
<GaleriaContext.Provider
|
||||
value={useMemo(
|
||||
() => ({
|
||||
urls,
|
||||
theme,
|
||||
initialIndex: 0,
|
||||
open: false,
|
||||
src: "",
|
||||
setOpen: noop,
|
||||
ids,
|
||||
}),
|
||||
[urls, theme, ids],
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</GaleriaContext.Provider>
|
||||
)
|
||||
},
|
||||
{
|
||||
Image: () => null,
|
||||
Image(props: GaleriaViewProps) {
|
||||
return props.children
|
||||
},
|
||||
},
|
||||
) as unknown as typeof GaleriaInterface
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,10 @@ export function EntryItemSkeleton() {
|
|||
return (
|
||||
<View className="m-1 overflow-hidden rounded-md">
|
||||
{/* Video thumbnail */}
|
||||
<View className="bg-system-fill aspect-video h-32 w-full animate-pulse rounded-md" />
|
||||
<View
|
||||
className="bg-system-fill h-32 w-full animate-pulse rounded-md"
|
||||
style={{ aspectRatio: 16 / 9 }}
|
||||
/>
|
||||
|
||||
{/* Description and footer */}
|
||||
<View className="my-2 px-2">
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export function EntryPictureItem({ id }: { id: string }) {
|
|||
|
||||
if (!hasMedia) {
|
||||
return (
|
||||
<View className="aspect-video w-full items-center justify-center">
|
||||
<View className="w-full items-center justify-center" style={{ aspectRatio: 16 / 9 }}>
|
||||
<Text className="text-label text-center">No media available</Text>
|
||||
</View>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export function PagerList({
|
|||
const { page, pagerRef, ...rest } = usePagerView({
|
||||
initialPage: viewIdIndex,
|
||||
onIndexChange: (index) => {
|
||||
if (index === undefined) return
|
||||
selectTimeline({ type: "view", viewId: activeViews[index]!.view }, false)
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import { useEffect } from "react"
|
||||
|
||||
import { prepareEntryRenderWebView } from "@/src/components/native/webview"
|
||||
import { usePrepareEntryRenderWebView } from "@/src/components/native/webview/hooks"
|
||||
import { Home5CuteFiIcon } from "@/src/icons/home_5_cute_fi"
|
||||
import { Home5CuteReIcon } from "@/src/icons/home_5_cute_re"
|
||||
import type { TabScreenComponent } from "@/src/lib/navigation/bottom-tab/types"
|
||||
import { EntryList } from "@/src/modules/entry-list"
|
||||
|
||||
export const IndexTabScreen: TabScreenComponent = () => {
|
||||
useEffect(() => {
|
||||
prepareEntryRenderWebView()
|
||||
}, [])
|
||||
usePrepareEntryRenderWebView()
|
||||
|
||||
return <EntryList />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -484,6 +484,10 @@ class EntrySyncServices {
|
|||
ids: nextIds,
|
||||
}),
|
||||
})
|
||||
if (!response.ok) {
|
||||
console.error("Failed to fetch stream:", response.statusText, await response.text())
|
||||
return
|
||||
}
|
||||
|
||||
const reader = response.body?.getReader()
|
||||
if (!reader) return
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ index 7741994191921ed86f7577f7ce589b3939d9dd49..776c09ec0ae450daa4dc9031ccd3ff4b
|
|||
@@ -151,17 +151,4 @@ @interface RCT_EXTERN_REMAP_MODULE(TrackPlayerModule, RNTrackPlayer, NSObject)
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject);
|
||||
|
||||
|
||||
-RCT_EXTERN_METHOD(getSleepTimerProgress:(RCTPromiseResolveBlock)resolve
|
||||
- rejecter:(RCTPromiseRejectBlock)reject);
|
||||
-
|
||||
|
|
@ -20,3 +20,412 @@ index 7741994191921ed86f7577f7ce589b3939d9dd49..776c09ec0ae450daa4dc9031ccd3ff4b
|
|||
- rejecter:(RCTPromiseRejectBlock)reject);
|
||||
-
|
||||
@end
|
||||
diff --git a/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicModule.kt b/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicModule.kt
|
||||
index b2409a09939164c49c0f7a16bb6d3284e8eab8fb..099caae67c52d27e451a0a5e7b11ca3645dfcd9d 100644
|
||||
--- a/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicModule.kt
|
||||
+++ b/android/src/main/java/com/doublesymmetry/trackplayer/module/MusicModule.kt
|
||||
@@ -251,8 +251,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun updateOptions(data: ReadableMap?, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun updateOptions(data: ReadableMap?, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
val options = Arguments.toBundle(data)
|
||||
|
||||
@@ -264,14 +264,14 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun add(data: ReadableArray?, insertBeforeIndex: Int, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun add(data: ReadableArray?, insertBeforeIndex: Int, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
try {
|
||||
val tracks = readableArrayToTrackList(data);
|
||||
if (insertBeforeIndex < -1 || insertBeforeIndex > musicService.tracks.size) {
|
||||
callback.reject("index_out_of_bounds", "The track index is out of bounds")
|
||||
- return@launch
|
||||
+ return@launchInScope
|
||||
}
|
||||
val index = if (insertBeforeIndex == -1) musicService.tracks.size else insertBeforeIndex
|
||||
musicService.add(
|
||||
@@ -285,11 +285,11 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun load(data: ReadableMap?, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun load(data: ReadableMap?, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
if (data == null) {
|
||||
callback.resolve(null)
|
||||
- return@launch
|
||||
+ return@launchInScope
|
||||
}
|
||||
val bundle = Arguments.toBundle(data);
|
||||
if (bundle is Bundle) {
|
||||
@@ -301,15 +301,15 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun move(fromIndex: Int, toIndex: Int, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun move(fromIndex: Int, toIndex: Int, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
musicService.move(fromIndex, toIndex)
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun remove(data: ReadableArray?, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun remove(data: ReadableArray?, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
val inputIndexes = Arguments.toList(data)
|
||||
if (inputIndexes != null) {
|
||||
val size = musicService.tracks.size
|
||||
@@ -321,7 +321,7 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
"index_out_of_bounds",
|
||||
"One or more indexes was out of bounds"
|
||||
)
|
||||
- return@launch
|
||||
+ return@launchInScope
|
||||
}
|
||||
indexes.add(index)
|
||||
}
|
||||
@@ -332,8 +332,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
|
||||
@ReactMethod
|
||||
fun updateMetadataForTrack(index: Int, map: ReadableMap?, callback: Promise) =
|
||||
- scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
if (index < 0 || index >= musicService.tracks.size) {
|
||||
callback.reject("index_out_of_bounds", "The index is out of bounds")
|
||||
@@ -348,8 +348,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun updateNowPlayingMetadata(map: ReadableMap?, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun updateNowPlayingMetadata(map: ReadableMap?, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
if (musicService.tracks.isEmpty())
|
||||
callback.reject("no_current_item", "There is no current item in the player")
|
||||
@@ -364,8 +364,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun clearNowPlayingMetadata(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun clearNowPlayingMetadata(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
if (musicService.tracks.isEmpty())
|
||||
callback.reject("no_current_item", "There is no current item in the player")
|
||||
@@ -375,16 +375,16 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun removeUpcomingTracks(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun removeUpcomingTracks(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.removeUpcomingTracks()
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun skip(index: Int, initialTime: Float, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun skip(index: Int, initialTime: Float, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.skip(index)
|
||||
|
||||
@@ -396,8 +396,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun skipToNext(initialTime: Float, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun skipToNext(initialTime: Float, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.skipToNext()
|
||||
|
||||
@@ -409,8 +409,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun skipToPrevious(initialTime: Float, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun skipToPrevious(initialTime: Float, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.skipToPrevious()
|
||||
|
||||
@@ -422,8 +422,8 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun reset(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun reset(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.stop()
|
||||
delay(300) // Allow playback to stop
|
||||
@@ -433,116 +433,116 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun play(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun play(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.play()
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun pause(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun pause(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.pause()
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun stop(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun stop(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.stop()
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun seekTo(seconds: Float, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun seekTo(seconds: Float, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.seekTo(seconds)
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun seekBy(offset: Float, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun seekBy(offset: Float, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.seekBy(offset)
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun retry(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun retry(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.retry()
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun setVolume(volume: Float, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun setVolume(volume: Float, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.setVolume(volume)
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getVolume(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getVolume(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(musicService.getVolume())
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun setRate(rate: Float, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun setRate(rate: Float, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.setRate(rate)
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getRate(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getRate(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(musicService.getRate())
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun setRepeatMode(mode: Int, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun setRepeatMode(mode: Int, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.setRepeatMode(RepeatMode.fromOrdinal(mode))
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getRepeatMode(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getRepeatMode(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(musicService.getRepeatMode().ordinal)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun setPlayWhenReady(playWhenReady: Boolean, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun setPlayWhenReady(playWhenReady: Boolean, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
musicService.playWhenReady = playWhenReady
|
||||
callback.resolve(null)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getPlayWhenReady(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getPlayWhenReady(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(musicService.playWhenReady)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getTrack(index: Int, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getTrack(index: Int, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
if (index >= 0 && index < musicService.tracks.size) {
|
||||
callback.resolve(Arguments.fromBundle(musicService.tracks[index].originalItem))
|
||||
@@ -552,15 +552,15 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getQueue(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getQueue(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(Arguments.fromList(musicService.tracks.map { it.originalItem }))
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun setQueue(data: ReadableArray?, callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun setQueue(data: ReadableArray?, callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
try {
|
||||
musicService.clear()
|
||||
@@ -572,16 +572,16 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getActiveTrackIndex(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getActiveTrackIndex(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
callback.resolve(
|
||||
if (musicService.tracks.isEmpty()) null else musicService.getCurrentTrackIndex()
|
||||
)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getActiveTrack(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getActiveTrack(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
callback.resolve(
|
||||
if (musicService.tracks.isEmpty()) null
|
||||
else Arguments.fromBundle(
|
||||
@@ -591,29 +591,29 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getDuration(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getDuration(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(musicService.getDurationInSeconds())
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getBufferedPosition(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getBufferedPosition(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(musicService.getBufferedPositionInSeconds())
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getPosition(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getPosition(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
|
||||
callback.resolve(musicService.getPositionInSeconds())
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getProgress(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getProgress(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
var bundle = Bundle()
|
||||
bundle.putDouble("duration", musicService.getDurationInSeconds());
|
||||
bundle.putDouble("position", musicService.getPositionInSeconds());
|
||||
@@ -622,8 +622,16 @@ class MusicModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaM
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
- fun getPlaybackState(callback: Promise) = scope.launch {
|
||||
- if (verifyServiceBoundOrReject(callback)) return@launch
|
||||
+ fun getPlaybackState(callback: Promise) = launchInScope {
|
||||
+ if (verifyServiceBoundOrReject(callback)) return@launchInScope
|
||||
callback.resolve(Arguments.fromBundle(musicService.getPlayerStateBundle(musicService.state)))
|
||||
}
|
||||
+
|
||||
+ // Bridgeless interop layer tries to pass the `Job` from `scope.launch` to the JS side
|
||||
+ // which causes an exception. We can work around this using a wrapper.
|
||||
+ private fun launchInScope(block: suspend () -> Unit) {
|
||||
+ scope.launch {
|
||||
+ block()
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ patchedDependencies:
|
|||
hash: 8a10ea43dd4d194a581d3d765a1eb3e864441b968d5d5729382a9986f710ef32
|
||||
path: patches/react-native-sheet-transitions.patch
|
||||
react-native-track-player:
|
||||
hash: debcf90daeaf7d38c744ecb11cded73b5bfc671b8d0937865ec21307414761b3
|
||||
hash: 24a9527477cf307020abf337d3f5db78cdc015636e7ab47d085a1eb5792a5ff0
|
||||
path: patches/react-native-track-player.patch
|
||||
workbox-precaching:
|
||||
hash: 51e57c78af317e292b43fca4b72f57b247b7b1a1faa8b03c8881ddb798c7c52f
|
||||
|
|
@ -815,6 +815,9 @@ importers:
|
|||
'@react-native-firebase/crashlytics':
|
||||
specifier: 21.12.3
|
||||
version: 21.12.3(d1616443082d3617f967a48301823ad4)
|
||||
'@react-native-menu/menu':
|
||||
specifier: 1.2.3
|
||||
version: 1.2.3(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
'@react-native-picker/picker':
|
||||
specifier: 2.11.0
|
||||
version: 2.11.0(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
|
|
@ -1027,7 +1030,7 @@ importers:
|
|||
version: 15.11.2(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
react-native-track-player:
|
||||
specifier: 4.1.1
|
||||
version: 4.1.1(patch_hash=debcf90daeaf7d38c744ecb11cded73b5bfc671b8d0937865ec21307414761b3)(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
version: 4.1.1(patch_hash=24a9527477cf307020abf337d3f5db78cdc015636e7ab47d085a1eb5792a5ff0)(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
react-native-uikit-colors:
|
||||
specifier: 0.3.10
|
||||
version: 0.3.10(0d6f50eaea763e7ce5838e09437a33ba)
|
||||
|
|
@ -1057,7 +1060,7 @@ importers:
|
|||
version: 3.1.1(react@18.3.1)
|
||||
zeego:
|
||||
specifier: 3.0.6
|
||||
version: 3.0.6(10862d462bdf72b2f4c999770e7a8205)
|
||||
version: 3.0.6(9cf8a8a5ccce31468c34b8be336f174a)
|
||||
zod:
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2
|
||||
|
|
@ -5257,8 +5260,8 @@ packages:
|
|||
expo:
|
||||
optional: true
|
||||
|
||||
'@react-native-menu/menu@1.2.2':
|
||||
resolution: {integrity: sha512-Uk65PAhwNkCVBAqJu5t2H9biV+m0JLwJc7m3v2X2A/W8SFJmUqYabBsLH4fOWKI3a7kkR9QDT6HruliIKSfM8w==}
|
||||
'@react-native-menu/menu@1.2.3':
|
||||
resolution: {integrity: sha512-sEfiVIivsa0lSelFm9Wbm/RAi+XoEHc75GGhjwvSrj9KSCVvNNXwr9F8l42e1t/lzYvVYzmkYxLG6VKxrDYJiw==}
|
||||
peerDependencies:
|
||||
react: 18.3.1
|
||||
react-native: '*'
|
||||
|
|
@ -20153,7 +20156,7 @@ snapshots:
|
|||
optionalDependencies:
|
||||
expo: 52.0.41(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@expo/metro-runtime@4.0.1(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5)))(bufferutil@4.0.9)(encoding@0.1.13)(graphql@16.8.1)(react-native-webview@13.13.5(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1))(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)(utf-8-validate@6.0.5)
|
||||
|
||||
'@react-native-menu/menu@1.2.2(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)':
|
||||
'@react-native-menu/menu@1.2.3(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)':
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-native: 0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5)
|
||||
|
|
@ -29364,7 +29367,7 @@ snapshots:
|
|||
react-native: 0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5)
|
||||
warn-once: 0.1.1
|
||||
|
||||
react-native-track-player@4.1.1(patch_hash=debcf90daeaf7d38c744ecb11cded73b5bfc671b8d0937865ec21307414761b3)(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1):
|
||||
react-native-track-player@4.1.1(patch_hash=24a9527477cf307020abf337d3f5db78cdc015636e7ab47d085a1eb5792a5ff0)(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-native: 0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5)
|
||||
|
|
@ -32129,11 +32132,11 @@ snapshots:
|
|||
|
||||
yoga-wasm-web@0.3.3: {}
|
||||
|
||||
zeego@3.0.6(10862d462bdf72b2f4c999770e7a8205):
|
||||
zeego@3.0.6(9cf8a8a5ccce31468c34b8be336f174a):
|
||||
dependencies:
|
||||
'@radix-ui/react-context-menu': 2.2.6(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@radix-ui/react-dropdown-menu': 2.1.6(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@react-native-menu/menu': 1.2.2(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
'@react-native-menu/menu': 1.2.3(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-native: 0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5)
|
||||
react-native-ios-context-menu: 3.1.0(react-native-ios-utilities@5.1.3(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1))(react-native@0.77.2(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.12)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@6.0.5))(react@18.3.1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue