Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception are now always caught, remove compile time check #345

Open
wants to merge 2 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.bitmovin.player.reactnative.extensions.offlineModule
import com.bitmovin.player.reactnative.extensions.playerModule
import com.bitmovin.player.reactnative.extensions.sourceModule
import com.bitmovin.player.reactnative.extensions.uiManagerModule
import com.bitmovin.player.reactnative.offline.OfflineContentManagerBridge
import com.facebook.react.bridge.*
import com.facebook.react.uimanager.UIManagerModule

Expand All @@ -30,69 +31,80 @@ abstract class BitmovinBaseModule(
* Runs [block] on the UI thread with [UIManagerModule.addUIBlock] and [TPromise.resolve] [this] with
* its return value. If [block] throws, [Promise.reject] [this] with the [Throwable].
*/
protected inline fun <T, R : T> TPromise<T>.resolveOnUiThread(
crossinline block: RejectPromiseOnExceptionBlock.() -> R,
) {
protected inline fun <T, R : T> TPromise<T>.resolveOnUiThread(crossinline block: () -> R) {
val uiManager = runAndRejectOnException { uiManager } ?: return
uiManager.addUIBlock {
resolveOnCurrentThread { block() }
}
}

protected val RejectPromiseOnExceptionBlock.playerModule: PlayerModule get() = context.playerModule
protected val playerModule: PlayerModule get() = context.playerModule
?: throw IllegalArgumentException("PlayerModule not found")

protected val RejectPromiseOnExceptionBlock.uiManager: UIManagerModule get() = context.uiManagerModule
protected val uiManager: UIManagerModule get() = context.uiManagerModule
?: throw IllegalStateException("UIManager not found")

protected val RejectPromiseOnExceptionBlock.sourceModule: SourceModule get() = context.sourceModule
protected val sourceModule: SourceModule get() = context.sourceModule
?: throw IllegalStateException("SourceModule not found")

protected val RejectPromiseOnExceptionBlock.offlineModule: OfflineModule get() = context.offlineModule
protected val offlineModule: OfflineModule get() = context.offlineModule
?: throw IllegalStateException("OfflineModule not found")

protected val RejectPromiseOnExceptionBlock.drmModule: DrmModule get() = context.drmModule
protected val drmModule: DrmModule get() = context.drmModule
?: throw IllegalStateException("DrmModule not found")

fun RejectPromiseOnExceptionBlock.getPlayer(
fun getPlayer(
nativeId: NativeId,
playerModule: PlayerModule = this.playerModule,
): Player = playerModule.getPlayerOrNull(nativeId) ?: throw IllegalArgumentException("Invalid PlayerId $nativeId")

fun RejectPromiseOnExceptionBlock.getSource(
fun getSource(
nativeId: NativeId,
sourceModule: SourceModule = this.sourceModule,
): Source = sourceModule.getSourceOrNull(nativeId) ?: throw IllegalArgumentException("Invalid SourceId $nativeId")
}

/** Run [block], returning it's return value. If [block] throws, [Promise.reject] [this] and return null. */
inline fun <T, R> TPromise<T>.runAndRejectOnException(block: RejectPromiseOnExceptionBlock.() -> R): R? = try {
RejectPromiseOnExceptionBlock.block()
} catch (e: Exception) {
reject(e)
null
fun getOfflineContentManagerBridge(
nativeId: NativeId,
offlineModule: OfflineModule = this.offlineModule,
): OfflineContentManagerBridge = offlineModule.getOfflineContentManagerBridgeOrNull(nativeId)
?: throw IllegalArgumentException("Invalid offline content manager bridge id: $nativeId")
}

/**
* [TPromise.resolve] [this] with [block] return value.
* If [block] throws, [Promise.reject] [this] with the [Throwable].
*/
inline fun <T> TPromise<T>.resolveOnCurrentThread(
crossinline block: RejectPromiseOnExceptionBlock.() -> T,
): Unit = runAndRejectOnException { [email protected](block()) } ?: Unit

/** Receiver of code that can safely throw when resolving a [Promise]. */
object RejectPromiseOnExceptionBlock

/** Compile time wrapper for Promises to type check the resolved type [T]. */
@JvmInline
value class TPromise<T>(val promise: Promise) {
/**
* Resolve the promise with [value], see [Promise.resolve].
*
krocard marked this conversation as resolved.
Show resolved Hide resolved
* Prefer [resolveOnCurrentThread] to automatically reject promise if an Exception is thrown.
*/
// Promise only support built-in types. Functions that return [Unit] must resolve to `null`.
fun resolve(value: T): Unit = promise.resolve(value.takeUnless { it is Unit })

/**
* Reject the promise due to [throwable], see [Promise.reject].
* Prefer [resolveOnCurrentThread] or [runAndRejectOnException] instead for automatic catching.
krocard marked this conversation as resolved.
Show resolved Hide resolved
*/
fun reject(throwable: Throwable) {
Log.e(MODULE_NAME, "Failed to execute Bitmovin method", throwable)
promise.reject(throwable)
}

/**
* [TPromise.resolve] with [block] return value.
* If [block] throws, [Promise.reject] with the [Throwable].
*/
inline fun resolveOnCurrentThread(
crossinline block: () -> T,
): Unit = runAndRejectOnException { resolve(block()) } ?: Unit

/** Run [block], returning it's return value. If [block] throws, [Promise.reject] and return null. */
inline fun <R> runAndRejectOnException(block: () -> R): R? = try {
block()
} catch (e: Exception) {
reject(e)
null
}
}

inline val Promise.int get() = TPromise<Int>(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
nativeId: NativeId,
): OfflineContentManagerBridge? = offlineContentManagerBridges[nativeId]

private fun RejectPromiseOnExceptionBlock.getOfflineContentManagerBridge(
nativeId: NativeId,
): OfflineContentManagerBridge = offlineContentManagerBridges[nativeId]
?: throw IllegalArgumentException("No offline content manager bridge for id $nativeId")

/**
* Callback when a new NativeEventEmitter is created from the Typescript layer.
*/
Expand Down Expand Up @@ -242,7 +237,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
crossinline block: OfflineContentManagerBridge.() -> T,
) {
resolveOnCurrentThread {
getOfflineContentManagerBridge(nativeId).block()
getOfflineContentManagerBridge(nativeId, this@OfflineModule).block()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class PlayerAnalyticsModule(context: ReactApplicationContext) : BitmovinBaseModu
playerId: NativeId,
crossinline block: AnalyticsApi.() -> T,
) = resolveOnUiThread {
val analytics = getPlayer(playerId).analytics ?: throw IllegalStateException("Analytics is disabled")
val analytics = getPlayer(playerId).analytics
?: throw IllegalStateException("Analytics is disabled for player $playerId")
analytics.block()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ class PlayerModule(context: ReactApplicationContext) : BitmovinBaseModule(contex
*/
@ReactMethod
fun loadSource(nativeId: NativeId, sourceNativeId: String, promise: Promise) {
promise.unit.resolveOnUiThread {
getPlayer(nativeId, this@PlayerModule).load(getSource(sourceNativeId))
promise.unit.resolveOnUiThreadWithPlayer(nativeId) {
load(getSource(sourceNativeId))
}
}

Expand All @@ -108,12 +108,10 @@ class PlayerModule(context: ReactApplicationContext) : BitmovinBaseModule(contex
options: ReadableMap?,
promise: Promise,
) {
promise.unit.resolveOnUiThread {
offlineModule
.getOfflineContentManagerBridgeOrNull(offlineContentManagerBridgeId)
?.offlineContentManager
?.offlineSourceConfig
?.let { getPlayer(nativeId).load(it) }
promise.unit.resolveOnUiThreadWithPlayer(nativeId) {
val offlineContentManagerBridge = getOfflineContentManagerBridge(offlineContentManagerBridgeId)
val offlineSourceConfig = offlineContentManagerBridge.offlineContentManager.offlineSourceConfig
load(offlineSourceConfig ?: throw IllegalStateException("Offline source has no config"))
}
}

Expand Down