Commit 674e92e1 by bachinger Committed by Oliver Woodman

provide content description for the player view to make show/hide controls accessible

PiperOrigin-RevId: 274148026
parent a268e1b6
...@@ -110,6 +110,7 @@ ...@@ -110,6 +110,7 @@
* Add `MediaPeriod.isLoading` to improve `Player.isLoading` state. * Add `MediaPeriod.isLoading` to improve `Player.isLoading` state.
* Add support for ID3-in-EMSG in HLS streams * Add support for ID3-in-EMSG in HLS streams
([spec](https://aomediacodec.github.io/av1-id3/)). ([spec](https://aomediacodec.github.io/av1-id3/)).
* Make show and hide player controls accessible for TalkBack in `PlayerView`.
### 2.10.5 (2019-09-20) ### ### 2.10.5 (2019-09-20) ###
......
...@@ -43,6 +43,7 @@ import com.google.android.exoplayer2.util.Util; ...@@ -43,6 +43,7 @@ import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Formatter; import java.util.Formatter;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* A view for controlling {@link Player} instances. * A view for controlling {@link Player} instances.
...@@ -231,6 +232,7 @@ public class PlayerControlView extends FrameLayout { ...@@ -231,6 +232,7 @@ public class PlayerControlView extends FrameLayout {
private static final int MAX_UPDATE_INTERVAL_MS = 1000; private static final int MAX_UPDATE_INTERVAL_MS = 1000;
private final ComponentListener componentListener; private final ComponentListener componentListener;
private final CopyOnWriteArrayList<VisibilityListener> visibilityListeners;
private final View previousButton; private final View previousButton;
private final View nextButton; private final View nextButton;
private final View playButton; private final View playButton;
...@@ -265,7 +267,6 @@ public class PlayerControlView extends FrameLayout { ...@@ -265,7 +267,6 @@ public class PlayerControlView extends FrameLayout {
@Nullable private Player player; @Nullable private Player player;
private com.google.android.exoplayer2.ControlDispatcher controlDispatcher; private com.google.android.exoplayer2.ControlDispatcher controlDispatcher;
@Nullable private VisibilityListener visibilityListener;
@Nullable private ProgressUpdateListener progressUpdateListener; @Nullable private ProgressUpdateListener progressUpdateListener;
@Nullable private PlaybackPreparer playbackPreparer; @Nullable private PlaybackPreparer playbackPreparer;
...@@ -335,6 +336,7 @@ public class PlayerControlView extends FrameLayout { ...@@ -335,6 +336,7 @@ public class PlayerControlView extends FrameLayout {
a.recycle(); a.recycle();
} }
} }
visibilityListeners = new CopyOnWriteArrayList<>();
period = new Timeline.Period(); period = new Timeline.Period();
window = new Timeline.Window(); window = new Timeline.Window();
formatBuilder = new StringBuilder(); formatBuilder = new StringBuilder();
...@@ -510,13 +512,21 @@ public class PlayerControlView extends FrameLayout { ...@@ -510,13 +512,21 @@ public class PlayerControlView extends FrameLayout {
} }
/** /**
* Sets the {@link VisibilityListener}. * Adds a {@link VisibilityListener}.
* *
* @param listener The listener to be notified about visibility changes, or null to remove the * @param listener The listener to be notified about visibility changes.
* current listener.
*/ */
public void setVisibilityListener(@Nullable VisibilityListener listener) { public void addVisibilityListener(VisibilityListener listener) {
this.visibilityListener = listener; visibilityListeners.add(listener);
}
/**
* Removes a {@link VisibilityListener}.
*
* @param listener The listener to be removed.
*/
public void removeVisibilityListener(VisibilityListener listener) {
visibilityListeners.remove(listener);
} }
/** /**
...@@ -697,7 +707,7 @@ public class PlayerControlView extends FrameLayout { ...@@ -697,7 +707,7 @@ public class PlayerControlView extends FrameLayout {
public void show() { public void show() {
if (!isVisible()) { if (!isVisible()) {
setVisibility(VISIBLE); setVisibility(VISIBLE);
if (visibilityListener != null) { for (VisibilityListener visibilityListener : visibilityListeners) {
visibilityListener.onVisibilityChange(getVisibility()); visibilityListener.onVisibilityChange(getVisibility());
} }
updateAll(); updateAll();
...@@ -711,7 +721,7 @@ public class PlayerControlView extends FrameLayout { ...@@ -711,7 +721,7 @@ public class PlayerControlView extends FrameLayout {
public void hide() { public void hide() {
if (isVisible()) { if (isVisible()) {
setVisibility(GONE); setVisibility(GONE);
if (visibilityListener != null) { for (VisibilityListener visibilityListener : visibilityListeners) {
visibilityListener.onVisibilityChange(getVisibility()); visibilityListener.onVisibilityChange(getVisibility());
} }
removeCallbacks(updateProgressAction); removeCallbacks(updateProgressAction);
......
...@@ -294,6 +294,7 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -294,6 +294,7 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
private Player player; private Player player;
private boolean useController; private boolean useController;
@Nullable private PlayerControlView.VisibilityListener controllerVisibilityListener;
private boolean useArtwork; private boolean useArtwork;
@Nullable private Drawable defaultArtwork; @Nullable private Drawable defaultArtwork;
private @ShowBuffering int showBuffering; private @ShowBuffering int showBuffering;
...@@ -483,6 +484,10 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -483,6 +484,10 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
this.controllerHideDuringAds = controllerHideDuringAds; this.controllerHideDuringAds = controllerHideDuringAds;
this.useController = useController && controller != null; this.useController = useController && controller != null;
hideController(); hideController();
updateContentDescription();
if (controller != null) {
controller.addVisibilityListener(/* listener= */ componentListener);
}
} }
/** /**
...@@ -687,8 +692,9 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -687,8 +692,9 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
controller.setPlayer(player); controller.setPlayer(player);
} else if (controller != null) { } else if (controller != null) {
controller.hide(); controller.hide();
controller.setPlayer(null); controller.setPlayer(/* player= */ null);
} }
updateContentDescription();
} }
/** /**
...@@ -880,6 +886,7 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -880,6 +886,7 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
public void setControllerHideOnTouch(boolean controllerHideOnTouch) { public void setControllerHideOnTouch(boolean controllerHideOnTouch) {
Assertions.checkState(controller != null); Assertions.checkState(controller != null);
this.controllerHideOnTouch = controllerHideOnTouch; this.controllerHideOnTouch = controllerHideOnTouch;
updateContentDescription();
} }
/** /**
...@@ -921,7 +928,16 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -921,7 +928,16 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
public void setControllerVisibilityListener( public void setControllerVisibilityListener(
@Nullable PlayerControlView.VisibilityListener listener) { @Nullable PlayerControlView.VisibilityListener listener) {
Assertions.checkState(controller != null); Assertions.checkState(controller != null);
controller.setVisibilityListener(listener); if (this.controllerVisibilityListener == listener) {
return;
}
if (this.controllerVisibilityListener != null) {
controller.removeVisibilityListener(this.controllerVisibilityListener);
}
this.controllerVisibilityListener = listener;
if (listener != null) {
controller.addVisibilityListener(listener);
}
} }
/** /**
...@@ -1359,6 +1375,20 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -1359,6 +1375,20 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
} }
} }
private void updateContentDescription() {
if (controller == null || !useController) {
setContentDescription(/* contentDescription= */ null);
} else if (controller.getVisibility() == View.VISIBLE) {
setContentDescription(
/* contentDescription= */ controllerHideOnTouch
? getResources().getString(R.string.exo_controls_hide)
: null);
} else {
setContentDescription(
/* contentDescription= */ getResources().getString(R.string.exo_controls_show));
}
}
@TargetApi(23) @TargetApi(23)
private static void configureEditModeLogoV23(Resources resources, ImageView logo) { private static void configureEditModeLogoV23(Resources resources, ImageView logo) {
logo.setImageDrawable(resources.getDrawable(R.drawable.exo_edit_mode_logo, null)); logo.setImageDrawable(resources.getDrawable(R.drawable.exo_edit_mode_logo, null));
...@@ -1418,7 +1448,8 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -1418,7 +1448,8 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
TextOutput, TextOutput,
VideoListener, VideoListener,
OnLayoutChangeListener, OnLayoutChangeListener,
SingleTapListener { SingleTapListener,
PlayerControlView.VisibilityListener {
// TextOutput implementation // TextOutput implementation
...@@ -1513,5 +1544,12 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider ...@@ -1513,5 +1544,12 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
public boolean onSingleTapUp(MotionEvent e) { public boolean onSingleTapUp(MotionEvent e) {
return toggleControllerVisibility(); return toggleControllerVisibility();
} }
// PlayerControlView.VisibilityListener implementation
@Override
public void onVisibilityChange(int visibility) {
updateContentDescription();
}
} }
} }
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
limitations under the License. limitations under the License.
--> -->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Description for a media player view to indicate that tapping on the view shows the playback controls. [CHAR LIMIT=50] -->
<string name="exo_controls_show">Show player controls</string>
<!-- Description for a media player view to indicate that tapping on the view hides the playback controls [CHAR LIMIT=50] -->
<string name="exo_controls_hide">Hide player controls</string>
<!-- Description for a media control button that causes the previous track to be played. [CHAR LIMIT=30] --> <!-- Description for a media control button that causes the previous track to be played. [CHAR LIMIT=30] -->
<string name="exo_controls_previous_description">Previous track</string> <string name="exo_controls_previous_description">Previous track</string>
<!-- Description for a media control button that causes the next track to be played. [CHAR LIMIT=30] --> <!-- Description for a media control button that causes the next track to be played. [CHAR LIMIT=30] -->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment