Skip to content

Commit

Permalink
Merge branch 'development' into add-docs-generation-workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandkakonyi committed Oct 9, 2023
2 parents 9339f61 + 1802917 commit f831f4c
Show file tree
Hide file tree
Showing 27 changed files with 247 additions and 132 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ concurrency:
cancel-in-progress: true

jobs:
code-style-android:
name: Code style Android
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '11'

- name: Set up Gradle cache
uses: gradle/gradle-build-action@v2
with:
cache-read-only: ${{ github.ref != 'refs/heads/development' }}

- name: Check code style
run: ./gradlew ktlintCheck
working-directory: android

test-build-typescript:
name: Build Typescript
runs-on: ubuntu-latest
Expand Down
74 changes: 52 additions & 22 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,6 @@ Before creating a pull request, please
- Make sure all guidelines are followed
- Make sure your branch is free of merge conflicts

## TypeScript Code Style

- Follow the `eslint` rules (`yarn lint`). They are inforced automatically via a pre-commit git hook.
- Always add return values to functions (even if `void`)
- No unused imports
- Public functions should be documented with a description that explains _what_ it does
- Every code block that does not obviously explain itself should be commented with an explanation of _why_ and _what_ it does

## Development workflow

To get started with the project, run `yarn bootstrap` in the root directory to install the required dependencies for each package and cocoapods dependencies for the example app:
Expand Down Expand Up @@ -49,6 +41,27 @@ To build and run the example app on iOS:
yarn example ios
```

To edit the Swift/Objective-C files, open `example/ios/BitmovinPlayerReactNativeExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > RNBitmovinPlayer`.

To edit the Kotlin files, open `example/android` in Android Studio and find the source files at `bitmovin-player-react-native` under `Android`.

## TypeScript Code Style

- Follow the `eslint` rules (`yarn lint`). They are inforced automatically via a pre-commit git hook.
- Always add return values to functions (even if `void`)
- No unused imports
- Public functions should be documented with a description that explains _what_ it does
- Every code block that does not obviously explain itself should be commented with an explanation of _why_ and _what_ it does

## Linting

### Typescript

[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)

We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.

Our pre-commit hooks verify that the linter and tests pass when committing.
Make sure your code passes TypeScript and ESLint. Run the following to verify:

```sh
Expand All @@ -62,17 +75,42 @@ To fix formatting errors, run the following:
yarn lint --fix
```

Remember to add tests for your change if possible. Run the unit tests by:
### Kotlin

For Kotlin code [ktlint](https://pinterest.github.io/ktlint/) is used with [ktlint gradle plugin](https://github.com/jlleitschuh/ktlint-gradle).
Run the following inside `android` folder to verify code format:

```sh
yarn test
./gradlew ktlintCheck
```

To edit the Swift/Objective-C files, open `example/ios/BitmovinPlayerReactNativeExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > RNBitmovinPlayer`.
To fix formatting errors, run the following inside `android` folder:

To edit the Kotlin files, open `example/android` in Android Studio and find the source files at `bitmovin-player-react-native` under `Android`.
```sh
./gradlew ktlintFormat
```

You can add a lint check pre-commit hook by running inside `android` folder:

```sh
./gradlew addKtlintCheckGitPreCommitHook
```

and for automatic pre-commit formatting:

```sh
./gradlew addKtlintFormatGitPreCommitHook
```

### Commit message convention
## Testing

Remember to add tests for your change if possible. Run the unit tests by:

```sh
yarn test
```

## Commit message convention

We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages:

Expand All @@ -85,15 +123,7 @@ We follow the [conventional commits specification](https://www.conventionalcommi

Our pre-commit hooks verify that your commit message matches this format when committing.

### Linting and tests

[ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/)

We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing.

Our pre-commit hooks verify that the linter and tests pass when committing.

### Scripts
## Scripts

The `package.json` file contains various scripts for common tasks:

Expand Down
2 changes: 1 addition & 1 deletion RNBitmovinPlayer.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m,mm,swift}"

s.dependency "React-Core"
s.dependency "BitmovinPlayer", "3.45.0"
s.dependency "BitmovinPlayer", "3.46.0"
s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.18.4"
s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.8.2"
end
8 changes: 8 additions & 0 deletions android/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[*.{kt,kts}]
ktlint_code_style=android_studio
max_line_length=120
# Allow wildcard imports for react native bridge, since IntelliJ does this automatically when performing
# imports optimization.
ij_kotlin_packages_to_use_import_on_demand = com.facebook.react.bridge.*
ij_kotlin_allow_trailing_comma=true
ij_kotlin_allow_trailing_comma_on_call_site=true
6 changes: 6 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ buildscript {
ext {
kotlinVersion = '1.7.21'
androidToolsVersion = '7.4.2'
ktlintVersion = '11.6.0'
}
repositories {
google()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "com.android.tools.build:gradle:$androidToolsVersion"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "org.jlleitschuh.gradle:ktlint-gradle:$ktlintVersion"
}
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'org.jlleitschuh.gradle.ktlint'

repositories {
mavenLocal()
Expand Down
Binary file added android/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions android/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class BitmovinCastManagerModule(
uiManager?.addUIBlock {
BitmovinCastManager.initialize(
castOptions?.applicationId,
castOptions?.messageNamespace
castOptions?.messageNamespace,
)
promise.resolve(null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class DrmModule(private val context: ReactApplicationContext) : ReactContextBase
nativeId,
"onPrepareMessage",
preparedMessages,
preparedMessagesCondition
preparedMessagesCondition,
)
widevineConfig.prepareMessageCallback = PrepareMessageCallback {
prepareMessage(it)
Expand All @@ -151,7 +151,7 @@ class DrmModule(private val context: ReactApplicationContext) : ReactContextBase
nativeId,
"onPrepareLicense",
preparedLicenses,
preparedLicensesCondition
preparedLicensesCondition,
)
widevineConfig.prepareLicenseCallback = PrepareLicenseCallback {
prepareLicense(it)
Expand All @@ -170,11 +170,11 @@ class DrmModule(private val context: ReactApplicationContext) : ReactContextBase
nativeId: NativeId,
method: String,
registry: Registry<String>,
registryCondition: Condition
registryCondition: Condition,
): PrepareCallback = {
val args = Arguments.createArray()
args.pushString(Base64.encodeToString(it, Base64.NO_WRAP))
context.catalystInstance.callFunction("DRM-${nativeId}", method, args as NativeArray)
context.catalystInstance.callFunction("DRM-$nativeId", method, args as NativeArray)
lock.withLock {
registryCondition.await()
val result = registry[nativeId]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class EventRelay<E : EventEmitter<T>, T : Event>(
/**
* Is called for every relayed event together with its associated name.
*/
private val eventOutput: (String, Event) -> Unit
private val eventOutput: (String, Event) -> Unit,
) {
private val eventListeners = forwardingEventClassesAndNameMapping.map {
Subscription(it.key) { event -> eventOutput(it.value, event) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.bitmovin.player.reactnative
import com.bitmovin.player.api.offline.options.OfflineOptionEntryState
import com.bitmovin.player.reactnative.converter.JsonConverter
import com.bitmovin.player.reactnative.extensions.toList
import com.bitmovin.player.reactnative.offline.OfflineDownloadRequest
import com.bitmovin.player.reactnative.offline.OfflineContentManagerBridge
import com.bitmovin.player.reactnative.offline.OfflineDownloadRequest
import com.facebook.react.bridge.*
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.UIManagerModule
Expand Down Expand Up @@ -69,7 +69,13 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
return@addUIBlock
}

offlineContentManagerBridges[nativeId] = OfflineContentManagerBridge(nativeId, context, identifier, sourceConfig, context.cacheDir.path)
offlineContentManagerBridges[nativeId] = OfflineContentManagerBridge(
nativeId,
context,
identifier,
sourceConfig,
context.cacheDir.path,
)
}
promise.resolve(null)
}
Expand Down Expand Up @@ -117,7 +123,8 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
return@safeOfflineContentManager
}
OfflineOptionEntryState.Downloading,
OfflineOptionEntryState.Failed -> {
OfflineOptionEntryState.Failed,
-> {
promise.reject(IllegalStateException("Download already in progress"))
return@safeOfflineContentManager
}
Expand All @@ -127,7 +134,7 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
}
else -> {}
}
val minimumBitRate = if(request.hasKey("minimumBitrate")) request.getInt("minimumBitrate") else null
val minimumBitRate = if (request.hasKey("minimumBitrate")) request.getInt("minimumBitrate") else null
if (minimumBitRate != null && minimumBitRate < 0) {
promise.reject(IllegalArgumentException("Invalid download request"))
return@safeOfflineContentManager
Expand All @@ -136,7 +143,9 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
val audioOptionIds = request.getArray("audioOptionIds")?.toList<String>()?.filterNotNull()
val textOptionIds = request.getArray("textOptionIds")?.toList<String>()?.filterNotNull()

getOfflineContentManagerBridge(nativeId)?.process(OfflineDownloadRequest(minimumBitRate, audioOptionIds, textOptionIds))
getOfflineContentManagerBridge(nativeId)?.process(
OfflineDownloadRequest(minimumBitRate, audioOptionIds, textOptionIds),
)
promise.resolve(null)
} catch (e: Exception) {
promise.reject(e)
Expand Down Expand Up @@ -260,20 +269,22 @@ class OfflineModule(private val context: ReactApplicationContext) : ReactContext
}
}

private fun safeOfflineContentManager(nativeId: NativeId, promise: Promise, runBlock: OfflineContentManagerBridge.() -> Unit) {
private fun safeOfflineContentManager(
nativeId: NativeId,
promise: Promise,
runBlock: OfflineContentManagerBridge.() -> Unit,
) {
getOfflineContentManagerBridge(nativeId)?.let(runBlock)
?: promise.reject(IllegalArgumentException("Could not find the offline module instance"))
?: promise.reject(IllegalArgumentException("Could not find the offline module instance"))
}

/**
* Helper function that returns the initialized `DrmModule` instance.
*/
private fun drmModule(): DrmModule? =
context.getNativeModule(DrmModule::class.java)
private fun drmModule(): DrmModule? = context.getNativeModule(DrmModule::class.java)

/**
* Helper function that returns the initialized `UIManager` instance.
*/
private fun uiManager(): UIManagerModule? =
context.getNativeModule(UIManagerModule::class.java)
private fun uiManager(): UIManagerModule? = context.getNativeModule(UIManagerModule::class.java)
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ class PlayerModule(private val context: ReactApplicationContext) : ReactContextB
}
val playerConfig = JsonConverter.toPlayerConfig(playerConfigJson)
val analyticsConfig = JsonConverter.toAnalyticsConfig(analyticsConfigJson)
val defaultMetadata = JsonConverter.toAnalyticsDefaultMetadata(analyticsConfigJson?.getMap("defaultMetadata"))
val defaultMetadata = JsonConverter.toAnalyticsDefaultMetadata(
analyticsConfigJson?.getMap("defaultMetadata"),
)

players[nativeId] = if (analyticsConfig == null) {
Player.create(context, playerConfig)
Expand Down Expand Up @@ -481,7 +483,9 @@ class PlayerModule(private val context: ReactApplicationContext) : ReactContextB
@ReactMethod
fun setMaxSelectableBitrate(nativeId: NativeId, maxSelectableBitrate: Int) {
uiManager()?.addUIBlock {
players[nativeId]?.setMaxSelectableVideoBitrate(maxSelectableBitrate.takeUnless { it == -1 } ?: Integer.MAX_VALUE)
players[nativeId]?.setMaxSelectableVideoBitrate(
maxSelectableBitrate.takeUnless { it == -1 } ?: Integer.MAX_VALUE,
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING_UI = mapOf<KClass<out Event
* exposes player events as bubbling events.
*/
@SuppressLint("ViewConstructor")
class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context),
LifecycleEventListener, View.OnLayoutChangeListener, RNPictureInPictureDelegate {
class RNPlayerView(val context: ReactApplicationContext) :
LinearLayout(context),
LifecycleEventListener,
View.OnLayoutChangeListener,
RNPictureInPictureDelegate {

init {
// React Native has a bug that dynamically added views sometimes aren't laid out again properly.
Expand All @@ -109,6 +112,7 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
* to the `eventOutput` callback.
*/
private val playerEventRelay = EventRelay<Player, Event>(EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING, ::emitEvent)

/**
* Relays the provided set of events, emitted by the player view, together with the associated name
* to the `eventOutput` callback.
Expand Down Expand Up @@ -273,7 +277,7 @@ class RNPlayerView(val context: ReactApplicationContext) : LinearLayout(context)
post {
measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY),
)
layout(left, top, right, bottom)
}
Expand Down
Loading

0 comments on commit f831f4c

Please sign in to comment.