Fix some PlayerControlView accessibility issues

- Fix focus when pausing and resuming
- Prevent repeated readout of the playback position when paused

#exofixit
#minor-release
Issue #9111

PiperOrigin-RevId: 395301765
This commit is contained in:
olly 2021-09-07 20:10:47 +01:00 committed by Oliver Woodman
parent e53e59388f
commit 86f8c4e44e
3 changed files with 55 additions and 2 deletions

View File

@ -37,6 +37,8 @@
* Use `defStyleAttr` when obtaining styled attributes in * Use `defStyleAttr` when obtaining styled attributes in
`StyledPlayerView`, `PlayerView` and `PlayerControlView` `StyledPlayerView`, `PlayerView` and `PlayerControlView`
([#9024](https://github.com/google/ExoPlayer/issues/9024)). ([#9024](https://github.com/google/ExoPlayer/issues/9024)).
* Fix accessibility focus in `PlayerControlView`
([#9111](https://github.com/google/ExoPlayer/issues/9111)).
* Remove deprecated symbols: * Remove deprecated symbols:
* Remove `Renderer.VIDEO_SCALING_MODE_*` constants. Use identically named * Remove `Renderer.VIDEO_SCALING_MODE_*` constants. Use identically named
constants in `C` instead. constants in `C` instead.

View File

@ -512,6 +512,9 @@ public class DefaultTimeBar extends View implements TimeBar {
@Override @Override
public void setPosition(long position) { public void setPosition(long position) {
if (this.position == position) {
return;
}
this.position = position; this.position = position;
setContentDescription(getProgressText()); setContentDescription(getProgressText());
update(); update();
@ -519,12 +522,18 @@ public class DefaultTimeBar extends View implements TimeBar {
@Override @Override
public void setBufferedPosition(long bufferedPosition) { public void setBufferedPosition(long bufferedPosition) {
if (this.bufferedPosition == bufferedPosition) {
return;
}
this.bufferedPosition = bufferedPosition; this.bufferedPosition = bufferedPosition;
update(); update();
} }
@Override @Override
public void setDuration(long duration) { public void setDuration(long duration) {
if (this.duration == duration) {
return;
}
this.duration = duration; this.duration = duration;
if (scrubbing && duration == C.TIME_UNSET) { if (scrubbing && duration == C.TIME_UNSET) {
stopScrubbing(/* canceled= */ true); stopScrubbing(/* canceled= */ true);

View File

@ -41,10 +41,13 @@ import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ControlDispatcher; import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ExoPlayer;
@ -339,6 +342,8 @@ public class PlayerControlView extends FrameLayout {
private long[] extraAdGroupTimesMs; private long[] extraAdGroupTimesMs;
private boolean[] extraPlayedAdGroups; private boolean[] extraPlayedAdGroups;
private long currentWindowOffset; private long currentWindowOffset;
private long currentPosition;
private long currentBufferedPosition;
public PlayerControlView(Context context) { public PlayerControlView(Context context) {
this(context, /* attrs= */ null); this(context, /* attrs= */ null);
@ -783,6 +788,7 @@ public class PlayerControlView extends FrameLayout {
} }
updateAll(); updateAll();
requestPlayPauseFocus(); requestPlayPauseFocus();
requestPlayPauseAccessibilityFocus();
} }
// Call hideAfterTimeout even if already visible to reset the timeout. // Call hideAfterTimeout even if already visible to reset the timeout.
hideAfterTimeout(); hideAfterTimeout();
@ -831,18 +837,30 @@ public class PlayerControlView extends FrameLayout {
return; return;
} }
boolean requestPlayPauseFocus = false; boolean requestPlayPauseFocus = false;
boolean requestPlayPauseAccessibilityFocus = false;
boolean shouldShowPauseButton = shouldShowPauseButton(); boolean shouldShowPauseButton = shouldShowPauseButton();
if (playButton != null) { if (playButton != null) {
requestPlayPauseFocus |= shouldShowPauseButton && playButton.isFocused(); requestPlayPauseFocus |= shouldShowPauseButton && playButton.isFocused();
requestPlayPauseAccessibilityFocus |=
Util.SDK_INT < 21
? requestPlayPauseFocus
: (shouldShowPauseButton && Api21.isAccessibilityFocused(playButton));
playButton.setVisibility(shouldShowPauseButton ? GONE : VISIBLE); playButton.setVisibility(shouldShowPauseButton ? GONE : VISIBLE);
} }
if (pauseButton != null) { if (pauseButton != null) {
requestPlayPauseFocus |= !shouldShowPauseButton && pauseButton.isFocused(); requestPlayPauseFocus |= !shouldShowPauseButton && pauseButton.isFocused();
requestPlayPauseAccessibilityFocus |=
Util.SDK_INT < 21
? requestPlayPauseFocus
: (!shouldShowPauseButton && Api21.isAccessibilityFocused(pauseButton));
pauseButton.setVisibility(shouldShowPauseButton ? VISIBLE : GONE); pauseButton.setVisibility(shouldShowPauseButton ? VISIBLE : GONE);
} }
if (requestPlayPauseFocus) { if (requestPlayPauseFocus) {
requestPlayPauseFocus(); requestPlayPauseFocus();
} }
if (requestPlayPauseAccessibilityFocus) {
requestPlayPauseAccessibilityFocus();
}
} }
private void updateNavigation() { private void updateNavigation() {
@ -1021,14 +1039,21 @@ public class PlayerControlView extends FrameLayout {
position = currentWindowOffset + player.getContentPosition(); position = currentWindowOffset + player.getContentPosition();
bufferedPosition = currentWindowOffset + player.getContentBufferedPosition(); bufferedPosition = currentWindowOffset + player.getContentBufferedPosition();
} }
if (positionView != null && !scrubbing) { boolean positionChanged = position != currentPosition;
boolean bufferedPositionChanged = bufferedPosition != currentBufferedPosition;
currentPosition = position;
currentBufferedPosition = bufferedPosition;
// Only update the TextView if the position has changed, else TalkBack will repeatedly read the
// same position to the user.
if (positionView != null && !scrubbing && positionChanged) {
positionView.setText(Util.getStringForTime(formatBuilder, formatter, position)); positionView.setText(Util.getStringForTime(formatBuilder, formatter, position));
} }
if (timeBar != null) { if (timeBar != null) {
timeBar.setPosition(position); timeBar.setPosition(position);
timeBar.setBufferedPosition(bufferedPosition); timeBar.setBufferedPosition(bufferedPosition);
} }
if (progressUpdateListener != null) { if (progressUpdateListener != null && (positionChanged || bufferedPositionChanged)) {
progressUpdateListener.onProgressUpdate(position, bufferedPosition); progressUpdateListener.onProgressUpdate(position, bufferedPosition);
} }
@ -1065,6 +1090,15 @@ public class PlayerControlView extends FrameLayout {
} }
} }
private void requestPlayPauseAccessibilityFocus() {
boolean shouldShowPauseButton = shouldShowPauseButton();
if (!shouldShowPauseButton && playButton != null) {
playButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
} else if (shouldShowPauseButton && pauseButton != null) {
pauseButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
}
private void updateButton(boolean visible, boolean enabled, @Nullable View view) { private void updateButton(boolean visible, boolean enabled, @Nullable View view) {
if (view == null) { if (view == null) {
return; return;
@ -1339,4 +1373,12 @@ public class PlayerControlView extends FrameLayout {
} }
} }
} }
@RequiresApi(21)
private static final class Api21 {
@DoNotInline
public static boolean isAccessibilityFocused(View view) {
return view.isAccessibilityFocused();
}
}
} }