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
`StyledPlayerView`, `PlayerView` and `PlayerControlView`
([#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 `Renderer.VIDEO_SCALING_MODE_*` constants. Use identically named
constants in `C` instead.

View File

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

View File

@ -41,10 +41,13 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ControlDispatcher;
import com.google.android.exoplayer2.ExoPlayer;
@ -339,6 +342,8 @@ public class PlayerControlView extends FrameLayout {
private long[] extraAdGroupTimesMs;
private boolean[] extraPlayedAdGroups;
private long currentWindowOffset;
private long currentPosition;
private long currentBufferedPosition;
public PlayerControlView(Context context) {
this(context, /* attrs= */ null);
@ -783,6 +788,7 @@ public class PlayerControlView extends FrameLayout {
}
updateAll();
requestPlayPauseFocus();
requestPlayPauseAccessibilityFocus();
}
// Call hideAfterTimeout even if already visible to reset the timeout.
hideAfterTimeout();
@ -831,18 +837,30 @@ public class PlayerControlView extends FrameLayout {
return;
}
boolean requestPlayPauseFocus = false;
boolean requestPlayPauseAccessibilityFocus = false;
boolean shouldShowPauseButton = shouldShowPauseButton();
if (playButton != null) {
requestPlayPauseFocus |= shouldShowPauseButton && playButton.isFocused();
requestPlayPauseAccessibilityFocus |=
Util.SDK_INT < 21
? requestPlayPauseFocus
: (shouldShowPauseButton && Api21.isAccessibilityFocused(playButton));
playButton.setVisibility(shouldShowPauseButton ? GONE : VISIBLE);
}
if (pauseButton != null) {
requestPlayPauseFocus |= !shouldShowPauseButton && pauseButton.isFocused();
requestPlayPauseAccessibilityFocus |=
Util.SDK_INT < 21
? requestPlayPauseFocus
: (!shouldShowPauseButton && Api21.isAccessibilityFocused(pauseButton));
pauseButton.setVisibility(shouldShowPauseButton ? VISIBLE : GONE);
}
if (requestPlayPauseFocus) {
requestPlayPauseFocus();
}
if (requestPlayPauseAccessibilityFocus) {
requestPlayPauseAccessibilityFocus();
}
}
private void updateNavigation() {
@ -1021,14 +1039,21 @@ public class PlayerControlView extends FrameLayout {
position = currentWindowOffset + player.getContentPosition();
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));
}
if (timeBar != null) {
timeBar.setPosition(position);
timeBar.setBufferedPosition(bufferedPosition);
}
if (progressUpdateListener != null) {
if (progressUpdateListener != null && (positionChanged || bufferedPositionChanged)) {
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) {
if (view == null) {
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();
}
}
}