@bluebillywig/react-native-bb-player 8.42.7 → 8.42.8
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.
|
@@ -4,6 +4,8 @@ import android.app.Activity
|
|
|
4
4
|
import android.os.Handler
|
|
5
5
|
import android.os.Looper
|
|
6
6
|
import android.util.Log
|
|
7
|
+
import android.view.MotionEvent
|
|
8
|
+
import android.view.View
|
|
7
9
|
import android.widget.FrameLayout
|
|
8
10
|
import androidx.collection.ArrayMap
|
|
9
11
|
import androidx.mediarouter.app.MediaRouteButton
|
|
@@ -31,8 +33,40 @@ private inline fun debugLog(tag: String, message: () -> String) {
|
|
|
31
33
|
/**
|
|
32
34
|
* React Native View for Blue Billywig Native Player
|
|
33
35
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
+
* This view wraps the native BBNativePlayerView and handles the integration with
|
|
37
|
+
* React Native's view system. The key challenge is that React Native uses Yoga for
|
|
38
|
+
* layout, which can interfere with native Android views that have their own layout
|
|
39
|
+
* and touch handling requirements (like ExoPlayer's StyledPlayerView with controlbar).
|
|
40
|
+
*
|
|
41
|
+
* ## Native Layout Integration
|
|
42
|
+
*
|
|
43
|
+
* To ensure the native player controls work correctly, this view:
|
|
44
|
+
*
|
|
45
|
+
* 1. **Overrides requestLayout()** - Ensures layout requests propagate correctly through
|
|
46
|
+
* React Native's view hierarchy by posting to the choreographer.
|
|
47
|
+
*
|
|
48
|
+
* 2. **Overrides onMeasure()** - Uses native Android measurement (MeasureSpec.EXACTLY)
|
|
49
|
+
* instead of letting Yoga determine the size, ensuring the player and its controls
|
|
50
|
+
* receive proper dimensions.
|
|
51
|
+
*
|
|
52
|
+
* 3. **Overrides onLayout()** - Explicitly layouts child views to fill the container,
|
|
53
|
+
* which is necessary because React Native's Yoga layout doesn't automatically
|
|
54
|
+
* propagate layout to native child views.
|
|
55
|
+
*
|
|
56
|
+
* 4. **Overrides onInterceptTouchEvent()** - Returns false to ensure touch events
|
|
57
|
+
* always reach the child BBNativePlayerView, allowing the player's controlbar
|
|
58
|
+
* to respond to taps.
|
|
59
|
+
*
|
|
60
|
+
* ## Why This Is Necessary
|
|
61
|
+
*
|
|
62
|
+
* React Native's Yoga layout system is designed for flexbox-based UI, not for native
|
|
63
|
+
* views with complex internal view hierarchies. The BBNativePlayerView contains an
|
|
64
|
+
* ExoPlayer StyledPlayerView which has its own gesture detectors and controlbar that
|
|
65
|
+
* need to receive touch events directly. Without these overrides, React Native's
|
|
66
|
+
* touch handling system intercepts events before they reach the native player controls.
|
|
67
|
+
*
|
|
68
|
+
* This approach is similar to how Expo's ExpoView handles native views with
|
|
69
|
+
* `shouldUseAndroidLayout = true`.
|
|
36
70
|
*/
|
|
37
71
|
class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(reactContext),
|
|
38
72
|
BBNativePlayerViewDelegate {
|
|
@@ -56,18 +90,108 @@ class BBPlayerView(private val reactContext: ThemedReactContext) : FrameLayout(r
|
|
|
56
90
|
setBackgroundColor(android.graphics.Color.BLACK)
|
|
57
91
|
}
|
|
58
92
|
|
|
93
|
+
// ==================================================================================
|
|
94
|
+
// NATIVE LAYOUT INTEGRATION
|
|
95
|
+
// These overrides ensure the native player view and its controls work correctly
|
|
96
|
+
// within React Native's Yoga-based layout system.
|
|
97
|
+
// ==================================================================================
|
|
98
|
+
|
|
59
99
|
/**
|
|
60
|
-
* Override
|
|
61
|
-
*
|
|
100
|
+
* Override requestLayout to ensure layout requests are properly handled.
|
|
101
|
+
*
|
|
102
|
+
* React Native's layout system uses Yoga which processes layout asynchronously.
|
|
103
|
+
* Native Android views expect requestLayout() to trigger a synchronous layout pass.
|
|
104
|
+
* This override posts a layout request to ensure proper propagation through the
|
|
105
|
+
* view hierarchy while being frame-aligned via Choreographer for performance.
|
|
106
|
+
*
|
|
107
|
+
* Without this, the native player view may not update its layout when needed,
|
|
108
|
+
* causing issues with control positioning and visibility.
|
|
109
|
+
*/
|
|
110
|
+
override fun requestLayout() {
|
|
111
|
+
super.requestLayout()
|
|
112
|
+
|
|
113
|
+
// Post to ensure layout happens on the next frame, avoiding layout-during-layout issues
|
|
114
|
+
// This is a common pattern for native views embedded in React Native
|
|
115
|
+
post {
|
|
116
|
+
measure(
|
|
117
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
118
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
119
|
+
)
|
|
120
|
+
layout(left, top, right, bottom)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Override onMeasure to use native Android measurement.
|
|
126
|
+
*
|
|
127
|
+
* By default, React Native's Yoga layout may pass UNSPECIFIED or AT_MOST specs,
|
|
128
|
+
* which can confuse native views expecting EXACTLY specs. This ensures the player
|
|
129
|
+
* view receives precise dimensions matching the container size set by React Native.
|
|
130
|
+
*
|
|
131
|
+
* Performance note: This is called during the measure pass and is optimized to
|
|
132
|
+
* avoid unnecessary work by only measuring children when we have valid dimensions.
|
|
133
|
+
*/
|
|
134
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
135
|
+
// Get the exact dimensions from React Native's layout
|
|
136
|
+
val width = MeasureSpec.getSize(widthMeasureSpec)
|
|
137
|
+
val height = MeasureSpec.getSize(heightMeasureSpec)
|
|
138
|
+
|
|
139
|
+
// Set our measured dimensions
|
|
140
|
+
setMeasuredDimension(width, height)
|
|
141
|
+
|
|
142
|
+
// Measure all children with EXACTLY specs to ensure they fill the container
|
|
143
|
+
// This is critical for the player view to receive proper dimensions
|
|
144
|
+
if (width > 0 && height > 0) {
|
|
145
|
+
val childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)
|
|
146
|
+
val childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
147
|
+
|
|
148
|
+
for (i in 0 until childCount) {
|
|
149
|
+
getChildAt(i)?.measure(childWidthSpec, childHeightSpec)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Override onLayout to explicitly position child views.
|
|
156
|
+
*
|
|
157
|
+
* React Native's Yoga layout calculates positions but doesn't automatically apply
|
|
158
|
+
* them to native child views. This explicitly layouts all children to fill the
|
|
159
|
+
* container, which is necessary for the BBNativePlayerView to render correctly.
|
|
62
160
|
*/
|
|
63
161
|
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
64
162
|
super.onLayout(changed, left, top, right, bottom)
|
|
65
|
-
|
|
163
|
+
|
|
164
|
+
val width = right - left
|
|
165
|
+
val height = bottom - top
|
|
166
|
+
|
|
167
|
+
// Layout all children to fill the entire container
|
|
66
168
|
for (i in 0 until childCount) {
|
|
67
|
-
getChildAt(i)?.layout(0, 0,
|
|
169
|
+
getChildAt(i)?.layout(0, 0, width, height)
|
|
68
170
|
}
|
|
69
171
|
}
|
|
70
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Never intercept touch events - let them pass through to child views.
|
|
175
|
+
*
|
|
176
|
+
* This is CRITICAL for the player controlbar to work. React Native's gesture
|
|
177
|
+
* handling system can intercept touch events before they reach native views.
|
|
178
|
+
* By always returning false, we ensure:
|
|
179
|
+
*
|
|
180
|
+
* 1. Single taps reach the PlayerView to toggle the controlbar
|
|
181
|
+
* 2. Double taps reach the PlayerView for seek functionality
|
|
182
|
+
* 3. Swipes and other gestures work for any interactive elements
|
|
183
|
+
*
|
|
184
|
+
* The BBNativePlayerView handles its own touch events internally through
|
|
185
|
+
* ExoPlayer's StyledPlayerView and custom gesture detectors.
|
|
186
|
+
*/
|
|
187
|
+
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
|
188
|
+
return false
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ==================================================================================
|
|
192
|
+
// END NATIVE LAYOUT INTEGRATION
|
|
193
|
+
// ==================================================================================
|
|
194
|
+
|
|
71
195
|
// Timer for periodic time updates (opt-in for performance)
|
|
72
196
|
private val timeUpdateHandler = Handler(Looper.getMainLooper())
|
|
73
197
|
private var timeUpdateRunnable: Runnable? = null
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bluebillywig/react-native-bb-player",
|
|
3
|
-
"version": "8.42.
|
|
3
|
+
"version": "8.42.8",
|
|
4
4
|
"description": "Blue Billywig Native Video Player for React Native - iOS AVPlayer and Android ExoPlayer integration",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|