Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4438,6 +4438,22 @@ public MessagesController.SavedMusicList getMusicList() {
return currentSavedMusicList;
}

// how far the message got, for screen readers, while it is playing or paused
public static CharSequence getPlaybackPositionDescription(MessageObject messageObject) {
if (messageObject == null || !getInstance().isPlayingMessage(messageObject)) {
return null;
}
final int duration = (int) messageObject.getDuration();
// the seconds are counted as it plays, while moving the slider takes it somewhere else
// without counting: where the slider stands is what holds in both
int position = messageObject.audioProgressSec;
if (duration > 0 && messageObject.audioProgress > 0) {
position = Math.round(messageObject.audioProgress * duration);
}
position = Math.max(0, Math.min(duration, position));
return LocaleController.formatString(R.string.AccDescrPlayerDuration, LocaleController.formatDuration(position), LocaleController.formatDuration(duration));
}

public boolean isPlayingMessage(MessageObject messageObject) {
if (messageObject != null && messageObject.isRepostPreview) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,17 @@ public int getObserverTag() {
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
CharSequence text;
if (currentMessageObject.isMusic()) {
info.setText(LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle()));
text = LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle());
} else { // voice message
info.setText(titleLayout.getText() + ", " + descriptionLayout.getText());
text = titleLayout.getText() + ", " + descriptionLayout.getText();
}
final CharSequence playbackPosition = MediaController.getPlaybackPositionDescription(currentMessageObject);
if (playbackPosition != null) {
text = playbackPosition + ", " + text;
}
info.setText(text);
}

private int getThemedColor(int key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6342,6 +6342,10 @@ public TLRPC.Chat getCurrentChat() {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();

accessibilityFocused = false;
accessibilityHovered = false;
readPlaybackPosition = null;

NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.startSpoilers);
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.stopSpoilers);
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded);
Expand Down Expand Up @@ -26347,6 +26351,12 @@ public int getLayoutHeight() {

@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
accessibilityFocused = true;
} else if (action == AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) {
accessibilityFocused = false;
readPlaybackPosition = null;
}
if (delegate != null && delegate.onAccessibilityAction(action, arguments)) {
return false;
}
Expand Down Expand Up @@ -26396,6 +26406,7 @@ public boolean performAccessibilityAction(int action, Bundle arguments) {
}
if (currentMessageObject.isVoice() || currentMessageObject.isRoundVideo() || currentMessageObject.isMusic() && MediaController.getInstance().isPlayingMessage(currentMessageObject)) {
if (seekBarAccessibilityDelegate.performAccessibilityActionInternal(action, arguments)) {
announcePlaybackPosition();
return true;
}
}
Expand Down Expand Up @@ -26424,6 +26435,7 @@ public boolean onHoverEvent(MotionEvent event) {
int x = (int) getEventX(event);
int y = (int) getEventY(event);
if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER || event.getAction() == MotionEvent.ACTION_HOVER_MOVE) {
accessibilityHovered = true;
for (int i = 0; i < accessibilityVirtualViewBounds.size(); i++) {
Rect rect = accessibilityVirtualViewBounds.valueAt(i);
if (rect.contains(x, y)) {
Expand All @@ -26437,10 +26449,59 @@ public boolean onHoverEvent(MotionEvent event) {
}
} else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
currentFocusedVirtualView = 0;
accessibilityHovered = false;
readPlaybackPosition = null;
}
return super.onHoverEvent(event);
}

private boolean accessibilityFocused;
private boolean accessibilityHovered;
private CharSequence readPlaybackPosition;

private boolean isReadByAccessibility() {
return accessibilityFocused || accessibilityHovered || isAccessibilityFocused();
}

private Runnable announcePlaybackPositionRunnable;

