Getting Started
Short Video is an external Belive SDK module but it can be used independently. Please check the document for the Belive SDK setup first.
The Short Video requires a Belive core module, Belive common module and other 3rd libraries to work normally.
build.gradle
dependencies:
// sdk aar files & required dependencies
implementation (name:'belive-core-release', ext:'aar')
implementation (name:'belive-common-release', ext:'aar')
implementation (name:'belive-shortvideo-release', ext:'aar')
implementation 'io.insert-koin:koin-android:3.4.0'
implementation 'androidx.media3:media3-exoplayer:1.0.2'
implementation 'androidx.media3:media3-ui:1.0.2'
implementation 'androidx.media3:media3-exoplayer-hls:1.0.2'
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.9.0"
implementation 'com.github.bumptech.glide:glide:4.15.1'
kapt 'com.github.bumptech.glide:compiler:4.15.1'
ShortVideoView
ShortVideoView is a UI entry point and designed to display a list of videos in many layout types: vertical grid, horizontal grid and carousel. When a video item is clicked, it opens the main player and play the selected video.
Adding a ShortVideoView is as simple as a normal Android View.
<sg.belive.lib.shortvideo.view.ShortVideoView
android:id="@+id/short_video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
ShortVideoView loads the video data from a Video Source which is defined by a View Config. This View Config defines the layout type and provides some customization for the UI. It also controls some basic player behaviors.
Grid layout
Grid layout supports vertical (default) and horizontal.
val cfBuilder = ViewConfig.Builder(this) // `this` is a context
// set the video source (DefaultVideoSource, SingleVideoSource, PlaylistVideoSource)
cfBuilder.videoSource(source)
// Create the detail view config which is used to render the main player
val detailCfBuilder = DetailViewConfig.Builder(this)
val detailLoCf = ShortVideoDetailLayout.createDefaultConfig(this)
detailCfBuilder.layoutConfig(detailLoCf)
// Set the detail view config
cfBuilder.detailViewConfig(detailCfBuilder.build())
shortVideoView.init(cfBuilder.build())
You can change the Grid orientation to horizontal or update the span count (number of grid columns/rows)
val cfBuilder = ViewConfig.Builder(this)
cfBuilder.videoSource(source)
val loCf = ShortVideoGridLayout.createDefaultConfig(this)
loCf.scrollDirection = ScrollDirection.HORIZONTAL
loCf.numberOfRows = 2
cfBuilder.layoutConfig(loCf)
shortVideoView.init(cfBuilder.build())
The player behavior config is set as below
val playerCf = PlayerItemConfiguration().apply {
enableAutoPlay()
actionOnCompleted = PlayerCompleteAction.STOP
}
cfBuilder.playerConfig(playerCf)
UI Customization
For Grid layout, you can customize the below UI components/attrs:
- Item width and height ratio: This ratio is in String format (“16:9”). It is recommended to use a portrait ratio
- Video title text view
- Play button
- Item’s corner radius
- Video thumbnail scale mode
val gridLoCf = ShortVideoGridLayout.createDefaultConfig(this)
gridLoCf.ratio = "16:9"
gridLoCf.itemConfiguration?.let { cf ->
cf.title = TitleConfiguration().apply {
this.color = ContextCompat.getColor(context, R.color.bls_sv_video_item_title_color)
this.backgroundRes = R.drawable.bls_sv_bg_carousel_video_item_title
this.textSize =
context.resources.getDimension(R.dimen.bls_sv_video_item_title_size)
this.font = Typeface.DEFAULT
this.gravity = Gravity.START or Gravity.CENTER_VERTICAL
}
cf.playButton = ButtonViewItemConfiguration().apply {
this.onStateIconRes = R.drawable.ic_play
}
cf.cornerRadius = 10F
cf.imageScaleMode = ImageView.ScaleType.CENTER
}
Carousel layout
Carousel layout brings a video player feature to the ShortVideoView. When using this layout, it requires a parent container and this container should be a ConstraintLayout or FrameLayout to avoid unwanted layout behaviors when switching from Fullscreen and Compact mode.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/loContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<sg.belive.lib.shortvideo.view.ShortVideoView
android:id="@+id/short_video_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="50dp"
app:layout_constraintDimensionRatio="9:16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The View config is almost same as the Grid type, except the ShortVideoCarouselLayout is used for the layoutConfig.
val cfBuilder = ViewConfig.Builder(this) // `this` is a context
cfBuilder.videoSource(source)
val carouselLoCf = ShortVideoCarouselLayout.createDefaultConfig(this)
carouselLoCf.containerView = loContainer
// Create the detail view config which is used to render the main player
val detailCfBuilder = DetailViewConfig.Builder(this)
val detailLoCf = ShortVideoDetailLayout.createDefaultConfig(this)
detailCfBuilder.layoutConfig(detailLoCf)
// Set the detail view config
cfBuilder.detailViewConfig(detailCfBuilder.build())
shortVideoView.init(cfBuilder.build())
This layout requires a non-null containerView which is the parent view of the ShortViewView in Compact mode. In Fullscreen mode, the parent view will be the content view of the Activity (if the ShortVideoView’s context is an Activity), otherwise it will be a top most root view. You can override this behavior by manually setting a fullScreenContainerView to the View Config.
val cfBuilder = ViewConfig.Builder(this) // `this` is a context
cfBuilder.videoSource(source)
val carouselLoCf = ShortVideoCarouselLayout.createDefaultConfig(this)
carouselLoCf.containerView = loContainer
carouselLoCf.fullScreenContainerView = target_fullscreen_container_view
Play and pause the video
User can pause and resume the video by calling the following APIs
shortVideoView.pause()
//
shortVideoView.play()
Scrolling and navigation buttons
The Carousel layout can be configured to disable the scrolling behavior. This might be useful when you want to navigate the video items manually.
val carouselLoCf = ShortVideoCarouselLayout.createDefaultConfig(this)
// Disable the scrolling
carouselLoCf.allowScroll = false
You can also hide the navigation buttons (next & previous button) by setting their view config to null.
val carouselLoCf = ShortVideoCarouselLayout.createDefaultConfig(this)
// Hide the navigation button
carouselLoCf.nextButton = null
carouselLoCf.prevButton = null
UI Customization
For Carousel layout, you can customize the below UI components/attrs:
- Video title text view (the layoutGravity attr is ignored)
- Item’s corner radius
- Progress bar
- Play/pause, mute, full screen, share button
- Video thumbnail scale mode
- Video scale mode
val carouselLoCf = ShortVideoCarouselLayout.createDefaultConfig(this) carouselLoCf.itemConfiguration?.let { cf -> cf.title = TitleConfiguration() cf.playPauseButton = ButtonViewItemConfiguration().apply { this.onStateIconRes = R.drawable.ic_play this.offStateIconRes = R.drawable.ic_pause } cf.imageScaleMode = ImageView.ScaleType.CENTER cf.videoScaleMode = BlsPlayerScaleMode.DEFAULT }
Main player
The ShortVideo module provides a main player as an internal screen (activity) that can be opened when clicking on a ShortVideoView’s video item. This screen includes a video player with many functions and a product list. The UI and player behavior can be configured when setting up the Grid/Carousel layout.
val gridViewCfBuilder = ViewConfig.Builder(this)
// Create the detail view config
val detailCfBuilder = DetailViewConfig.Builder(this)
detailCfBuilder.layoutConfig(ShortVideoDetailLayout.createDefaultConfig(this))
detailCfBuilder.playerConfig(PlayerItemConfiguration())
// Set the detail view config
gridViewCfBuilder.detailViewConfig(detailCfBuilder.build())
shortVideoView.init(gridViewCfBuilder.build())
Picture in Picture (PiP)
The main player supports play a video in PiP mode on devices that support Pip (Android 8+). You can disable this feature by hiding the PiP button.
val detailCfBuilder = DetailViewConfig.Builder(this)
val detailLoCf = ShortVideoDetailLayout.createDefaultConfig(this)
detailLoCf.itemConfiguration?.let { cf ->
cf.pipButton = null
}
Product list
A list of products can be attached to a video item and it will be displayed in the main player. Each product item might contain a Shop CTA button which opens the product URL in an external browser (you can override this behavior). The product item UI can also be customized.
Scrolling and navigation buttons
Same as the Carousel layout, the main player can be configured to disable the scrolling behavior.
val detailLoCf = ShortVideoDetailLayout.createDefaultConfig(this)
// Disable the scrolling
detailLoCf.allowScroll = false
You can also hide the navigation buttons (next & previous button) by setting their view config to null.
val detailLoCf = ShortVideoDetailLayout.createDefaultConfig(this)
// Hide the navigation button
detailLoCf.nextButton = null
detailLoCf.prevButton = null
UI Customization
For main player layout, you can customize the below UI components/attrs:
- Video title, duration text view (the layoutGravity attr is ignored)
- Item’s corner radius
- Seek bar
- Play/pause, mute, pip, share, close button
- Video thumbnail scale mode
- Video scale mode
- Product item view.
For product item layout, you can customize the below UI components/attrs:
- Product title text view (the layoutGravity attr is ignored)
- Item’s corner radius/background
- Shop CTA text color and icon resource.
- Shop CTA text content. If set, it will override the remote value.
- Product thumbnail scale mode
val detailLoCf = ShortVideoDetailLayout.createDefaultConfig(this)
detailLoCf.itemConfiguration?.let { cf ->
cf.closeButton = ButtonViewItemConfiguration().apply {
onStateIconRes = R.drawable.ic_close_main_player
}
cf.muteButton = ButtonViewItemConfiguration().apply {
onStateIconRes = R.drawable.ic_unmute
offStateIconRes = R.drawable.ic_muted
}
cf.seekBar = SeekViewItemConfiguration().apply {
thumbIconRes = R.drawable.ic_seek_thumb
progressRes = R.drawable.progress_seek_bar
}
cf.productItem = ShortVideoDetailLayout.ProductItemConfiguration().apply {
this.title = TitleConfiguration().apply {
this.color =
ContextCompat.getColor(
context,
R.color.product_item_title_color
)
this.textSize =
resources.getDimension(R.dimen.product_item_title_size)
this.font = Typeface.DEFAULT
this.gravity = Gravity.START
}
this.cornerRadius = 10F
this.backgroundColor = ContextCompat.getColor(context, R.color.white)
this.ctaTextColor =
ContextCompat.getColor(
context,
R.color.color_shop_cta_button
)
this.ctaIconRes = R.drawable.ic_product_cta_nav
}
}
Video source
Default
The default video source will fetch all the available videos data from server. To create a default source, use the below API
ShortVideoSource.default()
The default source will load the data in pagination. When scrolling the list to the end, it will automatically load the next page until reaching the end of the source.
Playlist
The playlist video source will fetch all the available videos from a specific playlist by an alias. To create a playlist source, use the below API
ShortVideoSource.playlist(playlistAlias)
Single
The single video source will fetch a single video by a video id. To create a single source, use the below API
ShortVideoSource.singleVideo(videoId)
Listeners
Error listener
When an error happens, the ShortVideo module triggers an error callback with a code and a message. To listen to the error, set a BlsShortVideoErrorListener
to the ShortVideoView
shortVideoView.setErrorListener(object : BlsShortVideoErrorListener {
override fun onShortVideoError(code: Int, msg: String?) {
}
})
The error code can be found in the BlsShortVideoErrorCode
and the message will describe the error
Video Item Click listener
When user clicks on a video item, it will open the main player and trigger a callback.
shortVideoView.setVideoItemClickedListener(object : BlsShortVideoItemClickListener {
override fun onShortVideoItemClicked(videoId: String) {
}
})
The implementation of the listener will not affect the flow, it is used mainly for analytics purpose.
Main Player Action listener
This listener is different to other listeners that it is bound to an singleton handler. You have to take care of the usage of this listener or a memory leak can occur. You should not implement this interface as an anonymous class in the activity context. A recommended approach is to create a class and implement this interface to avoid any strong reference to the activity or fragment.
class MainPlayerAction : BlsShortVideoMainPlayerActionListener {
override fun onProductItemClicked(
context: Context, item: ShortVideoProductModel
): Boolean {
context.openBrowser(item.productUrl.orEmpty())
return true
}
override fun onShareVideo(context: Context, videoId: String): Boolean {
context.shareText(videoId)
return true
}
}
The onProductItemClicked
is triggered when a shop CTA button is clicked. You can return true in this API to consume the event and stop the flow.
If you return false, the SDK will continue handling the event (if any).
The onShareVideo
is triggered when a share button is clicked (in both Carousel and Main player). You can return true in this API to consume the event and stop the flow.
If you return false, the SDK will continue handling the event. The default handler is to open the system share dialog.
You have to reset the listener when unused
binding.shortVideoView.releaseMainPlayerActionListener(this)
View configuration
The basic configuration for a view item are size
and backgroundRes
. If the size
is not set, a LayoutParams with WRAP_CONTENT
for both width and height will be generated.
Text View
A TextView can be customized using a TitleConfiguration
. This configuration provides some basic attributes:
- color: map to the TextView’s textColor
- font: map to the TextView’s typeface
- maxLines: map to the TextView’s maxLines. The default value is 1
- layoutGravity: map to the LayoutParams’s gravity. The default value is Gravity.BOTTOM
- gravity: map to the TextView’s gravity. The default value is Gravity.START
- textSize: map to the TextView’s textSize
Image Button
A ImageButton can be customized using a ButtonViewItemConfiguration
. This configuration provides some basic attributes:
- onStateIconRes: The initialize state icon of the ImageButton
- offStateIconRes: The next state icon of the button. If a button has 1 state only (like the share, close button), this attribute is ignored.
Progress Bar
A ProgressBar can be customized using a ProgressViewItemConfiguration
. This configuration provides some basic attributes:
- normalColor: The color of the progress background (not the background of the whole view) (map to the
progressBackgroundTintList
) - progressColor: The color of the progress (map to the
progressTintList
) - normalTintMode: The tintMode of the progress background (map to the
progressBackgroundTintMode
) - progressTintMode: The tintMode of the progress background (map to the
progressTintList
) - progressRes: Map to the
progressDrawable
. If set, the above attributes will be ignored.
Seek Bar
A SeekBar can be customized using a SeekViewItemConfiguration
. This configuration provides some basic attributes:
- normalColor: The color of the progress background (not the background of the whole view) (map to the
progressBackgroundTintList
) - progressColor: The color of the progress (map to the
progressTintList
) - normalTintMode: The tintMode of the progress background (map to the
progressBackgroundTintMode
) - progressTintMode: The tintMode of the progress background (map to the
progressTintList
) - progressRes: Map to the
progressDrawable
. If set, the above attributes will be ignored. - thumbColor: The color of the thumb (map to the
thumbTintList
) - thumbTintMode: The tintMode of the thumb (map to the
thumbTintMode
) - thumbIconRes: Map to the
thumb
attr. If set, the above thumb attributes will be ignored.
Image/Video scale mode
Some of the layout items support image and video scale mode.
The Image scale mode can be set from ImageView.Scale
.
The default value is ImageView.ScaleType.CENTER_CROP
The Video Scale mode can be set from BlsPlayerScaleMode
. Here are the support modes:
- DEFAULT: Let the SDK choose the best scale mode bases on video/device orientation. This is default and recommended mode.
- FIT: Make the video fit the view and keep the ratio in at least 1 dimension (width or height)
- FILL: Zoom the video and do not keep the ratio to fit the view in both width and height
- FIXED_WIDTH: Make the video fit the view width and keep the ratio
- FIXED_HEIGHT: Make the video fit the view height and keep the ratio
- ZOOM: Zoom the video and keep the ratio to fit the view in both width and height
Player configuration
Playlist autoLoop
For Playlist video source, there will be a flag called autoLoop which is set by the CMS. For other sources (Default/Single), this flag is always off.
If this flag is on, the whole playlist will be repeated (depends on the SDK’s autoPlay flag and action on player complete).
If this flag is off, the playlist will behave same as other video sources.
Note: The autoLoop is applied to the Playlist but the autoPlay and action on player complete are applied to every single video. It means, even the autoLoop is on, the playlist might not be repeated if a video in the list is not playing or the action is not configured to play the next item.
Auto play
By default, the video autoPlay (Carousel and Main player) flag is controlled by the CMS. You can override this behavior by explicitly setting it in the SDK.
val playerCf = PlayerItemConfiguration().apply {
enableAutoPlay()
//
disableAutoPlay()
}
When the auto play flag is enabled/disabled by the SDK, the one from CMS will be ignored.
Action on player complete
You can control the behavior when the player finish a playback.
val playerCf = PlayerItemConfiguration().apply {
actionOnCompleted = PlayerCompleteAction.STOP
}
There are 4 options that you can set:
- NONE: This is the default value.
The behavior now will be controlled by the playlist autoLoop.
If the autoLoop value is on, then it will behave like
PLAY_NEXT_ITEM
, except the player will repeat the playlist after playing the last video. If the autoLoop value is off, then it will behave likeSTOP
- STOP: The player will always stop the video after playing it. The playlist’s autoLoop is ignored.
- PLAY_NEXT_ITEM: The player will always play the next video in the list. The playlist’s autoLoop is ignored. If the current video is the last item, the player will stop.
- REPEAT: The player will always loop the current video. The playlist’s autoLoop is ignored.