diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..77bcc822
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,47 @@
+name: Build Android APK
+on:
+ schedule:
+
+ workflow_dispatch: # Allows you to run this manually from the "Actions" tab
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'npm'
+
+ - name: Setup Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: '17'
+
+ - name: Install Dependencies
+ run: npm install --legacy-peer-deps
+
+ - name: Setup Expo
+ uses: expo/expo-github-action@v8
+ with:
+ expo-version: latest
+
+ - name: Prebuild Android
+ run: npx expo prebuild --platform android
+
+ - name: Build APK (Release)
+ run: |
+ cd android
+ chmod +x gradlew
+ ./gradlew assembleRelease
+
+ - name: Upload APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuvio-app-release
+ path: android/app/build/outputs/apk/release/app-release.apk
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 810d5111..aab98261 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -2,12 +2,12 @@
+
-
diff --git a/android/sentry.properties b/android/sentry.properties
index ae003a4b..5581e03d 100644
--- a/android/sentry.properties
+++ b/android/sentry.properties
@@ -1,4 +1,4 @@
defaults.url=https://sentry.io/
defaults.org=tapframe
defaults.project=react-native
-auth.token=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
+# Using SENTRY_AUTH_TOKEN environment variable
\ No newline at end of file
diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt b/node_modules/react-native-video/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt
index 953eb59e..1ac0fd03 100644
--- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt
+++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt
@@ -1,6 +1,5 @@
package com.brentvatne.common.api
-import android.graphics.Color
import com.brentvatne.common.toolbox.ReactBridgeUtils
import com.facebook.react.bridge.ReadableMap
@@ -23,17 +22,6 @@ class SubtitleStyle public constructor() {
var subtitlesFollowVideo = true
private set
- // Extended styling (used by ExoPlayerView via Media3 SubtitleView)
- // Stored as Android color ints to avoid parsing multiple times.
- var textColor: Int? = null
- private set
- var backgroundColor: Int? = null
- private set
- var edgeType: String? = null
- private set
- var edgeColor: Int? = null
- private set
-
companion object {
private const val PROP_FONT_SIZE_TRACK = "fontSize"
private const val PROP_PADDING_BOTTOM = "paddingBottom"
@@ -43,21 +31,6 @@ class SubtitleStyle public constructor() {
private const val PROP_OPACITY = "opacity"
private const val PROP_SUBTITLES_FOLLOW_VIDEO = "subtitlesFollowVideo"
- // Extended props (optional)
- private const val PROP_TEXT_COLOR = "textColor"
- private const val PROP_BACKGROUND_COLOR = "backgroundColor"
- private const val PROP_EDGE_TYPE = "edgeType"
- private const val PROP_EDGE_COLOR = "edgeColor"
-
- private fun parseColorOrNull(value: String?): Int? {
- if (value.isNullOrBlank()) return null
- return try {
- Color.parseColor(value)
- } catch (_: IllegalArgumentException) {
- null
- }
- }
-
@JvmStatic
fun parse(src: ReadableMap?): SubtitleStyle {
val subtitleStyle = SubtitleStyle()
@@ -68,13 +41,6 @@ class SubtitleStyle public constructor() {
subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0)
subtitleStyle.opacity = ReactBridgeUtils.safeGetFloat(src, PROP_OPACITY, 1f)
subtitleStyle.subtitlesFollowVideo = ReactBridgeUtils.safeGetBool(src, PROP_SUBTITLES_FOLLOW_VIDEO, true)
-
- // Extended styling
- subtitleStyle.textColor = parseColorOrNull(ReactBridgeUtils.safeGetString(src, PROP_TEXT_COLOR, null))
- subtitleStyle.backgroundColor = parseColorOrNull(ReactBridgeUtils.safeGetString(src, PROP_BACKGROUND_COLOR, null))
- subtitleStyle.edgeType = ReactBridgeUtils.safeGetString(src, PROP_EDGE_TYPE, null)
- subtitleStyle.edgeColor = parseColorOrNull(ReactBridgeUtils.safeGetString(src, PROP_EDGE_COLOR, null))
-
return subtitleStyle
}
}
diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt
index 2d3f8cae..bb945fe7 100644
--- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt
+++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.kt
@@ -10,14 +10,11 @@ import android.widget.FrameLayout
import android.widget.TextView
import androidx.media3.common.Player
import androidx.media3.common.Timeline
-import androidx.media3.common.text.CueGroup
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.AspectRatioFrameLayout
-import androidx.media3.ui.CaptionStyleCompat
import androidx.media3.ui.DefaultTimeBar
import androidx.media3.ui.PlayerView
-import androidx.media3.ui.SubtitleView
import com.brentvatne.common.api.ResizeMode
import com.brentvatne.common.api.SubtitleStyle
@@ -55,58 +52,15 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
resizeMode = androidx.media3.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT
}
- /**
- * Subtitles rendered in a full-size overlay (NOT inside PlayerView's content frame).
- * This keeps subtitles anchored in-place even when the video surface/content frame moves
- * due to aspect ratio / resizeMode changes.
- *
- * Controlled by SubtitleStyle.subtitlesFollowVideo.
- */
- private val overlaySubtitleView = SubtitleView(context).apply {
- layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
- visibility = View.GONE
- // We control styling via SubtitleStyle; don't pull Android system caption defaults.
- setApplyEmbeddedStyles(true)
- setApplyEmbeddedFontSizes(true)
- }
-
- private fun updateSubtitleRenderingMode() {
- val internalSubtitleView = playerView.subtitleView
- val followVideo = localStyle.subtitlesFollowVideo
- val shouldShow = localStyle.opacity != 0.0f
-
- if (followVideo) {
- internalSubtitleView?.visibility = if (shouldShow) View.VISIBLE else View.GONE
- overlaySubtitleView.visibility = View.GONE
- } else {
- // Hard-disable PlayerView's internal subtitle view. PlayerView can recreate/toggle this view
- // during resize/layout, so we re-assert this in multiple lifecycle points.
- internalSubtitleView?.visibility = View.GONE
- internalSubtitleView?.alpha = 0f
- overlaySubtitleView.visibility = if (shouldShow) View.VISIBLE else View.GONE
- overlaySubtitleView.alpha = 1f
- }
- }
-
init {
// Add PlayerView with explicit layout parameters
val playerViewLayoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
addView(playerView, playerViewLayoutParams)
- // Add overlay subtitles above PlayerView (so it doesn't move with video content frame)
- val subtitleOverlayLayoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
- addView(overlaySubtitleView, subtitleOverlayLayoutParams)
-
// Add live badge with its own layout parameters
val liveBadgeLayoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
liveBadgeLayoutParams.setMargins(16, 16, 16, 16)
addView(liveBadge, liveBadgeLayoutParams)
-
- // PlayerView may internally recreate its subtitle view during relayouts (e.g. resizeMode changes).
- // Ensure our rendering mode is re-applied whenever PlayerView lays out.
- playerView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
- updateSubtitleRenderingMode()
- }
}
fun setPlayer(player: ExoPlayer?) {
@@ -126,10 +80,6 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
playerView.resizeMode = resizeMode
}
}
-
- // Re-assert subtitle rendering mode for the current style.
- updateSubtitleRenderingMode()
- applySubtitleStyle(localStyle)
}
fun getPlayerView(): PlayerView = playerView
@@ -158,63 +108,23 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
}
fun setSubtitleStyle(style: SubtitleStyle) {
- localStyle = style
- applySubtitleStyle(localStyle)
- }
-
- private fun applySubtitleStyle(style: SubtitleStyle) {
- updateSubtitleRenderingMode()
-
playerView.subtitleView?.let { subtitleView ->
- // Important:
- // Avoid inheriting Android system caption settings via setUserDefaultStyle(),
- // because those can force a background/window that the app doesn't want.
- val resolvedTextColor = style.textColor ?: CaptionStyleCompat.DEFAULT.foregroundColor
- val resolvedBackgroundColor = style.backgroundColor ?: Color.TRANSPARENT
- val resolvedEdgeColor = style.edgeColor ?: Color.BLACK
+ // Reset to defaults
+ subtitleView.setUserDefaultStyle()
+ subtitleView.setUserDefaultTextSize()
- val resolvedEdgeType = when (style.edgeType?.lowercase()) {
- "outline" -> CaptionStyleCompat.EDGE_TYPE_OUTLINE
- "shadow" -> CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW
- else -> CaptionStyleCompat.EDGE_TYPE_NONE
- }
-
- // windowColor MUST be transparent to avoid the "caption window" background.
- val captionStyle = CaptionStyleCompat(
- resolvedTextColor,
- resolvedBackgroundColor,
- Color.TRANSPARENT,
- resolvedEdgeType,
- resolvedEdgeColor,
- null
- )
- subtitleView.setStyle(captionStyle)
-
- // Text size: if not provided, fall back to user default size.
+ // Apply custom styling
if (style.fontSize > 0) {
- // Use DIP so the value matches React Native's dp-based fontSize more closely.
- // SP would multiply by system fontScale and makes "30" look larger than RN "30".
- subtitleView.setFixedTextSize(android.util.TypedValue.COMPLEX_UNIT_DIP, style.fontSize.toFloat())
- } else {
- subtitleView.setUserDefaultTextSize()
+ subtitleView.setFixedTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, style.fontSize.toFloat())
}
- // Horizontal padding is still useful (safe area); vertical offset is handled via bottomPaddingFraction.
subtitleView.setPadding(
style.paddingLeft,
style.paddingTop,
style.paddingRight,
- 0
+ style.paddingBottom
)
- // Bottom offset for *internal* subtitles:
- // Use Media3 SubtitleView's bottomPaddingFraction (moves cues up) rather than raw view padding.
- if (style.paddingBottom > 0 && playerView.height > 0) {
- val fraction = (style.paddingBottom.toFloat() / playerView.height.toFloat())
- .coerceIn(0f, 0.9f)
- subtitleView.setBottomPaddingFraction(fraction)
- }
-
if (style.opacity != 0.0f) {
subtitleView.alpha = style.opacity
subtitleView.visibility = android.view.View.VISIBLE
@@ -222,59 +132,7 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
subtitleView.visibility = android.view.View.GONE
}
}
-
- // Apply the same styling to the overlay subtitle view.
- run {
- val subtitleView = overlaySubtitleView
-
- val resolvedTextColor = style.textColor ?: CaptionStyleCompat.DEFAULT.foregroundColor
- val resolvedBackgroundColor = style.backgroundColor ?: Color.TRANSPARENT
- val resolvedEdgeColor = style.edgeColor ?: Color.BLACK
-
- val resolvedEdgeType = when (style.edgeType?.lowercase()) {
- "outline" -> CaptionStyleCompat.EDGE_TYPE_OUTLINE
- "shadow" -> CaptionStyleCompat.EDGE_TYPE_DROP_SHADOW
- else -> CaptionStyleCompat.EDGE_TYPE_NONE
- }
-
- val captionStyle = CaptionStyleCompat(
- resolvedTextColor,
- resolvedBackgroundColor,
- Color.TRANSPARENT,
- resolvedEdgeType,
- resolvedEdgeColor,
- null
- )
- subtitleView.setStyle(captionStyle)
-
- if (style.fontSize > 0) {
- // Use DIP so the value matches React Native's dp-based fontSize more closely.
- subtitleView.setFixedTextSize(android.util.TypedValue.COMPLEX_UNIT_DIP, style.fontSize.toFloat())
- } else {
- subtitleView.setUserDefaultTextSize()
- }
-
- subtitleView.setPadding(
- style.paddingLeft,
- style.paddingTop,
- style.paddingRight,
- 0
- )
-
- // Bottom offset relative to the full view height (stable even when video content frame moves).
- val h = height.takeIf { it > 0 } ?: subtitleView.height
- if (style.paddingBottom > 0 && h > 0) {
- val fraction = (style.paddingBottom.toFloat() / h.toFloat())
- .coerceIn(0f, 0.9f)
- subtitleView.setBottomPaddingFraction(fraction)
- } else {
- subtitleView.setBottomPaddingFraction(0f)
- }
-
- if (style.opacity != 0.0f) {
- subtitleView.alpha = style.opacity
- }
- }
+ localStyle = style
}
fun setShutterColor(color: Int) {
@@ -365,13 +223,6 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
}
private val playerListener = object : Player.Listener {
- override fun onCues(cueGroup: CueGroup) {
- // Keep overlay subtitles in sync. This does NOT interfere with PlayerView's own subtitle rendering.
- // When subtitlesFollowVideo=false, overlaySubtitleView is the visible one.
- updateSubtitleRenderingMode()
- overlaySubtitleView.setCues(cueGroup.cues)
- }
-
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
playerView.post {
playerView.requestLayout()
@@ -433,9 +284,6 @@ class ExoPlayerView @JvmOverloads constructor(context: Context, attrs: Attribute
pendingResizeMode?.let { resizeMode ->
playerView.resizeMode = resizeMode
}
- // Re-apply bottomPaddingFraction once we have a concrete height.
- updateSubtitleRenderingMode()
- applySubtitleStyle(localStyle)
}
}
}
diff --git a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
index 72007063..e16ac96d 100644
--- a/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
+++ b/node_modules/react-native-video/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java
@@ -228,8 +228,7 @@ public class ReactExoplayerView extends FrameLayout implements
private ArrayList rootViewChildrenOriginalVisibility = new ArrayList();
/*
- * When user is seeking first called is on onPositionDiscontinuity ->
- * DISCONTINUITY_REASON_SEEK
+ * When user is seeking first called is on onPositionDiscontinuity -> DISCONTINUITY_REASON_SEEK
* Then we set if to false when playback is back in onIsPlayingChanged -> true
*/
private boolean isSeeking = false;
@@ -299,8 +298,7 @@ public class ReactExoplayerView extends FrameLayout implements
lastPos = pos;
lastBufferDuration = bufferedDuration;
lastDuration = duration;
- eventEmitter.onVideoProgress.invoke(pos, bufferedDuration, player.getDuration(),
- getPositionInFirstPeriodMsForCurrentWindow(pos));
+ eventEmitter.onVideoProgress.invoke(pos, bufferedDuration, player.getDuration(), getPositionInFirstPeriodMsForCurrentWindow(pos));
}
}
}
@@ -318,7 +316,7 @@ public class ReactExoplayerView extends FrameLayout implements
public double getPositionInFirstPeriodMsForCurrentWindow(long currentPosition) {
Timeline.Window window = new Timeline.Window();
- if (!player.getCurrentTimeline().isEmpty()) {
+ if(!player.getCurrentTimeline().isEmpty()) {
player.getCurrentTimeline().getWindow(player.getCurrentMediaItemIndex(), window);
}
return window.windowStartTimeMs + currentPosition;
@@ -357,9 +355,9 @@ public class ReactExoplayerView extends FrameLayout implements
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
exoPlayerView = new ExoPlayerView(getContext());
- exoPlayerView.addOnLayoutChangeListener(
- (View v, int l, int t, int r, int b, int ol, int ot, int or, int ob) -> PictureInPictureUtil
- .applySourceRectHint(themedReactContext, pictureInPictureParamsBuilder, exoPlayerView));
+ exoPlayerView.addOnLayoutChangeListener( (View v, int l, int t, int r, int b, int ol, int ot, int or, int ob) ->
+ PictureInPictureUtil.applySourceRectHint(themedReactContext, pictureInPictureParamsBuilder, exoPlayerView)
+ );
exoPlayerView.setLayoutParams(layoutParams);
addView(exoPlayerView, 0, layoutParams);
@@ -385,10 +383,8 @@ public class ReactExoplayerView extends FrameLayout implements
public void onHostPause() {
isInBackground = true;
Activity activity = themedReactContext.getCurrentActivity();
- boolean isInPictureInPicture = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null
- && activity.isInPictureInPictureMode();
- boolean isInMultiWindowMode = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null
- && activity.isInMultiWindowMode();
+ boolean isInPictureInPicture = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null && activity.isInPictureInPictureMode();
+ boolean isInMultiWindowMode = Util.SDK_INT >= Build.VERSION_CODES.N && activity != null && activity.isInMultiWindowMode();
if (playInBackground || isInPictureInPicture || isInMultiWindowMode) {
return;
}
@@ -407,7 +403,7 @@ public class ReactExoplayerView extends FrameLayout implements
viewHasDropped = true;
}
- // BandwidthMeter.EventListener implementation
+ //BandwidthMeter.EventListener implementation
@Override
public void onBandwidthSample(int elapsedMs, long bytes, long bitrate) {
if (mReportBandwidth) {
@@ -415,8 +411,7 @@ public class ReactExoplayerView extends FrameLayout implements
eventEmitter.onVideoBandwidthUpdate.invoke(bitrate, 0, 0, null);
} else {
Format videoFormat = player.getVideoFormat();
- boolean isRotatedContent = videoFormat != null
- && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
+ boolean isRotatedContent = videoFormat != null && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
int width = videoFormat != null ? (isRotatedContent ? videoFormat.height : videoFormat.width) : 0;
int height = videoFormat != null ? (isRotatedContent ? videoFormat.width : videoFormat.height) : 0;
String trackId = videoFormat != null ? videoFormat.id : null;
@@ -431,8 +426,7 @@ public class ReactExoplayerView extends FrameLayout implements
* Toggling the visibility of the player control view
*/
private void togglePlayerControlVisibility() {
- if (player == null)
- return;
+ if (player == null) return;
if (exoPlayerView.isControllerVisible()) {
exoPlayerView.hideController();
} else {
@@ -456,8 +450,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void updateControllerConfig() {
- if (exoPlayerView == null)
- return;
+ if (exoPlayerView == null) return;
exoPlayerView.setControllerShowTimeoutMs(5000);
@@ -468,8 +461,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void updateControllerVisibility() {
- if (exoPlayerView == null)
- return;
+ if (exoPlayerView == null) return;
exoPlayerView.setUseController(controls && !controlsConfig.getHideFullscreen());
}
@@ -477,7 +469,7 @@ public class ReactExoplayerView extends FrameLayout implements
private void openSettings() {
AlertDialog.Builder builder = new AlertDialog.Builder(themedReactContext);
builder.setTitle(R.string.settings);
- String[] settingsOptions = { themedReactContext.getString(R.string.playback_speed) };
+ String[] settingsOptions = {themedReactContext.getString(R.string.playback_speed)};
builder.setItems(settingsOptions, (dialog, which) -> {
if (which == 0) {
showPlaybackSpeedOptions();
@@ -487,7 +479,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
private void showPlaybackSpeedOptions() {
- String[] speedOptions = { "0.5x", "1.0x", "1.5x", "2.0x" };
+ String[] speedOptions = {"0.5x", "1.0x", "1.5x", "2.0x"};
AlertDialog.Builder builder = new AlertDialog.Builder(themedReactContext);
builder.setTitle(R.string.select_playback_speed);
@@ -505,10 +497,8 @@ public class ReactExoplayerView extends FrameLayout implements
speed = 2.0f;
break;
default:
- speed = 1.0f;
- ;
- }
- ;
+ speed = 1.0f;;
+ };
setRateModifier(speed);
});
builder.show();
@@ -520,30 +510,24 @@ public class ReactExoplayerView extends FrameLayout implements
/**
* Update the layout
+ * @param view view needs to update layout
*
- * @param view view needs to update layout
- *
- * This is a workaround for the open bug in react-native: ...
+ * This is a workaround for the open bug in react-native: ...
*/
private void reLayout(View view) {
- if (view == null)
- return;
+ if (view == null) return;
view.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
view.layout(view.getLeft(), view.getTop(), view.getMeasuredWidth(), view.getMeasuredHeight());
}
private void refreshControlsStyles() {
- if (exoPlayerView == null || player == null || !controls)
- return;
+ if (exoPlayerView == null || player == null || !controls) return;
updateControllerVisibility();
}
- // Note: The following methods for live content and button visibility are no
- // longer needed
- // as PlayerView handles controls automatically. Some functionality may need to
- // be
+ // Note: The following methods for live content and button visibility are no longer needed
+ // as PlayerView handles controls automatically. Some functionality may need to be
// reimplemented using PlayerView's APIs if custom behavior is required.
private void reLayoutControls() {
@@ -580,7 +564,6 @@ public class ReactExoplayerView extends FrameLayout implements
private class RNVLoadControl extends DefaultLoadControl {
private final int availableHeapInBytes;
private final Runtime runtime;
-
public RNVLoadControl(DefaultAllocator allocator, BufferConfig config) {
super(allocator,
config.getMinBufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
@@ -591,7 +574,7 @@ public class ReactExoplayerView extends FrameLayout implements
: DefaultLoadControl.DEFAULT_MAX_BUFFER_MS,
config.getBufferForPlaybackMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
? config.getBufferForPlaybackMs()
- : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS,
+ : DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS ,
config.getBufferForPlaybackAfterRebufferMs() != BufferConfig.Companion.getBufferConfigPropUnsetInt()
? config.getBufferForPlaybackAfterRebufferMs()
: DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
@@ -602,12 +585,10 @@ public class ReactExoplayerView extends FrameLayout implements
: DefaultLoadControl.DEFAULT_BACK_BUFFER_DURATION_MS,
DefaultLoadControl.DEFAULT_RETAIN_BACK_BUFFER_FROM_KEYFRAME);
runtime = Runtime.getRuntime();
- ActivityManager activityManager = (ActivityManager) themedReactContext
- .getSystemService(ThemedReactContext.ACTIVITY_SERVICE);
- double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion
- .getBufferConfigPropUnsetDouble()
- ? config.getMaxHeapAllocationPercent()
- : DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
+ ActivityManager activityManager = (ActivityManager) themedReactContext.getSystemService(ThemedReactContext.ACTIVITY_SERVICE);
+ double maxHeap = config.getMaxHeapAllocationPercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
+ ? config.getMaxHeapAllocationPercent()
+ : DEFAULT_MAX_HEAP_ALLOCATION_PERCENT;
availableHeapInBytes = (int) Math.floor(activityManager.getMemoryClass() * maxHeap * 1024 * 1024);
}
@@ -625,15 +606,13 @@ public class ReactExoplayerView extends FrameLayout implements
}
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
long freeMemory = runtime.maxMemory() - usedMemory;
- double minBufferMemoryReservePercent = source.getBufferConfig()
- .getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
- ? source.getBufferConfig().getMinBufferMemoryReservePercent()
- : ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
+ double minBufferMemoryReservePercent = source.getBufferConfig().getMinBufferMemoryReservePercent() != BufferConfig.Companion.getBufferConfigPropUnsetDouble()
+ ? source.getBufferConfig().getMinBufferMemoryReservePercent()
+ : ReactExoplayerView.DEFAULT_MIN_BUFFER_MEMORY_RESERVE;
long reserveMemory = (long) minBufferMemoryReservePercent * runtime.maxMemory();
long bufferedMs = bufferedDurationUs / (long) 1000;
if (reserveMemory > freeMemory && bufferedMs > 2000) {
- // We don't have enough memory in reserve so we stop buffering to allow other
- // components to use it instead
+ // We don't have enough memory in reserve so we stop buffering to allow other components to use it instead
return false;
}
if (runtime.freeMemory() == 0) {
@@ -666,13 +645,13 @@ public class ReactExoplayerView extends FrameLayout implements
// Initialize core configuration and listeners
initializePlayerCore(self);
pipListenerUnsubscribe = PictureInPictureUtil.addLifecycleEventListener(themedReactContext, this);
- PictureInPictureUtil.applyAutoEnterEnabled(themedReactContext, pictureInPictureParamsBuilder,
- this.enterPictureInPictureOnLeave);
+ PictureInPictureUtil.applyAutoEnterEnabled(themedReactContext, pictureInPictureParamsBuilder, this.enterPictureInPictureOnLeave);
}
if (!source.isLocalAssetFile() && !source.isAsset() && source.getBufferConfig().getCacheSize() > 0) {
RNVSimpleCache.INSTANCE.setSimpleCache(
this.getContext(),
- source.getBufferConfig().getCacheSize());
+ source.getBufferConfig().getCacheSize()
+ );
useCache = true;
} else {
useCache = false;
@@ -680,8 +659,7 @@ public class ReactExoplayerView extends FrameLayout implements
if (playerNeedsSource) {
// Will force display of shutter view if needed
exoPlayerView.invalidateAspectRatio();
- // DRM session manager creation must be done on a different thread to prevent
- // crashes so we start a new thread
+ // DRM session manager creation must be done on a different thread to prevent crashes so we start a new thread
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(() -> {
// DRM initialization must run on a different thread
@@ -690,8 +668,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
if (activity == null) {
DebugLog.e(TAG, "Failed to initialize Player!, null activity");
- eventEmitter.onVideoError.invoke("Failed to initialize Player!",
- new Exception("Current Activity is null!"), "1001");
+ eventEmitter.onVideoError.invoke("Failed to initialize Player!", new Exception("Current Activity is null!"), "1001");
return;
}
@@ -744,7 +721,8 @@ public class ReactExoplayerView extends FrameLayout implements
DefaultAllocator allocator = new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE);
RNVLoadControl loadControl = new RNVLoadControl(
allocator,
- source.getBufferConfig());
+ source.getBufferConfig()
+ );
long initialBitrate = source.getBufferConfig().getInitialBitrate();
if (initialBitrate > 0) {
@@ -752,10 +730,11 @@ public class ReactExoplayerView extends FrameLayout implements
this.bandwidthMeter = config.getBandwidthMeter();
}
- DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext())
- .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER)
- .setEnableDecoderFallback(true)
- .forceEnableMediaCodecAsynchronousQueueing();
+ DefaultRenderersFactory renderersFactory =
+ new DefaultRenderersFactory(getContext())
+ .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF)
+ .setEnableDecoderFallback(true)
+ .forceEnableMediaCodecAsynchronousQueueing();
DefaultMediaSourceFactory mediaSourceFactory;
@@ -764,13 +743,11 @@ public class ReactExoplayerView extends FrameLayout implements
} else {
mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory);
- mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader,
- exoPlayerView.getPlayerView());
+ mediaSourceFactory.setLocalAdInsertionComponents(unusedAdTagUri -> adsLoader, exoPlayerView.getPlayerView());
}
if (useCache && !disableCache) {
- mediaSourceFactory
- .setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
+ mediaSourceFactory.setDataSourceFactory(RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
}
player = new ExoPlayer.Builder(getContext(), renderersFactory)
@@ -795,7 +772,7 @@ public class ReactExoplayerView extends FrameLayout implements
player.setPlaybackParameters(params);
changeAudioOutput(this.audioOutput);
- if (showNotificationControls) {
+ if(showNotificationControls) {
setupPlaybackService();
}
}
@@ -807,7 +784,8 @@ public class ReactExoplayerView extends FrameLayout implements
Uri adTagUrl = adProps.getAdTagUrl();
if (adTagUrl != null) {
// Create an AdsLoader.
- ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader.Builder(themedReactContext)
+ ImaAdsLoader.Builder imaLoaderBuilder = new ImaAdsLoader
+ .Builder(themedReactContext)
.setAdEventListener(this)
.setAdErrorListener(this);
@@ -839,8 +817,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
try {
- // First check if there's a custom DRM manager registered through the plugin
- // system
+ // First check if there's a custom DRM manager registered through the plugin system
DRMManagerSpec drmManager = ReactNativeVideoManager.Companion.getInstance().getDRMManager();
if (drmManager == null) {
// If no custom manager is registered, use the default implementation
@@ -849,13 +826,11 @@ public class ReactExoplayerView extends FrameLayout implements
DrmSessionManager drmSessionManager = drmManager.buildDrmSessionManager(uuid, drmProps);
if (drmSessionManager == null) {
- eventEmitter.onVideoError.invoke("Failed to build DRM session manager",
- new Exception("DRM session manager is null"), "3007");
+ eventEmitter.onVideoError.invoke("Failed to build DRM session manager", new Exception("DRM session manager is null"), "3007");
}
// Allow plugins to override the DrmSessionManager
- DrmSessionManager overriddenManager = ReactNativeVideoManager.Companion.getInstance()
- .overrideDrmSessionManager(source, drmSessionManager);
+ DrmSessionManager overriddenManager = ReactNativeVideoManager.Companion.getInstance().overrideDrmSessionManager(source, drmSessionManager);
return overriddenManager != null ? overriddenManager : drmSessionManager;
} catch (UnsupportedDrmException ex) {
// Unsupported DRM exceptions are handled by the calling method
@@ -878,8 +853,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
/// init DRM
DrmSessionManager drmSessionManager = initializePlayerDrm();
- if (drmSessionManager == null && runningSource.getDrmProps() != null
- && runningSource.getDrmProps().getDrmType() != null) {
+ if (drmSessionManager == null && runningSource.getDrmProps() != null && runningSource.getDrmProps().getDrmType() != null) {
// Failed to initialize DRM session manager - cannot continue
DebugLog.e(TAG, "Failed to initialize DRM Session Manager Framework!");
return;
@@ -936,8 +910,7 @@ public class ReactExoplayerView extends FrameLayout implements
} catch (UnsupportedDrmException e) {
int errorStringId = Util.SDK_INT < 18 ? R.string.error_drm_not_supported
: (e.reason == UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME
- ? R.string.error_drm_unsupported_scheme
- : R.string.error_drm_unknown);
+ ? R.string.error_drm_unsupported_scheme : R.string.error_drm_unknown);
eventEmitter.onVideoError.invoke(getResources().getString(errorStringId), e, "3003");
}
}
@@ -982,8 +955,7 @@ public class ReactExoplayerView extends FrameLayout implements
if (playbackServiceBinder != null) {
playbackServiceBinder.getService().unregisterPlayer(player);
}
- } catch (Exception ignored) {
- }
+ } catch (Exception ignored) {}
playbackServiceBinder = null;
}
@@ -1015,22 +987,21 @@ public class ReactExoplayerView extends FrameLayout implements
private void cleanupPlaybackService() {
try {
- if (player != null && playbackServiceBinder != null) {
+ if(player != null && playbackServiceBinder != null) {
playbackServiceBinder.getService().unregisterPlayer(player);
}
playbackServiceBinder = null;
- if (playbackServiceConnection != null) {
+ if(playbackServiceConnection != null) {
themedReactContext.unbindService(playbackServiceConnection);
}
- } catch (Exception e) {
+ } catch(Exception e) {
DebugLog.w(TAG, "Cloud not cleanup playback service");
}
}
- private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager,
- long cropStartMs, long cropEndMs) {
+ private MediaSource buildMediaSource(Uri uri, String overrideExtension, DrmSessionManager drmSessionManager, long cropStartMs, long cropEndMs) {
if (uri == null) {
throw new IllegalStateException("Invalid video uri");
}
@@ -1062,12 +1033,12 @@ public class ReactExoplayerView extends FrameLayout implements
Uri adTagUrl = source.getAdsProps().getAdTagUrl();
if (adTagUrl != null) {
mediaItemBuilder.setAdsConfiguration(
- new MediaItem.AdsConfiguration.Builder(adTagUrl).build());
+ new MediaItem.AdsConfiguration.Builder(adTagUrl).build()
+ );
}
}
- MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils
- .getLiveConfiguration(source.getBufferConfig());
+ MediaItem.LiveConfiguration.Builder liveConfiguration = ConfigurationUtils.getLiveConfiguration(source.getBufferConfig());
mediaItemBuilder.setLiveConfiguration(liveConfiguration.build());
MediaSource.Factory mediaSourceFactory;
@@ -1079,26 +1050,29 @@ public class ReactExoplayerView extends FrameLayout implements
drmProvider = new DefaultDrmSessionManagerProvider();
}
+
switch (type) {
case CONTENT_TYPE_SS:
- if (!BuildConfig.USE_EXOPLAYER_SMOOTH_STREAMING) {
+ if(!BuildConfig.USE_EXOPLAYER_SMOOTH_STREAMING) {
DebugLog.e("Exo Player Exception", "Smooth Streaming is not enabled!");
throw new IllegalStateException("Smooth Streaming is not enabled!");
}
mediaSourceFactory = new SsMediaSource.Factory(
new DefaultSsChunkSource.Factory(mediaDataSourceFactory),
- buildDataSourceFactory(false));
+ buildDataSourceFactory(false)
+ );
break;
case CONTENT_TYPE_DASH:
- if (!BuildConfig.USE_EXOPLAYER_DASH) {
+ if(!BuildConfig.USE_EXOPLAYER_DASH) {
DebugLog.e("Exo Player Exception", "DASH is not enabled!");
throw new IllegalStateException("DASH is not enabled!");
}
mediaSourceFactory = new DashMediaSource.Factory(
new DefaultDashChunkSource.Factory(mediaDataSourceFactory),
- buildDataSourceFactory(false));
+ buildDataSourceFactory(false)
+ );
break;
case CONTENT_TYPE_HLS:
if (!BuildConfig.USE_EXOPLAYER_HLS) {
@@ -1113,14 +1087,13 @@ public class ReactExoplayerView extends FrameLayout implements
}
mediaSourceFactory = new HlsMediaSource.Factory(
- dataSourceFactory)
- .setAllowChunklessPreparation(source.getTextTracksAllowChunklessPreparation());
+ dataSourceFactory
+ ).setAllowChunklessPreparation(source.getTextTracksAllowChunklessPreparation());
break;
case CONTENT_TYPE_OTHER:
if ("asset".equals(uri.getScheme())) {
try {
- DataSource.Factory assetDataSourceFactory = DataSourceUtil
- .buildAssetDataSourceFactory(themedReactContext, uri);
+ DataSource.Factory assetDataSourceFactory = DataSourceUtil.buildAssetDataSourceFactory(themedReactContext, uri);
mediaSourceFactory = new ProgressiveMediaSource.Factory(assetDataSourceFactory);
} catch (Exception e) {
throw new IllegalStateException("cannot open input file:" + uri);
@@ -1128,10 +1101,12 @@ public class ReactExoplayerView extends FrameLayout implements
} else if ("file".equals(uri.getScheme()) ||
!useCache) {
mediaSourceFactory = new ProgressiveMediaSource.Factory(
- mediaDataSourceFactory);
+ mediaDataSourceFactory
+ );
} else {
mediaSourceFactory = new ProgressiveMediaSource.Factory(
- RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true)));
+ RNVSimpleCache.INSTANCE.getCacheFactory(buildHttpDataSourceFactory(true))
+ );
}
break;
@@ -1150,19 +1125,20 @@ public class ReactExoplayerView extends FrameLayout implements
if (cmcdConfigurationFactory != null) {
mediaSourceFactory = mediaSourceFactory.setCmcdConfigurationFactory(
- cmcdConfigurationFactory::createCmcdConfiguration);
+ cmcdConfigurationFactory::createCmcdConfiguration
+ );
}
mediaSourceFactory = Objects.requireNonNullElse(
ReactNativeVideoManager.Companion.getInstance()
.overrideMediaSourceFactory(source, mediaSourceFactory, mediaDataSourceFactory),
- mediaSourceFactory);
+ mediaSourceFactory
+ );
mediaItemBuilder.setStreamKeys(streamKeys);
@Nullable
- final MediaItem.Builder overridenMediaItemBuilder = ReactNativeVideoManager.Companion.getInstance()
- .overrideMediaItemBuilder(source, mediaItemBuilder);
+ final MediaItem.Builder overridenMediaItemBuilder = ReactNativeVideoManager.Companion.getInstance().overrideMediaItemBuilder(source, mediaItemBuilder);
MediaItem mediaItem = overridenMediaItemBuilder != null
? overridenMediaItemBuilder.build()
@@ -1171,7 +1147,8 @@ public class ReactExoplayerView extends FrameLayout implements
MediaSource mediaSource = mediaSourceFactory
.setDrmSessionManagerProvider(drmProvider)
.setLoadErrorHandlingPolicy(
- config.buildLoadErrorHandlingPolicy(source.getMinLoadRetryCount()))
+ config.buildLoadErrorHandlingPolicy(source.getMinLoadRetryCount())
+ )
.createMediaSource(mediaItem);
if (cropStartMs >= 0 && cropEndMs >= 0) {
@@ -1206,8 +1183,7 @@ public class ReactExoplayerView extends FrameLayout implements
}
}
- MediaItem.SubtitleConfiguration.Builder configBuilder = new MediaItem.SubtitleConfiguration.Builder(
- track.getUri())
+ MediaItem.SubtitleConfiguration.Builder configBuilder = new MediaItem.SubtitleConfiguration.Builder(track.getUri())
.setId(trackId)
.setMimeType(track.getType())
.setLabel(label)
@@ -1218,8 +1194,7 @@ public class ReactExoplayerView extends FrameLayout implements
configBuilder.setLanguage(track.getLanguage());
}
- // Set selection flags - make first track default if no specific track is
- // selected
+ // Set selection flags - make first track default if no specific track is selected
if (trackIndex == 0 && (textTrackType == null || "disabled".equals(textTrackType))) {
configBuilder.setSelectionFlags(C.SELECTION_FLAG_DEFAULT);
} else {
@@ -1229,12 +1204,10 @@ public class ReactExoplayerView extends FrameLayout implements
MediaItem.SubtitleConfiguration subtitleConfiguration = configBuilder.build();
subtitleConfigurations.add(subtitleConfiguration);
- DebugLog.d(TAG,
- "Created subtitle configuration: " + trackId + " - " + label + " (" + track.getType() + ")");
+ DebugLog.d(TAG, "Created subtitle configuration: " + trackId + " - " + label + " (" + track.getType() + ")");
trackIndex++;
} catch (Exception e) {
- DebugLog.e(TAG,
- "Error creating SubtitleConfiguration for URI " + track.getUri() + ": " + e.getMessage());
+ DebugLog.e(TAG, "Error creating SubtitleConfiguration for URI " + track.getUri() + ": " + e.getMessage());
}
}
@@ -1247,7 +1220,7 @@ public class ReactExoplayerView extends FrameLayout implements
private void releasePlayer() {
if (player != null) {
- if (playbackServiceBinder != null) {
+ if(playbackServiceBinder != null) {
playbackServiceBinder.getService().unregisterPlayer(player);
themedReactContext.unbindService(playbackServiceConnection);
}
@@ -1303,8 +1276,7 @@ public class ReactExoplayerView extends FrameLayout implements
case AudioManager.AUDIOFOCUS_LOSS:
view.hasAudioFocus = false;
view.eventEmitter.onAudioFocusChanged.invoke(false);
- // FIXME this pause can cause issue if content doesn't have pause capability
- // (can happen on live channel)
+ // FIXME this pause can cause issue if content doesn't have pause capability (can happen on live channel)
if (activity != null) {
activity.runOnUiThread(view::pausePlayback);
}
@@ -1325,12 +1297,16 @@ public class ReactExoplayerView extends FrameLayout implements
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume
if (!view.muted) {
- activity.runOnUiThread(() -> view.player.setVolume(view.audioVolume * 0.8f));
+ activity.runOnUiThread(() ->
+ view.player.setVolume(view.audioVolume * 0.8f)
+ );
}
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Raise it back to normal
if (!view.muted) {
- activity.runOnUiThread(() -> view.player.setVolume(view.audioVolume * 1));
+ activity.runOnUiThread(() ->
+ view.player.setVolume(view.audioVolume * 1)
+ );
}
}
}
@@ -1403,8 +1379,7 @@ public class ReactExoplayerView extends FrameLayout implements
/**
* Returns a new DataSource factory.
*
- * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener
- * to the new
+ * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
* DataSource factory.
* @return A new DataSource factory.
*/
@@ -1416,14 +1391,12 @@ public class ReactExoplayerView extends FrameLayout implements
/**
* Returns a new HttpDataSource factory.
*
- * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener
- * to the new
- * DataSource factory.
+ * @param useBandwidthMeter Whether to set {@link #bandwidthMeter} as a listener to the new
+ * DataSource factory.
* @return A new HttpDataSource factory.
*/
private HttpDataSource.Factory buildHttpDataSourceFactory(boolean useBandwidthMeter) {
- return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext,
- useBandwidthMeter ? bandwidthMeter : null, source.getHeaders());
+ return DataSourceUtil.getDefaultHttpDataSourceFactory(this.themedReactContext, useBandwidthMeter ? bandwidthMeter : null, source.getHeaders());
}
// AudioBecomingNoisyListener implementation
@@ -1440,13 +1413,11 @@ public class ReactExoplayerView extends FrameLayout implements
@Override
public void onEvents(@NonNull Player player, Player.Events events) {
- if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)
- || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
+ if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
int playbackState = player.getPlaybackState();
boolean playWhenReady = player.getPlayWhenReady();
String text = "onStateChanged: playWhenReady=" + playWhenReady + ", playbackState=";
- eventEmitter.onPlaybackRateChange
- .invoke(playWhenReady && playbackState == ExoPlayer.STATE_READY ? 1.0f : 0.0f);
+ eventEmitter.onPlaybackRateChange.invoke(playWhenReady && playbackState == ExoPlayer.STATE_READY ? 1.0f : 0.0f);
switch (playbackState) {
case Player.STATE_IDLE:
text += "idle";
@@ -1503,11 +1474,9 @@ public class ReactExoplayerView extends FrameLayout implements
}
/**
- * The progress message handler will duplicate recursions of the
- * onProgressMessage handler
- * on change of player state from any state to STATE_READY with playWhenReady is
- * true (when
- * the video is not paused). This clears all existing messages.
+ * The progress message handler will duplicate recursions of the onProgressMessage handler
+ * on change of player state from any state to STATE_READY with playWhenReady is true (when
+ * the video is not paused). This clears all existing messages.
*/
private void clearProgressMessageHandler() {
progressHandler.removeMessages(SHOW_PROGRESS);
@@ -1526,8 +1495,7 @@ public class ReactExoplayerView extends FrameLayout implements
setSelectedTextTrack(textTrackType, textTrackValue);
}
Format videoFormat = player.getVideoFormat();
- boolean isRotatedContent = videoFormat != null
- && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
+ boolean isRotatedContent = videoFormat != null && (videoFormat.rotationDegrees == 90 || videoFormat.rotationDegrees == 270);
int width = videoFormat != null ? (isRotatedContent ? videoFormat.height : videoFormat.width) : 0;
int height = videoFormat != null ? (isRotatedContent ? videoFormat.width : videoFormat.height) : 0;
String trackId = videoFormat != null ? videoFormat.id : null;
@@ -1536,19 +1504,18 @@ public class ReactExoplayerView extends FrameLayout implements
long duration = player.getDuration();
long currentPosition = player.getCurrentPosition();
ArrayList