react-native-nitro-player 1.5.0-alpha.0 → 1.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/ios/core/CastSessionManager.swift +6 -0
- package/lib/components/CastButton.js +68 -43
- package/package.json +1 -1
- package/src/components/CastButton.tsx +84 -47
package/README.md
CHANGED
|
@@ -677,7 +677,9 @@ Cast requires native configuration in your **app** (not just the library).
|
|
|
677
677
|
<key>NSBonjourServices</key>
|
|
678
678
|
<array>
|
|
679
679
|
<string>_googlecast._tcp</string>
|
|
680
|
-
<!--
|
|
680
|
+
<!-- Receiver-specific service. Use _CC1AD845._googlecast._tcp for the Default
|
|
681
|
+
Media Receiver, or _<YOUR_RECEIVER_APP_ID>._googlecast._tcp for a custom one. -->
|
|
682
|
+
<string>_CC1AD845._googlecast._tcp</string>
|
|
681
683
|
</array>
|
|
682
684
|
<!-- Only if you use Cast guest mode (full SDK): -->
|
|
683
685
|
<key>NSBluetoothAlwaysUsageDescription</key>
|
|
@@ -41,9 +41,15 @@ final class CastSessionManager: NSObject {
|
|
|
41
41
|
let criteria = GCKDiscoveryCriteria(applicationID: appId)
|
|
42
42
|
let options = GCKCastOptions(discoveryCriteria: criteria)
|
|
43
43
|
options.suspendSessionsWhenBackgrounded = false
|
|
44
|
+
// We expose a custom Cast button (not GCKUICastButton), so device discovery
|
|
45
|
+
// must begin immediately instead of waiting for a tap on a GCKUICastButton —
|
|
46
|
+
// otherwise no devices are ever found.
|
|
47
|
+
options.startDiscoveryAfterFirstTapOnCastButton = false
|
|
44
48
|
GCKCastContext.setSharedInstanceWith(options)
|
|
45
49
|
|
|
46
50
|
let context = GCKCastContext.sharedInstance()
|
|
51
|
+
// Begin scanning right away so devices are available before the picker opens.
|
|
52
|
+
context.discoveryManager.startDiscovery()
|
|
47
53
|
context.sessionManager.add(self)
|
|
48
54
|
NotificationCenter.default.addObserver(
|
|
49
55
|
self,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Pressable,
|
|
2
|
+
import { Pressable, View, } from 'react-native';
|
|
3
3
|
import { Cast } from '../index';
|
|
4
4
|
import { useCastState } from '../hooks/useCastState';
|
|
5
5
|
/**
|
|
@@ -40,61 +40,86 @@ export function CastButton({ size = 24, color = '#000000', activeColor = '#1E88E
|
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Dependency-free rendition of the standard Cast glyph: a rounded "screen"
|
|
43
|
-
* rectangle with
|
|
44
|
-
*
|
|
43
|
+
* rectangle (with its bottom-left corner open) and a broadcast signal — a dot
|
|
44
|
+
* and two concentric arcs — radiating from the bottom-left. Drawn purely with
|
|
45
|
+
* `View`s so the library stays dependency-free.
|
|
45
46
|
*/
|
|
46
47
|
function CastGlyph({ size, color, }) {
|
|
47
|
-
const stroke = Math.max(1.5, Math.round(size
|
|
48
|
-
const inset =
|
|
49
|
-
const dotSize = stroke * 1.
|
|
50
|
-
|
|
51
|
-
const
|
|
48
|
+
const stroke = Math.max(1.5, Math.round(size / 13));
|
|
49
|
+
const inset = Math.round(size * 0.14);
|
|
50
|
+
const dotSize = stroke * 1.5;
|
|
51
|
+
// Origin of the broadcast signal — the screen's bottom-left corner.
|
|
52
|
+
const cx = inset + stroke / 2;
|
|
53
|
+
const cy = size - inset - stroke / 2;
|
|
54
|
+
// Leave the screen's bottom-left corner open so the signal reads clearly.
|
|
55
|
+
const gap = size * 0.46;
|
|
52
56
|
return (React.createElement(View, { style: { width: size, height: size } },
|
|
53
|
-
React.createElement(View, { style: [
|
|
54
|
-
StyleSheet.absoluteFill,
|
|
55
|
-
{
|
|
56
|
-
borderWidth: stroke,
|
|
57
|
-
borderColor: color,
|
|
58
|
-
borderRadius: Math.max(2, size * 0.12),
|
|
59
|
-
},
|
|
60
|
-
] }),
|
|
61
57
|
React.createElement(View, { style: {
|
|
62
58
|
position: 'absolute',
|
|
63
59
|
left: inset,
|
|
60
|
+
top: inset,
|
|
61
|
+
right: inset,
|
|
62
|
+
height: stroke,
|
|
63
|
+
backgroundColor: color,
|
|
64
|
+
borderRadius: stroke,
|
|
65
|
+
} }),
|
|
66
|
+
React.createElement(View, { style: {
|
|
67
|
+
position: 'absolute',
|
|
68
|
+
top: inset,
|
|
69
|
+
bottom: inset,
|
|
70
|
+
right: inset,
|
|
71
|
+
width: stroke,
|
|
72
|
+
backgroundColor: color,
|
|
73
|
+
borderRadius: stroke,
|
|
74
|
+
} }),
|
|
75
|
+
React.createElement(View, { style: {
|
|
76
|
+
position: 'absolute',
|
|
77
|
+
left: inset + gap,
|
|
64
78
|
bottom: inset,
|
|
79
|
+
right: inset,
|
|
80
|
+
height: stroke,
|
|
81
|
+
backgroundColor: color,
|
|
82
|
+
borderRadius: stroke,
|
|
83
|
+
} }),
|
|
84
|
+
React.createElement(View, { style: {
|
|
85
|
+
position: 'absolute',
|
|
86
|
+
left: inset,
|
|
87
|
+
top: inset,
|
|
88
|
+
height: size - 2 * inset - gap,
|
|
89
|
+
width: stroke,
|
|
90
|
+
backgroundColor: color,
|
|
91
|
+
borderRadius: stroke,
|
|
92
|
+
} }),
|
|
93
|
+
React.createElement(RippleArc, { cx: cx, cy: cy, radius: size * 0.2, stroke: stroke, color: color }),
|
|
94
|
+
React.createElement(RippleArc, { cx: cx, cy: cy, radius: size * 0.34, stroke: stroke, color: color }),
|
|
95
|
+
React.createElement(View, { style: {
|
|
96
|
+
position: 'absolute',
|
|
97
|
+
left: cx - dotSize / 2,
|
|
98
|
+
top: cy - dotSize / 2,
|
|
65
99
|
width: dotSize,
|
|
66
100
|
height: dotSize,
|
|
67
101
|
borderRadius: dotSize / 2,
|
|
68
102
|
backgroundColor: color,
|
|
69
|
-
} })
|
|
70
|
-
React.createElement(Arc, { origin: inset + dotSize / 2, radius: r1, stroke: stroke, color: color }),
|
|
71
|
-
React.createElement(Arc, { origin: inset + dotSize / 2, radius: r2, stroke: stroke, color: color })));
|
|
103
|
+
} })));
|
|
72
104
|
}
|
|
73
105
|
/**
|
|
74
|
-
* A quarter-circle arc (the
|
|
75
|
-
*
|
|
106
|
+
* A quarter-circle arc (the up-right quadrant) centered at (cx, cy). Rendered as
|
|
107
|
+
* a circle with only its top border colored, rotated 45° so the visible cap
|
|
108
|
+
* faces up-and-to-the-right — i.e. radiating from the bottom-left dot.
|
|
76
109
|
*/
|
|
77
|
-
function
|
|
78
|
-
return (React.createElement(View
|
|
79
|
-
// Clip box reveals only the quadrant facing up-and-right from the origin.
|
|
80
|
-
, {
|
|
81
|
-
// Clip box reveals only the quadrant facing up-and-right from the origin.
|
|
82
|
-
style: {
|
|
110
|
+
function RippleArc({ cx, cy, radius, stroke, color, }) {
|
|
111
|
+
return (React.createElement(View, { style: {
|
|
83
112
|
position: 'absolute',
|
|
84
|
-
left:
|
|
85
|
-
|
|
86
|
-
width: radius,
|
|
87
|
-
height: radius,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
borderRadius: radius,
|
|
97
|
-
borderWidth: stroke,
|
|
98
|
-
borderColor: color,
|
|
99
|
-
} })));
|
|
113
|
+
left: cx - radius,
|
|
114
|
+
top: cy - radius,
|
|
115
|
+
width: radius * 2,
|
|
116
|
+
height: radius * 2,
|
|
117
|
+
borderRadius: radius,
|
|
118
|
+
borderWidth: stroke,
|
|
119
|
+
borderTopColor: color,
|
|
120
|
+
borderRightColor: 'transparent',
|
|
121
|
+
borderBottomColor: 'transparent',
|
|
122
|
+
borderLeftColor: 'transparent',
|
|
123
|
+
transform: [{ rotate: '45deg' }],
|
|
124
|
+
} }));
|
|
100
125
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-player",
|
|
3
|
-
"version": "1.5.0-alpha.
|
|
3
|
+
"version": "1.5.0-alpha.1",
|
|
4
4
|
"description": "A powerful audio player library for React Native with playlist management, playback controls, and support for Android Auto and CarPlay",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import {
|
|
3
3
|
Pressable,
|
|
4
|
-
StyleSheet,
|
|
5
4
|
View,
|
|
6
5
|
type PressableProps,
|
|
7
6
|
type StyleProp,
|
|
@@ -104,8 +103,9 @@ export function CastButton({
|
|
|
104
103
|
|
|
105
104
|
/**
|
|
106
105
|
* Dependency-free rendition of the standard Cast glyph: a rounded "screen"
|
|
107
|
-
* rectangle with
|
|
108
|
-
*
|
|
106
|
+
* rectangle (with its bottom-left corner open) and a broadcast signal — a dot
|
|
107
|
+
* and two concentric arcs — radiating from the bottom-left. Drawn purely with
|
|
108
|
+
* `View`s so the library stays dependency-free.
|
|
109
109
|
*/
|
|
110
110
|
function CastGlyph({
|
|
111
111
|
size,
|
|
@@ -114,83 +114,120 @@ function CastGlyph({
|
|
|
114
114
|
size: number
|
|
115
115
|
color: string
|
|
116
116
|
}): React.ReactElement {
|
|
117
|
-
const stroke = Math.max(1.5, Math.round(size
|
|
118
|
-
const inset =
|
|
119
|
-
const dotSize = stroke * 1.
|
|
120
|
-
|
|
121
|
-
const
|
|
117
|
+
const stroke = Math.max(1.5, Math.round(size / 13))
|
|
118
|
+
const inset = Math.round(size * 0.14)
|
|
119
|
+
const dotSize = stroke * 1.5
|
|
120
|
+
// Origin of the broadcast signal — the screen's bottom-left corner.
|
|
121
|
+
const cx = inset + stroke / 2
|
|
122
|
+
const cy = size - inset - stroke / 2
|
|
123
|
+
// Leave the screen's bottom-left corner open so the signal reads clearly.
|
|
124
|
+
const gap = size * 0.46
|
|
122
125
|
|
|
123
126
|
return (
|
|
124
127
|
<View style={{ width: size, height: size }}>
|
|
125
|
-
{/* Screen
|
|
128
|
+
{/* Screen: top + right + most of bottom + most of left, leaving the
|
|
129
|
+
bottom-left corner open (composed from edges for a clean cast look). */}
|
|
130
|
+
{/* Top edge */}
|
|
126
131
|
<View
|
|
127
|
-
style={
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
style={{
|
|
133
|
+
position: 'absolute',
|
|
134
|
+
left: inset,
|
|
135
|
+
top: inset,
|
|
136
|
+
right: inset,
|
|
137
|
+
height: stroke,
|
|
138
|
+
backgroundColor: color,
|
|
139
|
+
borderRadius: stroke,
|
|
140
|
+
}}
|
|
135
141
|
/>
|
|
136
|
-
{/*
|
|
142
|
+
{/* Right edge */}
|
|
137
143
|
<View
|
|
138
144
|
style={{
|
|
139
145
|
position: 'absolute',
|
|
140
|
-
|
|
146
|
+
top: inset,
|
|
147
|
+
bottom: inset,
|
|
148
|
+
right: inset,
|
|
149
|
+
width: stroke,
|
|
150
|
+
backgroundColor: color,
|
|
151
|
+
borderRadius: stroke,
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
{/* Bottom edge (stops short of the bottom-left corner) */}
|
|
155
|
+
<View
|
|
156
|
+
style={{
|
|
157
|
+
position: 'absolute',
|
|
158
|
+
left: inset + gap,
|
|
141
159
|
bottom: inset,
|
|
160
|
+
right: inset,
|
|
161
|
+
height: stroke,
|
|
162
|
+
backgroundColor: color,
|
|
163
|
+
borderRadius: stroke,
|
|
164
|
+
}}
|
|
165
|
+
/>
|
|
166
|
+
{/* Left edge (stops short of the bottom-left corner) */}
|
|
167
|
+
<View
|
|
168
|
+
style={{
|
|
169
|
+
position: 'absolute',
|
|
170
|
+
left: inset,
|
|
171
|
+
top: inset,
|
|
172
|
+
height: size - 2 * inset - gap,
|
|
173
|
+
width: stroke,
|
|
174
|
+
backgroundColor: color,
|
|
175
|
+
borderRadius: stroke,
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
{/* Two concentric broadcast arcs radiating from the corner dot */}
|
|
179
|
+
<RippleArc cx={cx} cy={cy} radius={size * 0.2} stroke={stroke} color={color} />
|
|
180
|
+
<RippleArc cx={cx} cy={cy} radius={size * 0.34} stroke={stroke} color={color} />
|
|
181
|
+
{/* Broadcast dot */}
|
|
182
|
+
<View
|
|
183
|
+
style={{
|
|
184
|
+
position: 'absolute',
|
|
185
|
+
left: cx - dotSize / 2,
|
|
186
|
+
top: cy - dotSize / 2,
|
|
142
187
|
width: dotSize,
|
|
143
188
|
height: dotSize,
|
|
144
189
|
borderRadius: dotSize / 2,
|
|
145
190
|
backgroundColor: color,
|
|
146
191
|
}}
|
|
147
192
|
/>
|
|
148
|
-
{/* Two concentric quarter arcs radiating from the dot */}
|
|
149
|
-
<Arc origin={inset + dotSize / 2} radius={r1} stroke={stroke} color={color} />
|
|
150
|
-
<Arc origin={inset + dotSize / 2} radius={r2} stroke={stroke} color={color} />
|
|
151
193
|
</View>
|
|
152
194
|
)
|
|
153
195
|
}
|
|
154
196
|
|
|
155
197
|
/**
|
|
156
|
-
* A quarter-circle arc (the
|
|
157
|
-
*
|
|
198
|
+
* A quarter-circle arc (the up-right quadrant) centered at (cx, cy). Rendered as
|
|
199
|
+
* a circle with only its top border colored, rotated 45° so the visible cap
|
|
200
|
+
* faces up-and-to-the-right — i.e. radiating from the bottom-left dot.
|
|
158
201
|
*/
|
|
159
|
-
function
|
|
160
|
-
|
|
202
|
+
function RippleArc({
|
|
203
|
+
cx,
|
|
204
|
+
cy,
|
|
161
205
|
radius,
|
|
162
206
|
stroke,
|
|
163
207
|
color,
|
|
164
208
|
}: {
|
|
165
|
-
|
|
209
|
+
cx: number
|
|
210
|
+
cy: number
|
|
166
211
|
radius: number
|
|
167
212
|
stroke: number
|
|
168
213
|
color: string
|
|
169
214
|
}): React.ReactElement {
|
|
170
215
|
return (
|
|
171
216
|
<View
|
|
172
|
-
// Clip box reveals only the quadrant facing up-and-right from the origin.
|
|
173
217
|
style={{
|
|
174
218
|
position: 'absolute',
|
|
175
|
-
left:
|
|
176
|
-
|
|
177
|
-
width: radius,
|
|
178
|
-
height: radius,
|
|
179
|
-
|
|
219
|
+
left: cx - radius,
|
|
220
|
+
top: cy - radius,
|
|
221
|
+
width: radius * 2,
|
|
222
|
+
height: radius * 2,
|
|
223
|
+
borderRadius: radius,
|
|
224
|
+
borderWidth: stroke,
|
|
225
|
+
borderTopColor: color,
|
|
226
|
+
borderRightColor: 'transparent',
|
|
227
|
+
borderBottomColor: 'transparent',
|
|
228
|
+
borderLeftColor: 'transparent',
|
|
229
|
+
transform: [{ rotate: '45deg' }],
|
|
180
230
|
}}
|
|
181
|
-
|
|
182
|
-
<View
|
|
183
|
-
style={{
|
|
184
|
-
position: 'absolute',
|
|
185
|
-
left: -radius,
|
|
186
|
-
top: 0,
|
|
187
|
-
width: radius * 2,
|
|
188
|
-
height: radius * 2,
|
|
189
|
-
borderRadius: radius,
|
|
190
|
-
borderWidth: stroke,
|
|
191
|
-
borderColor: color,
|
|
192
|
-
}}
|
|
193
|
-
/>
|
|
194
|
-
</View>
|
|
231
|
+
/>
|
|
195
232
|
)
|
|
196
233
|
}
|