Skip to content

Commit 5493827

Browse files
authored
Releases/v1.2.0
## Improvements * feat: Add Instant Clipping Params (#65)
1 parent c72efee commit 5493827

File tree

12 files changed

+382
-238
lines changed

12 files changed

+382
-238
lines changed

app/src/main/java/com/mux/player/media3/examples/ConfigurablePlayerActivity.kt

Lines changed: 130 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,25 @@ package com.mux.player.media3.examples
22

33
import android.content.Context
44
import android.os.Bundle
5+
import android.text.Editable
6+
import android.util.AttributeSet
57
import android.util.Log
8+
import android.view.LayoutInflater
69
import android.view.Menu
710
import android.view.MenuItem
11+
import android.view.View
12+
import android.view.ViewGroup
813
import android.widget.Toast
914
import androidx.annotation.OptIn
1015
import androidx.appcompat.app.AppCompatActivity
16+
import androidx.constraintlayout.widget.ConstraintLayout
1117
import androidx.media3.common.MediaItem
1218
import androidx.media3.common.MediaMetadata
1319
import androidx.media3.common.PlaybackException
1420
import androidx.media3.common.Player
1521
import androidx.media3.common.util.UnstableApi
22+
import androidx.recyclerview.widget.RecyclerView
23+
import androidx.recyclerview.widget.RecyclerView.ViewHolder
1624
import com.mux.stats.sdk.core.model.CustomData
1725
import com.mux.stats.sdk.core.model.CustomerData
1826
import com.mux.stats.sdk.core.model.CustomerVideoData
@@ -21,6 +29,8 @@ import com.mux.stats.sdk.core.util.UUID
2129
import com.mux.player.MuxPlayer
2230
import com.mux.player.media3.R
2331
import com.mux.player.media3.databinding.ActivityConfigurablePlayerBinding
32+
import com.mux.player.media3.databinding.NumericParamEntryBinding
33+
import com.mux.player.media3.databinding.TextParamEntryBinding
2434

2535
/**
2636
* A configurable example that uses the normal media3 player UI to play a video in the foreground from
@@ -46,40 +56,31 @@ class ConfigurablePlayerActivity : AppCompatActivity() {
4656
playbackParamsHelper.restoreInstanceState(savedInstanceState)
4757
}
4858

49-
binding.configurablePlayerPlaybackIdIn.hint = playbackParamsHelper.playbackIdOrDefault()
59+
binding.configurablePlayerPlaybackId.hint = playbackParamsHelper.playbackIdOrDefault()
60+
binding.configurablePlayerPlaybackId.onClear = { playbackParamsHelper.playbackId = null }
61+
binding.configurablePlayerCustomDomain.onClear = { playbackParamsHelper.customDomain = null }
62+
binding.configurablePlayerInstantclipStart.onClear =
63+
{ playbackParamsHelper.assetStartTime = null }
64+
binding.configurablePlayerInstantclipEnd.onClear = { playbackParamsHelper.assetEndTime = null }
65+
binding.configurablePlayerPlaybackToken.onClear = { playbackParamsHelper.playbackToken = null }
66+
binding.configurablePlayerDrmToken.onClear = { playbackParamsHelper.drmToken = null }
5067

5168
binding.configurablePlayerUpdateMediaItem.setOnClickListener {
52-
playbackParamsHelper.playbackId = binding.configurablePlayerPlaybackIdIn.text?.trim()?.toString()
53-
playbackParamsHelper.playbackToken =
54-
binding.configurablePlayerPlaybackTokenIn.text?.trim()?.toString()
55-
playbackParamsHelper.drmToken = binding.configurablePlayerDrmTokenIn.text?.trim()?.toString()
56-
playbackParamsHelper.customDomain = binding.configurablePlayerDomainIn.text?.trim()?.toString()
69+
playbackParamsHelper.playbackId = binding.configurablePlayerPlaybackId.entry
70+
playbackParamsHelper.playbackToken = binding.configurablePlayerPlaybackToken.entry
71+
playbackParamsHelper.drmToken = binding.configurablePlayerDrmToken.entry
72+
playbackParamsHelper.customDomain = binding.configurablePlayerCustomDomain.entry
73+
playbackParamsHelper.assetStartTime = binding.configurablePlayerInstantclipStart.entry
74+
playbackParamsHelper.assetEndTime = binding.configurablePlayerInstantclipEnd.entry
5775

5876
maybePlayMediaItem(playbackParamsHelper.createMediaItem())
5977
}
60-
binding.configurablePlaybackIdClear.setOnClickListener {
61-
binding.configurablePlayerPlaybackIdIn.text = null
62-
playbackParamsHelper.playbackId = null
63-
}
64-
binding.configurablePlayerDrmTokenClear.setOnClickListener {
65-
binding.configurablePlayerDrmTokenIn.text = null
66-
playbackParamsHelper.drmToken = null
67-
}
68-
binding.configurablePlayerPlaybackTokenClear.setOnClickListener {
69-
binding.configurablePlayerPlaybackTokenIn.text = null
70-
playbackParamsHelper.playbackToken = null
71-
}
72-
binding.configurablePlayerDomainClear.setOnClickListener {
73-
binding.configurablePlayerDomainIn.text = null
74-
playbackParamsHelper.customDomain = null
75-
}
7678
}
7779

7880
override fun onStart() {
7981
super.onStart()
8082

8183
val mediaItem = playbackParamsHelper.createMediaItem()
82-
8384
maybePlayMediaItem(mediaItem)
8485
}
8586

@@ -168,7 +169,6 @@ class ConfigurablePlayerActivity : AppCompatActivity() {
168169

169170
out.addListener(object : Player.Listener {
170171
override fun onPlayerError(error: PlaybackException) {
171-
// todo - better error info than this, inline in ui
172172
Log.e(TAG, "player error!", error)
173173
Toast.makeText(
174174
this@ConfigurablePlayerActivity,
@@ -185,3 +185,109 @@ class ConfigurablePlayerActivity : AppCompatActivity() {
185185
val TAG = ConfigurablePlayerActivity::class.simpleName
186186
}
187187
}
188+
189+
class TextParamEntryView @JvmOverloads constructor(
190+
context: Context,
191+
attrs: AttributeSet? = null,
192+
defStyleAttr: Int = 0
193+
) : ConstraintLayout(context, attrs, defStyleAttr) {
194+
195+
private val binding: TextParamEntryBinding = TextParamEntryBinding.inflate(
196+
LayoutInflater.from(context),
197+
this,
198+
true
199+
)
200+
201+
init {
202+
context.theme.obtainStyledAttributes(attrs, R.styleable.TextParamEntryView, 0, R.style.Theme_MuxVideoMedia3).apply {
203+
try {
204+
hint = getString(R.styleable.TextParamEntryView_hint)
205+
} finally {
206+
recycle()
207+
}
208+
}
209+
context.theme.obtainStyledAttributes(attrs, R.styleable.ParamEntry, 0, 0).apply {
210+
try {
211+
title = getString(R.styleable.ParamEntry_title)
212+
} finally {
213+
recycle()
214+
}
215+
}
216+
binding.textParamEntryClear.setOnClickListener {
217+
binding.textParamEntryIn.text = null
218+
onClear?.invoke()
219+
}
220+
}
221+
222+
var title: CharSequence? = null
223+
set(value) {
224+
binding.textParamEntryLbl.text = value
225+
field = value
226+
}
227+
var hint: CharSequence? = null
228+
set(value) {
229+
binding.textParamEntryIn.hint = value
230+
field = value
231+
}
232+
233+
var onClear: (() -> Unit)? = null
234+
val entry: String? get() {
235+
val text = binding.textParamEntryIn.text?.trim()?.ifEmpty { null }?.toString()
236+
return text
237+
}
238+
}
239+
240+
class NumericParamEntryView @JvmOverloads constructor(
241+
context: Context,
242+
attrs: AttributeSet? = null,
243+
defStyleAttr: Int = 0
244+
) : ConstraintLayout(context, attrs, defStyleAttr) {
245+
246+
private val binding: NumericParamEntryBinding = NumericParamEntryBinding.inflate(
247+
LayoutInflater.from(context),
248+
this,
249+
true
250+
)
251+
252+
init {
253+
context.theme.obtainStyledAttributes(attrs, R.styleable.NumericParamEntryView, 0, 0).apply {
254+
try {
255+
hint = getFloat(R.styleable.NumericParamEntryView_hint_num, Float.NaN)
256+
.toDouble()
257+
.takeIf { !it.isNaN() }
258+
} finally {
259+
recycle()
260+
}
261+
}
262+
context.theme.obtainStyledAttributes(attrs, R.styleable.ParamEntry, 0, 0).apply {
263+
try {
264+
title = getString(R.styleable.ParamEntry_title)
265+
} finally {
266+
recycle()
267+
}
268+
}
269+
270+
binding.numericParamEntryClear.setOnClickListener {
271+
binding.numericParamEntryIn.text = null
272+
onClear?.invoke()
273+
}
274+
}
275+
276+
var title: CharSequence? = null
277+
set(value) {
278+
binding.numericParamEntryLbl.text = value
279+
field = value
280+
}
281+
var hint: Double? = null
282+
set(value) {
283+
binding.numericParamEntryIn.hint = value?.toString()
284+
field = value
285+
}
286+
287+
var onClear: (() -> Unit)? = null
288+
val entry: Double? get() {
289+
val text =
290+
binding.numericParamEntryIn.text?.trim()?.ifEmpty { null }?.toString()?.toDoubleOrNull()
291+
return text
292+
}
293+
}

app/src/main/java/com/mux/player/media3/examples/PlaybackParamsHelper.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ class PlaybackParamsHelper {
2323
var drmToken: String? = null
2424
var playbackId: String? = null
2525
var customDomain: String? = null
26+
var assetStartTime: Double? = null
27+
var assetEndTime: Double? = null
2628

2729
fun createMediaItemBuilder(): MediaItem.Builder {
2830
return MediaItems.builderFromMuxPlaybackId(
2931
playbackId = playbackIdOrDefault(),
3032
minResolution = minRes,
3133
maxResolution = maxRes,
3234
renditionOrder = renditionOrder,
35+
assetStartTime = assetStartTime,
36+
assetEndTime = assetEndTime,
3337
playbackToken = playbackToken?.ifEmpty { null },
3438
drmToken = drmToken?.ifEmpty { null },
3539
domain = customDomain?.ifEmpty { null },

app/src/main/java/com/mux/player/media3/examples/SmartCacheActivity.kt

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -38,33 +38,25 @@ class SmartCacheActivity : AppCompatActivity() {
3838
playbackParamsHelper.restoreInstanceState(savedInstanceState)
3939
}
4040

41-
binding.configurablePlayerPlaybackIdIn.hint = playbackParamsHelper.playbackIdOrDefault()
41+
binding.configurablePlayerPlaybackId.hint = playbackParamsHelper.playbackIdOrDefault()
42+
binding.configurablePlayerPlaybackId.onClear = { playbackParamsHelper.playbackId = null }
43+
binding.configurablePlayerCustomDomain.onClear = { playbackParamsHelper.customDomain = null }
44+
binding.configurablePlayerInstantclipStart.onClear =
45+
{ playbackParamsHelper.assetStartTime = null }
46+
binding.configurablePlayerInstantclipEnd.onClear = { playbackParamsHelper.assetEndTime = null }
47+
binding.configurablePlayerPlaybackToken.onClear = { playbackParamsHelper.playbackToken = null }
48+
binding.configurablePlayerDrmToken.onClear = { playbackParamsHelper.drmToken = null }
4249

4350
binding.configurablePlayerUpdateMediaItem.setOnClickListener {
44-
playbackParamsHelper.playbackId = binding.configurablePlayerPlaybackIdIn.text?.trim()?.toString()
45-
playbackParamsHelper.playbackToken =
46-
binding.configurablePlayerPlaybackTokenIn.text?.trim()?.toString()
47-
playbackParamsHelper.drmToken = binding.configurablePlayerDrmTokenIn.text?.trim()?.toString()
48-
playbackParamsHelper.customDomain = binding.configurablePlayerDomainIn.text?.trim()?.toString()
51+
playbackParamsHelper.playbackId = binding.configurablePlayerPlaybackId.entry
52+
playbackParamsHelper.playbackToken = binding.configurablePlayerPlaybackToken.entry
53+
playbackParamsHelper.drmToken = binding.configurablePlayerDrmToken.entry
54+
playbackParamsHelper.customDomain = binding.configurablePlayerCustomDomain.entry
55+
playbackParamsHelper.assetStartTime = binding.configurablePlayerInstantclipStart.entry
56+
playbackParamsHelper.assetEndTime = binding.configurablePlayerInstantclipEnd.entry
4957

5058
maybePlayMediaItem(playbackParamsHelper.createMediaItem())
5159
}
52-
binding.configurablePlaybackIdClear.setOnClickListener {
53-
binding.configurablePlayerPlaybackIdIn.text = null
54-
playbackParamsHelper.playbackId = null
55-
}
56-
binding.configurablePlayerDrmTokenClear.setOnClickListener {
57-
binding.configurablePlayerDrmTokenIn.text = null
58-
playbackParamsHelper.drmToken = null
59-
}
60-
binding.configurablePlayerPlaybackTokenClear.setOnClickListener {
61-
binding.configurablePlayerPlaybackTokenIn.text = null
62-
playbackParamsHelper.playbackToken = null
63-
}
64-
binding.configurablePlayerDomainClear.setOnClickListener {
65-
binding.configurablePlayerDomainIn.text = null
66-
playbackParamsHelper.customDomain = null
67-
}
6860
}
6961

7062
override fun onStart() {
@@ -139,7 +131,6 @@ class SmartCacheActivity : AppCompatActivity() {
139131

140132
out.addListener(object : Player.Listener {
141133
override fun onPlayerError(error: PlaybackException) {
142-
// todo - better error info than this, inline in ui
143134
Log.e(TAG, "player error!", error)
144135
Toast.makeText(
145136
this@SmartCacheActivity,

app/src/main/java/com/mux/player/media3/examples/carousel/PlayerCarouselActivity.kt

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ class PlayerCarouselActivity : AppCompatActivity() {
5353
adapter = { adapter }
5454
)
5555

56-
carousel.addOnChildAttachStateChangeListener(object : RecyclerView.OnChildAttachStateChangeListener {
56+
carousel.addOnChildAttachStateChangeListener(object :
57+
RecyclerView.OnChildAttachStateChangeListener {
5758
override fun onChildViewAttachedToWindow(view: View) {
5859
// If the player is idle when we bind, the UI is initializing and we should autoplay
5960
if (viewModel.player.playbackState == Player.STATE_IDLE
60-
|| viewModel.player.currentMediaItem == null) {
61+
|| viewModel.player.currentMediaItem == null
62+
) {
6163
val item = adapter.items[0]
6264
viewModel.changeMediaItem(item.mediaItem)
6365
viewModel.playIntoView(view.findViewById(R.id.item_fullscreen_player_playerview))
@@ -66,6 +68,7 @@ class PlayerCarouselActivity : AppCompatActivity() {
6668
Log.d("PlayerCarouselActivity", "Player was idle. Autoplaying")
6769
}
6870
}
71+
6972
override fun onChildViewDetachedFromWindow(view: View) {
7073
}
7174
})
@@ -88,13 +91,21 @@ class PlayerCarouselActivity : AppCompatActivity() {
8891
title = "Tears of Steel",
8992
description = "A time travel story with really cool high-quality CGI and some truly" +
9093
" powerful dialogue writing",
91-
mediaItem = MediaItems.fromMuxPlaybackId(PlaybackIds.TEARS_OF_STEEL),
94+
mediaItem = MediaItems.fromMuxPlaybackId(
95+
PlaybackIds.TEARS_OF_STEEL,
96+
assetStartTime = 25.0,
97+
assetEndTime = 35.0,
98+
),
9299
),
93100
CarouselItem(
94101
title = "Sintel",
95102
description = "A knight meets a baby dragon and together they do some adventure stuff." +
96-
" The dragon is super adorable, I forget what actually happens though",
97-
mediaItem = MediaItems.fromMuxPlaybackId(PlaybackIds.SINTEL),
103+
" PS, adorable monster alert",
104+
mediaItem = MediaItems.fromMuxPlaybackId(
105+
PlaybackIds.SINTEL,
106+
assetStartTime = 240.0,
107+
assetEndTime = 285.0,
108+
),
98109
),
99110
CarouselItem(
100111
title = "Robot shooting a bird",
@@ -104,12 +115,20 @@ class PlayerCarouselActivity : AppCompatActivity() {
104115
CarouselItem(
105116
title = "Making of Sintel",
106117
description = "A documentary about the making of Sintel",
107-
mediaItem = MediaItems.fromMuxPlaybackId(PlaybackIds.MAKING_OF_SINTEL),
118+
mediaItem = MediaItems.fromMuxPlaybackId(
119+
PlaybackIds.MAKING_OF_SINTEL,
120+
assetStartTime = 140.0,
121+
assetEndTime = 300.0,
122+
),
108123
),
109124
CarouselItem(
110125
title = "Big Buck Bunny",
111126
description = "A rabbit gets harassed by a bunch of punk squirrels.",
112-
mediaItem = MediaItems.fromMuxPlaybackId(PlaybackIds.BIG_BUCK_BUNNY),
127+
mediaItem = MediaItems.fromMuxPlaybackId(
128+
PlaybackIds.BIG_BUCK_BUNNY,
129+
assetStartTime = 200.0,
130+
assetEndTime = 260.0,
131+
),
113132
),
114133
CarouselItem(
115134
title = "Mux Marketing Video",
@@ -124,13 +143,21 @@ class PlayerCarouselActivity : AppCompatActivity() {
124143
title = "Making of Big Buck Bunny",
125144
description = "A group of nerds talk about making big buck bunny, presumably because they " +
126145
"were the ones that did that",
127-
mediaItem = MediaItems.fromMuxPlaybackId(PlaybackIds.MAKING_OF_BUCK_BUNNY),
146+
mediaItem = MediaItems.fromMuxPlaybackId(
147+
PlaybackIds.MAKING_OF_BUCK_BUNNY,
148+
assetStartTime = 0.0,
149+
assetEndTime = 30.0,
150+
),
128151
),
129152
CarouselItem(
130153
title = "Elephant's Dream",
131154
description = "A surreal fantasy action movie. The first Open Movie Project movie. It's " +
132155
"old but kinda trippy",
133-
mediaItem = MediaItems.fromMuxPlaybackId(PlaybackIds.ELEPHANTS_DREAM),
156+
mediaItem = MediaItems.fromMuxPlaybackId(
157+
PlaybackIds.ELEPHANTS_DREAM,
158+
assetStartTime = 300.0,
159+
assetEndTime = 450.0,
160+
),
134161
),
135162
CarouselItem(
136163
title = "View From a Blue Moon",

0 commit comments

Comments
 (0)