Using the Pulse plugin with the Native SDK for Android

In this topic, you will learn how to use the Pulse plugin with Brightcove's Native SDK for Android.

Introduction

Brightcove's Pulse plugin enables you to integrate Invidi's Pulse SDK with the Brightcove Native SDK for Android. Pulse is a Video Advertisement Platform. For campaigns and configuration details, see their User Guide.

Steps

Once a campaign is created in the Pulse platform, you can start using the Pulse Plugin for the Brightcove Native SDK for Android. Follow these steps to integrate the Pulse Plugin with your project:

  1. In your module build.gradle file, add the Pulse plugin dependency.

    dependencies {
        implementation 'com.brightcove.player:android-pulse-plugin:6.12.0'
    }
  2. Download the Pulse SDK .aar file.

  3. In your app/libs folder, open the build.gradle file from your module. Modify the following:

    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
    }
  4. In your MainActivity.java file, initialize the Pulse Plugin by instantiating a PulseComponent with the Pulse host URL created for your campaign.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        // Creating pulse component
        PulseComponent pulseComponent = new PulseComponent(
                "your pulse host url",
                brightcoveVideoView.getEventEmitter(),
                brightcoveVideoView);
        // ...
    }
  5. Set the PulseComponent Listener.

    pulseComponent.setListener(new PulseComponent.Listener() {
      @NonNull
      @Override
      public PulseSession onCreatePulseSession(
        @NonNull String hostUrl,
        @NonNull Video video,
        @NonNull ContentMetadata contentMetadata,
        @NonNull RequestSettings requestSettings) {
        // See step 3a
        return Pulse.createSession(contentMetadata, requestSettings);
      }
    
      @Override
      public void onOpenClickthrough(@NonNull PulseVideoAd pulseVideoAd) {
      }
    });
  6. Implement the onCreatePulseSession method, which creates a PulseSession and returns it to the PulseComponent. There are three parameters:

    • The pulse host
    • The content metadata settings
    • The request settings

    @NonNull
    @Override
    public PulseSession onCreatePulseSession(
        @NonNull String hostUrl,
        @NonNull Video video,
        @NonNull ContentMetadata contentMetadata,
        @NonNull RequestSettings requestSettings) {
      // Set the pulse Host:
      Pulse.setPulseHost(pulseHostUrl, null, null);
    
      // Content metadata settings
      contentMetadata.setCategory("skip-always");
      contentMetadata.setTags(Collections.singletonList("standard-linears"));
      contentMetadata.setIdentifier("demo");
    
      // Request Settings:
      // Adding mid-rolls
      List<Float> midrollCuePoints = new ArrayList<>();
      midrollCuePoints.add(60f);
      requestSettings.setLinearPlaybackPositions(midrollCuePoints);
    
      // Create and return the PulseSession
      return Pulse.createSession(contentMetadata, requestSettings);
    }
  7. Implement the onOpenClickthrough method, which is called when the learn more button from a Linear Ad is clicked. A typical action for this callback is to open the browser with the expected url.

    @Override
    public void onOpenClickthrough(@NonNull PulseVideoAd pulseVideoAd) {
      Intent intent = new Intent(Intent.ACTION_VIEW)
        .setData(Uri.parse(pulseVideoAd.getClickthroughURL().toString()));
      brightcoveVideoView.getContext().startActivity(intent);
      pulseVideoAd.adClickThroughTriggered();
    }
  8. Play your content

    Catalog catalog = new Catalog.Builder(
      eventEmitter,
      getString(R.string.account))
      .setPolicy(getString(R.string.policy))
      .build();
    
    catalog.findVideoByID(getString(R.string.videoId), new VideoListener() {
      // Add the video found to the queue with add().
      // Start playback of the video with start().
      @Override
      public void onVideo(Video video) {
        brightcoveVideoView.add(video);
        brightcoveVideoView.start();
      }
    });

Pulse Pause Ads

When the Pulse campaign has "Pause Ads" configured, the Pulse plugin will show the user when the content is paused.

Error handling

All errors will be surfaced to the developer using the EventType.AD_ERROR event, as shown below:

eventEmitter.on(EventType.AD_ERROR, event -> {
    Throwable error = event.getProperty(Event.ERROR, Throwable.class);
    Log.e(TAG, "AD_ERROR: ", error);
});

UI Customization

Internally, the Pulse plugin inflates the PulseAdView using the R.layout.pulse_ad_view layout id. For a different layout, you can create a layout file with the same name and add it to the res/layout directory. This overrides the default layout.

Use the following ids to replace the defaults:

Component Views
Component Views
Index View Type View Id
A TextView pulse_ad_number_view
B TextView pulse_ad_countdown_view
C TextView pulse_ad_name_view
D TextView pulse_ad_learn_more_view
E TextView pulse_skip_ad_view

Complete code sample

Here is a complete code sample for using the Pulse plugin with the Native SDK for Android.

Activity

