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

Refactor Kotlin code to improve error handling #317

Merged
merged 50 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
373b236
refactor: improve null safety
krocard Nov 8, 2023
a2e29de
refactor: report errors instead of ignoring invalid states
krocard Nov 9, 2023
e0f0244
refactor: move helpers in base module
krocard Nov 10, 2023
5fc530a
refactor: reformat
krocard Nov 10, 2023
09db8f3
fixup! method not called toJson
krocard Nov 10, 2023
2cb51f8
refactor: use getPlayer instead of direct map access
krocard Nov 10, 2023
46a7535
refactor: introduce module accessors
krocard Nov 13, 2023
14fc208
refactor: rename addUIBlock
krocard Nov 13, 2023
b39491e
refactor: introduce throw safe block
krocard Nov 13, 2023
f2ff634
fix: remove extra promise.resolve
krocard Nov 13, 2023
f8e3611
refactor: compile time safe getPlayer
krocard Nov 13, 2023
899d4f0
refactor: add withArray
krocard Nov 13, 2023
f60caee
chore: add documentation
krocard Nov 13, 2023
7b81d83
refactor: make map&array helpers type safe
krocard Nov 13, 2023
508caaa
refactor: cast manager module
krocard Nov 13, 2023
ce3d5b8
chore: prepare release 0.14.0
Nov 14, 2023
5cd38c8
Remove unnecessary dot from CHANGELOG.md
123mpozzi Nov 14, 2023
6883489
Merge pull request #321 from bitmovin/release/v0.14.0
123mpozzi Nov 14, 2023
9c86447
fix: don't return `Unit`
krocard Nov 14, 2023
c969a41
fix: remove unnecessary promise resolve
krocard Nov 14, 2023
598d8bf
fix: factorize with* methods
krocard Nov 14, 2023
efe8065
refactor: move helper method to extension
krocard Nov 14, 2023
d3c9572
refactor: rename PromiseRejectOnExceptionBlock to RejectPromiseOnExce…
krocard Nov 14, 2023
a6528bb
refactor: migrate SourceModule to BitmovinBaseModule
krocard Nov 14, 2023
ef81c51
Merge remote-tracking branch 'origin/main' into refactor--improve-nul…
krocard Nov 14, 2023
fe244b9
refactor: migrate offline module
krocard Nov 17, 2023
9f8abdb
refactor: migrate drm module
krocard Nov 17, 2023
3fd0906
refactor: remove duplicated promise.resolve
krocard Nov 17, 2023
edd2bfa
refactor: remove duplicated promise.resolve
krocard Nov 17, 2023
2aff015
fix: allow duration not propagated
krocard Nov 22, 2023
8d1d05c
fix: don't resolve Unit
krocard Nov 22, 2023
d733d09
refactor: introduce a type safe promise
krocard Nov 29, 2023
4e6f164
fix: code formating
krocard Nov 29, 2023
cb07f50
refactor: prefer early return on failure
krocard Dec 1, 2023
8773de7
fix: reject invalid command
krocard Dec 1, 2023
c30b6fe
fix: formating
krocard Dec 1, 2023
6231fd9
refactor: improve documentation
krocard Dec 1, 2023
f32a92f
fix: reject if analytics is disable
krocard Dec 4, 2023
4373ab6
refactor: mark noop function as inline
krocard Dec 4, 2023
08ab485
fix: made all array non nullable (empty on null)
krocard Dec 4, 2023
2c0d887
fix: serializing methods
krocard Dec 4, 2023
779b1e3
fix: don't skip player creation if config is missing
krocard Dec 4, 2023
d77c955
fix: don't send scaling mode if undefined
krocard Dec 6, 2023
4994562
fix: rename&use runOnMainLooperAndLogException
krocard Dec 6, 2023
d36c14d
fix: log invalid playerId
krocard Dec 6, 2023
ff1148c
fix: error message
krocard Dec 6, 2023
d272236
refactor: avoid creating a handler for each request
krocard Dec 6, 2023
e035a36
refactor: use single line function
krocard Dec 6, 2023
653fcd2
refactor: invalid ref in kdoc
krocard Dec 6, 2023
ae6f3da
fix: inverted condition breaking PiP
krocard Dec 7, 2023
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
matamegger marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bitmovin.player.reactnative

