diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index b0544c1d1b..ed9635a340 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -15,8 +15,11 @@
for the underlying track was changing (e.g., at some period transitions).
* Add `SilenceMediaSource` that can be used to play silence of a given
duration ([#5735](https://github.com/google/ExoPlayer/issues/5735)).
-* UI: Change playback controls toggle from touch down to touch up events
- ([#5784](https://github.com/google/ExoPlayer/issues/5784)).
+* UI:
+ * Allow setting `DefaultTimeBar` attributes on `PlayerView` and
+ `PlayerControlView`.
+ * Change playback controls toggle from touch down to touch up events
+ ([#5784](https://github.com/google/ExoPlayer/issues/5784)).
* Add a workaround for broken raw audio decoding on Oppo R9
([#5782](https://github.com/google/ExoPlayer/issues/5782)).
* Offline: Add Scheduler implementation which uses WorkManager.
diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
index 328b5d6a49..5c70203788 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
@@ -220,11 +220,26 @@ public class DefaultTimeBar extends View implements TimeBar {
private @Nullable long[] adGroupTimesMs;
private @Nullable boolean[] playedAdGroups;
- /** Creates a new time bar. */
+ public DefaultTimeBar(Context context) {
+ this(context, null);
+ }
+
+ public DefaultTimeBar(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DefaultTimeBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, attrs);
+ }
+
// Suppress warnings due to usage of View methods in the constructor.
@SuppressWarnings("nullness:method.invocation.invalid")
- public DefaultTimeBar(Context context, AttributeSet attrs) {
- super(context, attrs);
+ public DefaultTimeBar(
+ Context context,
+ @Nullable AttributeSet attrs,
+ int defStyleAttr,
+ @Nullable AttributeSet timebarAttrs) {
+ super(context, attrs, defStyleAttr);
seekBounds = new Rect();
progressBar = new Rect();
bufferedBar = new Rect();
@@ -251,9 +266,9 @@ public class DefaultTimeBar extends View implements TimeBar {
int defaultScrubberEnabledSize = dpToPx(density, DEFAULT_SCRUBBER_ENABLED_SIZE_DP);
int defaultScrubberDisabledSize = dpToPx(density, DEFAULT_SCRUBBER_DISABLED_SIZE_DP);
int defaultScrubberDraggedSize = dpToPx(density, DEFAULT_SCRUBBER_DRAGGED_SIZE_DP);
- if (attrs != null) {
- TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DefaultTimeBar, 0,
- 0);
+ if (timebarAttrs != null) {
+ TypedArray a =
+ context.getTheme().obtainStyledAttributes(timebarAttrs, R.styleable.DefaultTimeBar, 0, 0);
try {
scrubberDrawable = a.getDrawable(R.styleable.DefaultTimeBar_scrubber_drawable);
if (scrubberDrawable != null) {
diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java
index 54a592ce6f..b9b2456722 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java
@@ -28,6 +28,7 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -97,6 +98,9 @@ import java.util.Locale;
*
Corresponding method: None
* Default: {@code R.layout.exo_player_control_view}
*
+ * All attributes that can be set on {@link DefaultTimeBar} can also be set on a
+ * PlayerControlView, and will be propagated to the inflated {@link DefaultTimeBar} unless the
+ * layout is overridden to specify a custom {@code exo_progress} (see below).
*
*
* Overriding the layout file
@@ -154,7 +158,15 @@ import java.util.Locale;
*
* - Type: {@link TextView}
*
+ * {@code exo_progress_placeholder} - A placeholder that's replaced with the inflated
+ * {@link DefaultTimeBar}. Ignored if an {@code exo_progress} view exists.
+ *
+ * - Type: {@link View}
+ *
* {@code exo_progress} - Time bar that's updated during playback and allows seeking.
+ * {@link DefaultTimeBar} attributes set on the PlayerControlView will not be automatically
+ * propagated through to this instance. If a view exists with this id, any {@code
+ * exo_progress_placeholder} view will be ignored.
*
* - Type: {@link TimeBar}
*
@@ -330,9 +342,27 @@ public class PlayerControlView extends FrameLayout {
LayoutInflater.from(context).inflate(controllerLayoutId, this);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
+ TimeBar customTimeBar = findViewById(R.id.exo_progress);
+ View timeBarPlaceholder = findViewById(R.id.exo_progress_placeholder);
+ if (customTimeBar != null) {
+ timeBar = customTimeBar;
+ } else if (timeBarPlaceholder != null) {
+ // Propagate attrs as timebarAttrs so that DefaultTimeBar's custom attributes are transferred,
+ // but standard attributes (e.g. background) are not.
+ DefaultTimeBar defaultTimeBar = new DefaultTimeBar(context, null, 0, playbackAttrs);
+ defaultTimeBar.setId(R.id.exo_progress);
+ defaultTimeBar.setLayoutParams(timeBarPlaceholder.getLayoutParams());
+ ViewGroup parent = ((ViewGroup) timeBarPlaceholder.getParent());
+ int timeBarIndex = parent.indexOfChild(timeBarPlaceholder);
+ parent.removeView(timeBarPlaceholder);
+ parent.addView(defaultTimeBar, timeBarIndex);
+ timeBar = defaultTimeBar;
+ } else {
+ timeBar = null;
+ }
durationView = findViewById(R.id.exo_duration);
positionView = findViewById(R.id.exo_position);
- timeBar = findViewById(R.id.exo_progress);
+
if (timeBar != null) {
timeBar.addListener(componentListener);
}
diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
index 21467c3e25..5bb8324780 100644
--- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
+++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
@@ -162,9 +162,10 @@ import java.util.List;
* Corresponding method: None
* Default: {@code R.layout.exo_player_control_view}
*
- * All attributes that can be set on a {@link PlayerControlView} can also be set on a
- * PlayerView, and will be propagated to the inflated {@link PlayerControlView} unless the
- * layout is overridden to specify a custom {@code exo_controller} (see below).
+ * All attributes that can be set on {@link PlayerControlView} and {@link DefaultTimeBar} can
+ * also be set on a PlayerView, and will be propagated to the inflated {@link
+ * PlayerControlView} unless the layout is overridden to specify a custom {@code
+ * exo_controller} (see below).
*
*
* Overriding the layout file
@@ -214,9 +215,10 @@ import java.util.List;
* Type: {@link View}
*
* {@code exo_controller} - An already inflated {@link PlayerControlView}. Allows use
- * of a custom extension of {@link PlayerControlView}. Note that attributes such as {@code
- * rewind_increment} will not be automatically propagated through to this instance. If a view
- * exists with this id, any {@code exo_controller_placeholder} view will be ignored.
+ * of a custom extension of {@link PlayerControlView}. {@link PlayerControlView} and {@link
+ * DefaultTimeBar} attributes set on the PlayerView will not be automatically propagated
+ * through to this instance. If a view exists with this id, any {@code
+ * exo_controller_placeholder} view will be ignored.
*
* - Type: {@link PlayerControlView}
*
@@ -456,8 +458,9 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
this.controller = customController;
} else if (controllerPlaceholder != null) {
// Propagate attrs as playbackAttrs so that PlayerControlView's custom attributes are
- // transferred, but standard FrameLayout attributes (e.g. background) are not.
+ // transferred, but standard attributes (e.g. background) are not.
this.controller = new PlayerControlView(context, null, 0, attrs);
+ controller.setId(R.id.exo_controller);
controller.setLayoutParams(controllerPlaceholder.getLayoutParams());
ViewGroup parent = ((ViewGroup) controllerPlaceholder.getParent());
int controllerIndex = parent.indexOfChild(controllerPlaceholder);
diff --git a/library/ui/src/main/res/layout/exo_playback_control_view.xml b/library/ui/src/main/res/layout/exo_playback_control_view.xml
index ed2fb8e2b2..027e57ee92 100644
--- a/library/ui/src/main/res/layout/exo_playback_control_view.xml
+++ b/library/ui/src/main/res/layout/exo_playback_control_view.xml
@@ -76,8 +76,7 @@
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
-
diff --git a/library/ui/src/main/res/values/attrs.xml b/library/ui/src/main/res/values/attrs.xml
index 27e6a5b3b8..706fba0e0b 100644
--- a/library/ui/src/main/res/values/attrs.xml
+++ b/library/ui/src/main/res/values/attrs.xml
@@ -31,18 +31,36 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -58,9 +76,11 @@
-
+
+
-
+
+
@@ -69,6 +89,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -83,22 +117,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/library/ui/src/main/res/values/ids.xml b/library/ui/src/main/res/values/ids.xml
index e57301f946..17b55cd731 100644
--- a/library/ui/src/main/res/values/ids.xml
+++ b/library/ui/src/main/res/values/ids.xml
@@ -33,6 +33,7 @@
+