Here is a example of the full Activity code:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      setContentView(R.layout.activity_main);

      final BrightcoveVideoView videoView = findViewById(R.id.video_view);
      super.onCreate(savedInstanceState);

      EventEmitter eventEmitter = videoView.getEventEmitter();

      // Pulse setup
      PulseComponent pulseComponent = new PulseComponent(
          "https://pulse-demo.videoplaza.tv",
          eventEmitter,
          videoView);

      pulseComponent.setListener(new PulseComponent.Listener() {
          @NonNull
          @Override
          public PulseSession onCreatePulseSession(
                @NonNull String pulseHostUrl,
                @NonNull Video video,
                @NonNull ContentMetadata contentMetadata,
                @NonNull RequestSettings requestSettings) {
            Pulse.setPulseHost(pulseHostUrl, null, null);
            contentMetadata.setCategory("skip-always");
            contentMetadata.setTags(Collections.singletonList("standard-linears"));
            contentMetadata.setIdentifier("demo");

            // Adding mid-rolls
            List<Float> midrollCuePoints = new ArrayList<>();
            midrollCuePoints.add(60f);
            requestSettings.setLinearPlaybackPositions(midrollCuePoints);

            return Pulse.createSession(
              contentMetadata,
              requestSettings);
          }

          @Override
          public void onOpenClickthrough(@NonNull PulseVideoAd ad) {
            Intent intent = new Intent(Intent.ACTION_VIEW)
              .setData(Uri.parse(ad.getClickthroughURL().toString()));
            videoView.getContext().startActivity(intent);
            ad.adClickThroughTriggered();
          }
      });

      Catalog catalog = new Catalog.Builder(eventEmitter, "YourAccountId")
          .setPolicy("YourPolicyKey")
          .build();
      catalog.findVideoByID("YourVideoId", new VideoListener() {

        // Add the video found to the queue with add().
        // Start playback of the video with start().
        @Override
        public void onVideo(Video video) {
          videoView.add(video);
          videoView.start();
        }
      });
    }
  }

Layout

Here is an example of the layout code for the R.layout.pulse_ad_view.

<?xml version="1.0" encoding="utf-8"?>
  <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent">

      <RelativeLayout
          android:id="@+id/view_ad_details"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:background="@drawable/pulse_skip_button_background_selector">

          <TextView
              android:id="@+id/pulse_ad_name_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_alignParentTop="true"
              android:layout_marginTop="4dp"
              android:paddingTop="4dp"
              android:paddingStart="8dp"
              android:paddingEnd="8dp"
              android:textColor="@color/white"
              android:background="@color/bmc_live"
              android:textStyle="bold"
              tools:text="Preroll blue"/>

          <TextView
              android:id="@+id/pulse_ad_number_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:paddingStart="8dp"
              android:paddingEnd="4dp"
              android:paddingBottom="4dp"
              android:layout_marginBottom="8dp"
              android:layout_below="@id/pulse_ad_name_view"
              android:textColor="@color/white"
              android:background="@color/white_semi_trans"
              tools:text="Ad (1 of 2)"/>

          <TextView
              android:id="@+id/pulse_ad_countdown_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:paddingStart="4dp"
              android:paddingEnd="4dp"
              android:paddingBottom="4dp"
              android:layout_marginBottom="4dp"
              android:layout_below="@id/pulse_ad_name_view"
              android:layout_toEndOf="@+id/pulse_ad_number_view"
              android:textColor="@color/green_almost_opaque"
              android:text=""
              tools:text="00:06"/>

          <TextView
              android:id="@+id/pulse_ad_learn_more_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginStart="@dimen/pulse_ad_learn_more_margin_left"
              android:layout_marginTop="@dimen/pulse_ad_learn_more_margin_top"
              android:layout_marginEnd="@dimen/pulse_ad_learn_more_margin_right"
              android:layout_marginBottom="@dimen/pulse_ad_learn_more_margin_bottom"
              android:layout_alignTop="@id/pulse_ad_name_view"
              android:layout_alignBottom="@id/pulse_ad_countdown_view"
              android:layout_alignParentEnd="true"
              android:background="@drawable/pulse_learn_more_button_background"
              android:paddingStart="12dp"
              android:paddingEnd="12dp"
              android:padding="@dimen/pulse_ad_learn_more_padding_default"
              android:gravity="center"
              android:shadowColor="@color/brightcove_semitransparent"
              android:shadowDx="-1"
              android:shadowDy="1"
              android:shadowRadius="1.5"
              android:text="@string/pulse_message_learn_more"
              android:textColor="@color/pulse_button_text_color"
              android:nextFocusUp="@id/pulse_skip_ad_view"
              android:textSize="@dimen/pulse_message_text_size"
              android:visibility="gone"
              tools:visibility="visible" />

      </RelativeLayout>

      <TextView
          android:id="@+id/pulse_skip_ad_view"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:maxWidth="164dp"
          android:layout_alignParentEnd="true"
          android:layout_centerVertical="true"
          android:layout_marginBottom="@dimen/pulse_skip_ad_margin_bottom"
          android:background="@drawable/pulse_skip_button_background_selector"
          android:ellipsize="none"
          android:gravity="center"
          android:maxLines="2"
          android:paddingStart="@dimen/pulse_skip_ad_padding_left"
          android:paddingEnd="@dimen/pulse_skip_ad_padding_right"
          android:paddingTop="@dimen/pulse_skip_ad_padding"
          android:paddingBottom="@dimen/pulse_skip_ad_padding"
          android:scrollHorizontally="false"
          android:shadowColor="@color/brightcove_shadow"
          android:shadowDx="-1"
          android:shadowDy="1"
          android:shadowRadius="1.5"
          android:text="@string/pulse_message_skip_ad"
          android:textColor="@color/pulse_button_text_color"
          android:textSize="@dimen/pulse_message_text_size"
          android:visibility="gone"
          android:nextFocusUp="@id/pulse_ad_learn_more_view"
          android:focusable="true"
          tools:visibility="visible"/>

  </RelativeLayout>