import com.bitmovin.player.api.Player
import com.bitmovin.player.api.source.Source
import com.bitmovin.player.reactnative.extensions.drmModule
import com.bitmovin.player.reactnative.extensions.offlineModule
import com.bitmovin.player.reactnative.extensions.playerModule
Expand All @@ -22,13 +23,11 @@ import com.facebook.react.uimanager.UIManagerModule
abstract class BitmovinBaseModule(
protected val context: ReactApplicationContext,
) : ReactContextBaseJavaModule(context) {
/** [resolve] the [Promise] by running [block] in the UI thread with [UIManagerModule.addUIBlock]. */
protected inline fun <T> Promise.resolveOnUIThread(crossinline block: RejectPromiseOnExceptionBlock.() -> T) {
/** [resolve] the [TPromise] by running [block] in the UI thread with [UIManagerModule.addUIBlock]. */
protected inline fun <T, R : T> TPromise<T>.resolveOnUiThread(crossinline block: RejectPromiseOnExceptionBlock.() -> R) {
val uiManager = runAndRejectOnException { uiManager } ?: return
uiManager.addUIBlock {
resolveOnCurrentThread {
resolve(block())
}
resolveOnCurrentThread{ block() }
}
}

Expand All @@ -47,25 +46,48 @@ abstract class BitmovinBaseModule(
protected val RejectPromiseOnExceptionBlock.drmModule: DrmModule get() = context.drmModule
?: throw IllegalStateException("DrmModule not found")

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

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

/** Run [block], forwarding the return value. If it throws, sets [Promise.reject] and return null. */
inline fun <T> Promise.runAndRejectOnException(block: RejectPromiseOnExceptionBlock.() -> T): T? = try {
inline fun <T, R> TPromise<T>.runAndRejectOnException(block: RejectPromiseOnExceptionBlock.() -> R): R? = try {
RejectPromiseOnExceptionBlock.block()
} catch (e: Exception) {
reject(e)
null
}

/** Resolve the [Promise] with the value returned by [block]. If it throws, sets [Promise.reject]. */
inline fun <T> Promise.resolveOnCurrentThread(block: RejectPromiseOnExceptionBlock.() -> T): Unit = try {
// Promise only support built-in types. Functions that return [Unit] must resolve to `null`.
resolve(RejectPromiseOnExceptionBlock.block().takeUnless { it is Unit })
inline fun <T> TPromise<T>.resolveOnCurrentThread(crossinline block: RejectPromiseOnExceptionBlock.() -> T): Unit = try {
resolve(RejectPromiseOnExceptionBlock.block())
} catch (e: Exception) {
reject(e)
}

/** 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) {
// 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 })
fun reject(throwable: Throwable) = promise.reject(throwable)
}
val Promise.int get() = TPromise<Int>(this)
val Promise.unit get() = TPromise<Unit>(this)
val Promise.string get() = TPromise<String>(this)
val Promise.double get() = TPromise<Double>(this)
val Promise.float get() = TPromise<Float>(this)
val Promise.bool get() = TPromise<Boolean>(this)
val Promise.map get() = TPromise<ReadableMap>(this)
val Promise.array get() = TPromise<ReadableArray>(this)
val <T> TPromise<T>.nullable get() = TPromise<T?>(promise)
krocard marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ class BitmovinCastManagerModule(context: ReactApplicationContext) : BitmovinBase
* Returns whether the [BitmovinCastManager] is initialized.
*/
@ReactMethod
fun isInitialized(promise: Promise) = promise.resolveOnUIThread {
fun isInitialized(promise: Promise) = promise.unit.resolveOnUiThread {
BitmovinCastManager.isInitialized()
}

/**
* Initializes the [BitmovinCastManager] with the given options.
*/
@ReactMethod
fun initializeCastManager(options: ReadableMap?, promise: Promise) = promise.resolveOnUIThread {
fun initializeCastManager(options: ReadableMap?, promise: Promise) = promise.unit.resolveOnUiThread {
val castOptions = options?.toCastOptions()
BitmovinCastManager.initialize(
castOptions?.applicationId,
Expand All @@ -38,15 +38,15 @@ class BitmovinCastManagerModule(context: ReactApplicationContext) : BitmovinBase
* Sends a message to the receiver.
*/
@ReactMethod
fun sendMessage(message: String, messageNamespace: String?, promise: Promise) = promise.resolveOnUIThread {
fun sendMessage(message: String, messageNamespace: String?, promise: Promise) = promise.unit.resolveOnUiThread {
BitmovinCastManager.getInstance().sendMessage(message, messageNamespace)
}

/**
* Updates the context of the [BitmovinCastManager] to the current activity.
*/
@ReactMethod
fun updateContext(promise: Promise) = promise.resolveOnUIThread {
fun updateContext(promise: Promise) = promise.unit.resolveOnUiThread {
BitmovinCastManager.getInstance().updateContext(currentActivity)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class BufferModule(context: ReactApplicationContext) : BitmovinBaseModule(contex
*/
@ReactMethod
fun getLevel(nativeId: NativeId, type: String, promise: Promise) {
promise.resolveOnUIThread {
promise.map.resolveOnUiThread {
val player = getPlayer(nativeId)
val bufferType = type.toBufferTypeOrThrow()
RNBufferLevels(
Expand All @@ -40,7 +40,7 @@ class BufferModule(context: ReactApplicationContext) : BitmovinBaseModule(contex
*/
@ReactMethod
fun setTargetLevel(nativeId: NativeId, type: String, value: Double, promise: Promise) {
promise.resolveOnUIThread {
promise.unit.resolveOnUiThread {
getPlayer(nativeId).buffer.setTargetLevel(type.toBufferTypeOrThrow(), value)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class DrmModule(context: ReactApplicationContext) : BitmovinBaseModule(context)
*/
@ReactMethod
fun initWithConfig(nativeId: NativeId, config: ReadableMap, promise: Promise) {
promise.resolveOnUIThread {
promise.unit.resolveOnUiThread {
if (drmConfigs.containsKey(nativeId)) {
throw InvalidParameterException("NativeId already exists $nativeId")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun initWithConfig(nativeId: NativeId, config: ReadableMap?, drmNativeId: NativeId?, promise: Promise) {
promise.resolveOnUIThread {
promise.unit.resolveOnUiThread {
if (offlineContentManagerBridges.containsKey(nativeId)) {
throw InvalidParameterException("content manager bridge id already exists: $nativeId")
}
Expand All @@ -84,7 +84,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte

@ReactMethod
fun getState(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.string.resolveWithBridge(nativeId) {
state.name
}
}
Expand All @@ -96,7 +96,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun getOptions(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
getOptions()
}
}
Expand All @@ -110,7 +110,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun download(nativeId: NativeId, request: ReadableMap, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
when (state) {
OfflineOptionEntryState.Downloaded -> throw IllegalStateException("Download already completed")
OfflineOptionEntryState.Downloading, OfflineOptionEntryState.Failed -> throw IllegalStateException(
Expand All @@ -135,7 +135,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun resume(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
resume()
}
}
Expand All @@ -146,7 +146,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun suspend(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
suspend()
}
}
Expand All @@ -157,7 +157,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun cancelDownload(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
cancelDownload()
}
}
Expand All @@ -168,7 +168,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun usedStorage(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.double.resolveWithBridge(nativeId) {
offlineContentManager.usedStorage.toDouble()
}
}
Expand All @@ -179,7 +179,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun deleteAll(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
deleteAll()
}
}
Expand All @@ -192,7 +192,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun downloadLicense(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
downloadLicense()
}
}
Expand All @@ -205,7 +205,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun releaseLicense(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
releaseLicense()
}
}
Expand All @@ -218,7 +218,7 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun renewOfflineLicense(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
renewOfflineLicense()
}
}
Expand All @@ -231,18 +231,18 @@ class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(conte
*/
@ReactMethod
fun release(nativeId: NativeId, promise: Promise) {
promise.resolveWithBridge(nativeId) {
promise.unit.resolveWithBridge(nativeId) {
release()
offlineContentManagerBridges.remove(nativeId)
}
}

private fun <T>Promise.resolveWithBridge(
private inline fun <T> TPromise<T>.resolveWithBridge(
nativeId: NativeId,
runBlock: OfflineContentManagerBridge.() -> T,
crossinline block: OfflineContentManagerBridge.() -> T,
) {
resolveOnCurrentThread {
getOfflineContentManagerBridge(nativeId).runBlock()
getOfflineContentManagerBridge(nativeId).block()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class PlayerAnalyticsModule(context: ReactApplicationContext) : BitmovinBaseModu
*/
@ReactMethod
fun sendCustomDataEvent(nativeId: NativeId, json: ReadableMap, promise: Promise) {
promise.resolveOnUIThread {
promise.unit.resolveOnUiThread {
getPlayer(nativeId).analytics?.sendCustomDataEvent(json.toAnalyticsCustomData())
}
}
Expand All @@ -34,7 +34,7 @@ class PlayerAnalyticsModule(context: ReactApplicationContext) : BitmovinBaseModu
*/
@ReactMethod
fun getUserId(playerId: NativeId, promise: Promise) {
promise.resolveOnUIThread {
promise.string.nullable.resolveOnUiThread {
matamegger marked this conversation as resolved.
Show resolved Hide resolved
getPlayer(playerId).analytics?.userId
}
}
Expand Down
Loading
Loading