diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..9e3a702 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,27 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +jobs: + build: + name: Run tests + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: '17' + - name: Run unit tests + run: ./gradlew testDebugUnitTest + - name: Display unit test results + uses: mikepenz/action-junit-report@v4 + if: always() + with: + check_name: 'Unit tests: Results' + report_paths: '**/build/test-results/testDebugUnitTest/TEST-*.xml' diff --git a/CHANGELOG.md b/CHANGELOG.md index b1aa118..981a938 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,74 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -## 2.2.1 - 2023-08-10 +### Added +- Support for reporting the stack trace in case of error events, if provided by the player + +## 2.7.2 - 2024-10-28 + +### Added +- Default value of `false` for `c3.ad.isSlate` for client side ad insertion per Conviva Custom Ad Manager integration docs + +## 2.7.1 - 2024-09-24 +### Fixed +- Reporting wrong ad position for mid-roll VMAP ads + +## 2.7.0 - 2024-09-05 +### Fixed +- Potential integration error shown in Touchstone if the player emits a warning outside of an active Conviva session + +### Changed +- Updated Bitmovin Player to `3.81.0` + +## 2.6.0 - 2024-08-28 +### Added +- `averageBitrate` to reported video metrics +- Possibility to start session tracking without a `Player` instance + - `ConvivaAnalyticsIntegration(customerKey:config:)` constructor without a `Player` + - `ConvivaAnalyticsIntegration.attachPlayer()` to attach the `Player` at a later point in the session life-cycle + +### Removed +- Unintentionally public initializers from `ConvivaAnalyticsIntegration` which were not intended to be public and only meant for testing + +### Changed +- Updated Bitmovin Player to `3.78.2` +- Updated conviva-core to `4.0.39` + +## 2.5.0 - 2024-07-05 +### Added +- `ConvivaAnalyticsIntegration.ssai` namespace to enable server side ad tracking + +### Fixed +- Potential exception when determining the IMA SDK version on ad start + +## 2.4.0 - 2024-06-06 +### Added +- Ad analytics for ad event reporting + +### Changed +- Updated Bitmovin Player to `3.71.0` +- Updated IMA SDK to `3.31.0` +- Updated conviva-core to `4.0.37` +- Increased minimum required `compileSdk` version to `34` +- Increased `compileSdk` and `targetSdkVersion` to `34` +- Increased `minSdkVersion` to `19` +- Ad break started and ended is now reported in `PlayerEvent.AdBreakStarted` and `PlayerEvent.AdBreakFinished` +- Updated Kotlin to `1.9.23` +- Updated Gradle wrapper to `8.2` and AGP to `8.2.2` + +### Removed +- Custom event for `AdSkipped` and `AdError`. Replaced by Conviva build in tracking + +### Fixed +- The pom file now also includes the `com.bitmovin.player` dependency which was missing before + +## 2.3.0 - 2024-05-21 +### Added +- New `TimeChanged` callback for reporting Playhead to conviva playback metric. Calculates Live and Vod playback for report. +- New `MetadataOverrides.setAdditionalStandardTags` that allows to set additional standard tags for the session. The List of tags can be found here: [Pre-defined Video and Content Metadata](https://pulse.conviva.com/learning-center/content/sensor_developer_center/sensor_integration/android/android_stream_sensor.htm#PredefinedVideoandContentMetadata) + ### Changed -- Updated Conviva SDK to 4.0.33 +- Updated conviva-core to 4.0.35 ## 2.2.0 - 2023-07-18 ### Added diff --git a/ConvivaExampleApp/build.gradle b/ConvivaExampleApp/build.gradle index 41eeceb..a7d2d2f 100644 --- a/ConvivaExampleApp/build.gradle +++ b/ConvivaExampleApp/build.gradle @@ -1,7 +1,8 @@ apply plugin: 'com.android.application' android { - compileSdkVersion rootProject.compileSdkVersion + compileSdk rootProject.compileSdkVersion + defaultConfig { applicationId "com.bitmovin.analytics.convivaanalyticsexample" minSdkVersion rootProject.minSdkVersion diff --git a/ConvivaExampleApp/src/main/java/com/bitmovin/analytics/convivaanalyticsexample/MainActivity.java b/ConvivaExampleApp/src/main/java/com/bitmovin/analytics/convivaanalyticsexample/MainActivity.java index 79ddf68..7b31197 100644 --- a/ConvivaExampleApp/src/main/java/com/bitmovin/analytics/convivaanalyticsexample/MainActivity.java +++ b/ConvivaExampleApp/src/main/java/com/bitmovin/analytics/convivaanalyticsexample/MainActivity.java @@ -1,46 +1,56 @@ package com.bitmovin.analytics.convivaanalyticsexample; import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; - +import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.Switch; -import com.bitmovin.analytics.conviva.ConvivaAnalyticsIntegration;; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.bitmovin.analytics.conviva.ConvivaAnalyticsException; +import com.bitmovin.analytics.conviva.ConvivaAnalyticsIntegration; import com.bitmovin.analytics.conviva.ConvivaConfig; import com.bitmovin.analytics.conviva.MetadataOverrides; -import com.bitmovin.player.api.Player; import com.bitmovin.player.PlayerView; -import com.bitmovin.player.api.source.SourceConfig; +import com.bitmovin.player.api.Player; +import com.bitmovin.player.api.PlayerBuilder; import com.bitmovin.player.api.PlayerConfig; import com.bitmovin.player.api.advertising.AdItem; import com.bitmovin.player.api.advertising.AdSource; import com.bitmovin.player.api.advertising.AdSourceType; import com.bitmovin.player.api.advertising.AdvertisingConfig; +import com.bitmovin.player.api.source.SourceConfig; import com.bitmovin.player.api.source.SourceType; import java.util.HashMap; import java.util.Map; public class MainActivity extends AppCompatActivity implements View.OnClickListener { + private static final String TAG = "Example"; + // UI private Button pauseTrackingButton; private Button resumeTrackingButton; private Button releaseButton; private Button createButton; private Button sendCustomEventButton; + private Button startSessionButton; private Switch includeAdsSwitch; // Conviva private static final String customerKey = "YOUR-CUSTOMER-KEY"; private static String gatewayUrl; // Set only in debug mode + @Nullable private ConvivaAnalyticsIntegration convivaAnalyticsIntegration; // Player + @Nullable private Player bitmovinPlayer; + @Nullable private PlayerView bitmovinPlayerView; @Override @@ -53,52 +63,35 @@ protected void onCreate(Bundle savedInstanceState) { resumeTrackingButton.setOnClickListener(this); releaseButton = findViewById(R.id.release_button); releaseButton.setOnClickListener(this); - createButton = findViewById(R.id.create_button); + createButton = findViewById(R.id.create_player_button); createButton.setOnClickListener(this); sendCustomEventButton = findViewById(R.id.custom_event_button); sendCustomEventButton.setOnClickListener(this); + startSessionButton = findViewById(R.id.start_session_button); + startSessionButton.setOnClickListener(this); includeAdsSwitch = findViewById(R.id.include_ads_switch); this.setupBitmovinPlayer(); } protected void setupBitmovinPlayer() { - this.bitmovinPlayer = Player.create(this, buildPlayerConfiguration()); + this.bitmovinPlayer = new PlayerBuilder(this) + .setPlayerConfig(buildPlayerConfiguration()) + .disableAnalytics() + .build(); this.bitmovinPlayerView = new PlayerView(this, this.bitmovinPlayer); LinearLayout playerUIView = this.findViewById(R.id.bitmovinPlayerUIView); playerUIView.addView(bitmovinPlayerView); - // Create your ConvivaConfig object - ConvivaConfig convivaConfig = new ConvivaConfig(); - - // Set only in debug mode - if (gatewayUrl != null) { - convivaConfig.setGatewayUrl(gatewayUrl); + if (convivaAnalyticsIntegration == null) { + convivaAnalyticsIntegration = setupConvivaAnalytics(bitmovinPlayer); + } else { + convivaAnalyticsIntegration.attachPlayer(bitmovinPlayer); } - // Add optional parameters - convivaConfig.setDebugLoggingEnabled(true); - - // Create ConvivaAnalytics - convivaAnalyticsIntegration = new ConvivaAnalyticsIntegration( - bitmovinPlayer, - customerKey, - getApplicationContext(), - convivaConfig); - - - MetadataOverrides metadata = new MetadataOverrides(); - metadata.setApplicationName("Bitmovin Android Conviva integration example app"); - metadata.setViewerId("awesomeViewerId"); - Map customInternTags = new HashMap<>(); - customInternTags.put("contentType", "Episode"); - metadata.setCustom(customInternTags); - convivaAnalyticsIntegration.updateContentMetadata(metadata); - // load source using the created source configuration bitmovinPlayer.load(buildSourceConfiguration()); - } private PlayerConfig buildPlayerConfiguration() { @@ -108,6 +101,8 @@ private PlayerConfig buildPlayerConfiguration() { playerConfiguration.setAdvertisingConfig(buildAdConfiguration()); } + playerConfiguration.getPlaybackConfig().setAutoplayEnabled(true); + return playerConfiguration; } @@ -132,35 +127,136 @@ private AdvertisingConfig buildAdConfiguration() { return new AdvertisingConfig(preRoll); } + @Override + protected void onStart() { + super.onStart(); + if (bitmovinPlayerView != null) { + bitmovinPlayerView.onStart(); + } + } + @Override protected void onResume() { super.onResume(); - bitmovinPlayerView.onResume(); - convivaAnalyticsIntegration.reportAppForegrounded(); + if (bitmovinPlayerView != null) { + bitmovinPlayerView.onResume(); + } } @Override protected void onPause() { - convivaAnalyticsIntegration.reportAppBackgrounded(); - bitmovinPlayerView.onStop(); super.onPause(); + if (bitmovinPlayerView != null) { + bitmovinPlayerView.onPause(); + } } @Override - protected void onDestroy() { - bitmovinPlayerView.onDestroy(); - convivaAnalyticsIntegration.release(); + protected void onStop() { + super.onStop(); + if (bitmovinPlayerView != null) { + bitmovinPlayerView.onStop(); + } + } + + @Override + protected void onDestroy() { super.onDestroy(); + if (bitmovinPlayerView != null) { + bitmovinPlayerView.onDestroy(); + } + if (convivaAnalyticsIntegration != null) { + convivaAnalyticsIntegration.release(); + convivaAnalyticsIntegration = null; + } } private void tearDownPlayer() { - convivaAnalyticsIntegration.release(); - ViewGroup parent = (ViewGroup) bitmovinPlayerView.getParent(); + if (convivaAnalyticsIntegration != null) { + convivaAnalyticsIntegration.release(); + convivaAnalyticsIntegration = null; + } + ViewGroup parent = null; + if (bitmovinPlayerView != null) { + parent = (ViewGroup) bitmovinPlayerView.getParent(); + } if (parent != null) { parent.removeView(bitmovinPlayerView); } - bitmovinPlayer.destroy(); - bitmovinPlayerView.onDestroy(); + if (bitmovinPlayer != null) { + bitmovinPlayer.destroy(); + } + if (bitmovinPlayerView != null) { + bitmovinPlayerView.onDestroy(); + } + bitmovinPlayer = null; + } + + private void startSession() { + ConvivaAnalyticsIntegration convivaAnalyticsIntegration = setupConvivaAnalytics(bitmovinPlayer); + convivaAnalyticsIntegration.updateContentMetadata(buildMetadataOverrides("Art of Motion")); + try { + convivaAnalyticsIntegration.initializeSession(); + + this.convivaAnalyticsIntegration = convivaAnalyticsIntegration; + } catch (ConvivaAnalyticsException e) { + Log.d(TAG, "ConvivaAnalytics initialization failed with error: " + e); + } + } + + private ConvivaAnalyticsIntegration setupConvivaAnalytics(@Nullable Player player) { + // Create your ConvivaConfig object + ConvivaConfig convivaConfig = new ConvivaConfig(); + + // Set only in debug mode + if (gatewayUrl != null) { + convivaConfig.setGatewayUrl(gatewayUrl); + } + + ConvivaAnalyticsIntegration convivaAnalyticsIntegration = null; + // Add optional parameters + convivaConfig.setDebugLoggingEnabled(true); + + if (player != null) { + convivaAnalyticsIntegration = new ConvivaAnalyticsIntegration( + player, + customerKey, + getApplicationContext(), + convivaConfig + ); + } else { + convivaAnalyticsIntegration = new ConvivaAnalyticsIntegration( + customerKey, + getApplicationContext(), + convivaConfig + ); + } + + convivaAnalyticsIntegration.updateContentMetadata(buildMetadataOverrides(null)); + + return convivaAnalyticsIntegration; + } + + private MetadataOverrides buildMetadataOverrides(@Nullable String assetName) { + MetadataOverrides metadata = new MetadataOverrides(); + metadata.setApplicationName("Bitmovin Android Conviva integration example app"); + metadata.setViewerId("awesomeViewerId"); + + Map standardTags = new HashMap<>(); + standardTags.put("c3.cm.contentType", "VOD"); + metadata.setAdditionalStandardTags(standardTags); + + Map customTags = new HashMap<>(); + customTags.put("custom_tag", "Episode"); + metadata.setCustom(customTags); + + metadata.setImaSdkVersion("3.31.0"); + + if (assetName != null) { + metadata.setAssetName(assetName); + } + + return metadata; } @Override @@ -172,12 +268,19 @@ public void onClick(View v) { } else if (v == sendCustomEventButton) { Map eventAttributes = new HashMap<>(); eventAttributes.put("Some", "Attributes"); - this.convivaAnalyticsIntegration.sendCustomPlaybackEvent("Custom Event", eventAttributes); + if (this.convivaAnalyticsIntegration != null) { + this.convivaAnalyticsIntegration.sendCustomPlaybackEvent("Custom Event", eventAttributes); + } } else if (v == pauseTrackingButton) { - this.convivaAnalyticsIntegration.pauseTracking(false); + if (this.convivaAnalyticsIntegration != null) { + this.convivaAnalyticsIntegration.pauseTracking(false); + } } else if (v == resumeTrackingButton) { - this.convivaAnalyticsIntegration.resumeTracking(); + if (this.convivaAnalyticsIntegration != null) { + this.convivaAnalyticsIntegration.resumeTracking(); + } + } else if (v == startSessionButton) { + this.startSession(); } } } - diff --git a/ConvivaExampleApp/src/main/res/layout/activity_main.xml b/ConvivaExampleApp/src/main/res/layout/activity_main.xml index 8d1a73a..58de572 100644 --- a/ConvivaExampleApp/src/main/res/layout/activity_main.xml +++ b/ConvivaExampleApp/src/main/res/layout/activity_main.xml @@ -7,6 +7,18 @@ android:keepScreenOn="true" tools:context=".MainActivity"> +