// after seeking, only the new position is of use, so report it instead of leaving it to
// the screen reader, which would read the whole message again
private CharSequence seekBarPositionDescription() {
if (currentMessageObject == null) {
return null;
}
final int duration = (int) currentMessageObject.getDuration();
if (duration <= 0 || seekBarAccessibilityDelegate == null) {
return null;
}
final float progress = currentMessageObject.isMusic() || currentMessageObject.isVoice() && !useSeekBarWaveform ? seekBar.getProgress() : (useSeekBarWaveform ? seekBarWaveform.getProgress() : currentMessageObject.audioProgress);
final int position = Math.max(0, Math.min(duration, Math.round(progress * duration)));
return formatString(R.string.AccDescrPlayerDuration, LocaleController.formatDuration(position), LocaleController.formatDuration(duration));
}

private void announcePlaybackPosition() {
// the position kept while the message is read is the one before the seek
readPlaybackPosition = null;
if (announcePlaybackPositionRunnable != null) {
removeCallbacks(announcePlaybackPositionRunnable);
}
announcePlaybackPositionRunnable = () -> {
announcePlaybackPositionRunnable = null;
readPlaybackPosition = null;
CharSequence playbackPosition = MediaController.getPlaybackPositionDescription(currentMessageObject);
if (playbackPosition == null) {
// a message that was never played is moved by its own slider, and nothing else
// knows where it stands
playbackPosition = seekBarPositionDescription();
}
if (playbackPosition != null) {
announceForAccessibility(playbackPosition);
}
};
postDelayed(announcePlaybackPositionRunnable, 400);
}

@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
Expand Down Expand Up @@ -27098,10 +27159,27 @@ public void onClick(View view) {
accessibilityTextFileSize = fileSize;
}

CharSequence spokenText = accessibilityText;
CharSequence playbackPosition = MediaController.getPlaybackPositionDescription(currentMessageObject);
if (playbackPosition != null) {
// while the message is being read, keep reporting the position it was reached
// at: a position that moves on every request looks like the message itself
// changed and has screen readers read all of it again
if (isReadByAccessibility() && !MediaController.getInstance().isMessagePaused()) {
if (readPlaybackPosition == null) {
readPlaybackPosition = playbackPosition;
}
playbackPosition = readPlaybackPosition;
} else {
readPlaybackPosition = null;
}
spokenText = TextUtils.concat(playbackPosition, ", ", accessibilityText);
}

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
info.setContentDescription(accessibilityText.toString());
info.setContentDescription(spokenText.toString());
} else {
info.setText(accessibilityText);
info.setText(spokenText);
}

info.setEnabled(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,8 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
sbuf.append(descriptionLayout.getText());
}
}
info.setText(sbuf);
final CharSequence playbackPosition = MediaController.getPlaybackPositionDescription(currentMessageObject);
info.setText(playbackPosition != null ? playbackPosition + ", " + sbuf : sbuf);
if (checkBox != null && checkBox.isChecked()) {
info.setCheckable(true);
info.setChecked(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -609,10 +609,18 @@ protected boolean needPlayMessage(MessageObject messageObject) {
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setEnabled(true);
CharSequence text = null;
if (currentMessageObject.isMusic()) {
info.setText(LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle()));
text = LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle());
} else if (titleLayout != null && descriptionLayout != null) {
info.setText(titleLayout.getText() + ", " + descriptionLayout.getText());
text = titleLayout.getText() + ", " + descriptionLayout.getText();
}
if (text != null) {
final CharSequence playbackPosition = MediaController.getPlaybackPositionDescription(currentMessageObject);
if (playbackPosition != null) {
text = playbackPosition + ", " + text;
}
info.setText(text);
}
if (checkBox.isChecked()) {
info.setCheckable(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,22 @@ protected boolean verifyDrawable(@NonNull Drawable who) {
@Override
protected TextView createTextView() {
TextView textView = new TextView(context);
// the switcher only holds the text views, so the position has to be told
// by the view a screen reader actually lands on
textView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
if (currentStyle != STYLE_AUDIO_PLAYER) {
return;
}
final CharSequence text = ((TextView) host).getText();
final CharSequence playbackPosition = MediaController.getPlaybackPositionDescription(MediaController.getInstance().getPlayingMessageObject());
if (playbackPosition != null && !TextUtils.isEmpty(text)) {
info.setText(playbackPosition + ", " + text);
}
}
});
textView.setMaxLines(1);
textView.setLines(1);
textView.setSingleLine(true);
Expand Down