[Android] Correctly display fractions in right-to-left mode
Robert Stone
git at videolan.org
Thu Nov 20 06:53:55 UTC 2025
vlc-android | branch: master | Robert Stone <rhstone at gmail.com> | Fri Nov 7 10:42:17 2025 -0800| [68387cfe41e0e1c5fc715fe3a1b5ad059dee4705] | committer: Nicolas Pomepuy
Correctly display fractions in right-to-left mode
> https://code.videolan.org/videolan/vlc-android/commit/68387cfe41e0e1c5fc715fe3a1b5ad059dee4705
---
.../src/main/java/org/videolan/tools/Strings.kt | 22 +++++++++++++-------
.../src/org/videolan/vlc/gui/audio/AudioPlayer.kt | 24 +++++++++++++++++-----
2 files changed, 34 insertions(+), 12 deletions(-)
diff --git a/application/tools/src/main/java/org/videolan/tools/Strings.kt b/application/tools/src/main/java/org/videolan/tools/Strings.kt
index a8644d85a3..9c4747aa95 100644
--- a/application/tools/src/main/java/org/videolan/tools/Strings.kt
+++ b/application/tools/src/main/java/org/videolan/tools/Strings.kt
@@ -120,21 +120,29 @@ fun Long.readableNumber(): String {
fun Int.forbiddenChars() = FORBIDDEN_CHARS.substrlng(this)
-fun String.markBidi(): String {
+fun String.markBidi(markLtr: Boolean = false): String {
+ //left-to-right isolate
+ val lri = "\u2066"
//right-to-left isolate
val rli = "\u2067"
//pop directional isolate
val pdi = "\u2069"
- for (ch in this) {
+ return when {
+ markLtr -> lri + this + pdi
+ this.hasRtl() -> rli + this + pdi
+ else -> this
+ }
+}
+
+fun String.hasRtl(): Boolean {
+ return this.toCharArray().any { ch ->
when (Character.getDirectionality(ch)) {
Character.DIRECTIONALITY_RIGHT_TO_LEFT,
Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC,
Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING,
- Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE -> return rli + this + pdi
- Character.DIRECTIONALITY_LEFT_TO_RIGHT,
- Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING,
- Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE -> return this
+ Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE,
+ Character.DIRECTIONALITY_RIGHT_TO_LEFT_ISOLATE -> return true
+ else -> false
}
}
- return this
}
\ No newline at end of file
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt
index 6a0a05679a..edd7322e53 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayer.kt
@@ -87,7 +87,9 @@ import org.videolan.tools.Settings
import org.videolan.tools.copy
import org.videolan.tools.dp
import org.videolan.tools.formatRateString
+import org.videolan.tools.hasRtl
import org.videolan.tools.isStarted
+import org.videolan.tools.markBidi
import org.videolan.tools.putSingle
import org.videolan.tools.setGone
import org.videolan.tools.setVisible
@@ -647,8 +649,11 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, IAudioPlay
val progressTimeDescription = TalkbackUtil.millisToString(requireActivity(), if (showRemainingTime && totalTime > 0) totalTime - progressTime else progressTime)
val currentProgressText = if (progressTimeText.isNullOrEmpty()) "0:00" else progressTimeText
+ val isRtlLocale = LocaleUtil.isRtl()
val size = if (playlistModel.service?.playlistManager?.stopAfter != -1 ) (playlistModel.service?.playlistManager?.stopAfter ?: 0) + 1 else medias.size
- val textTrack = getString(R.string.track_index, "${playlistModel.currentMediaPosition + 1} / $size")
+ val textTrack = getString(R.string.track_index, "${playlistModel.currentMediaPosition + 1} / $size".let {
+ if (isRtlLocale) it.markBidi(true) else it
+ })
val textTrackDescription = getString(R.string.talkback_track_index, "${playlistModel.currentMediaPosition + 1}", "$size")
val textProgress = if (audioPlayProgressMode) {
@@ -656,7 +661,9 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, IAudioPlay
if ((lastEndsAt - endsAt).absoluteValue > 1) lastEndsAt = endsAt
getString(
R.string.audio_queue_progress_finished,
- getTimeInstance(java.text.DateFormat.MEDIUM).format(lastEndsAt)
+ getTimeInstance(java.text.DateFormat.MEDIUM).format(lastEndsAt).let {
+ if (isRtlLocale) it.markBidi(true) else it
+ }
)
} else
if (showRemainingTime && totalTime > 0) getString(
@@ -665,14 +672,18 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, IAudioPlay
)
else getString(
R.string.audio_queue_progress,
- if (totalTimeText.isNullOrEmpty()) currentProgressText else "$currentProgressText / $totalTimeText"
+ if (totalTimeText.isNullOrEmpty()) currentProgressText else "$currentProgressText / $totalTimeText".let {
+ if (isRtlLocale) it.markBidi(true) else it
+ }
)
val textDescription = if (audioPlayProgressMode) {
val endsAt = System.currentTimeMillis() + totalTime - progressTime
if ((lastEndsAt - endsAt).absoluteValue > 1) lastEndsAt = endsAt
getString(
R.string.audio_queue_progress_finished,
- getTimeInstance(java.text.DateFormat.MEDIUM).format(lastEndsAt)
+ getTimeInstance(java.text.DateFormat.MEDIUM).format(lastEndsAt).let {
+ if (isRtlLocale) it.markBidi(true) else it
+ }
)
} else
if (showRemainingTime && totalTime > 0) getString(
@@ -683,7 +694,10 @@ class AudioPlayer : Fragment(), PlaylistAdapter.IPlayer, TextWatcher, IAudioPlay
R.string.audio_queue_progress,
if (totalTimeText.isNullOrEmpty()) progressTimeDescription else getString(R.string.talkback_out_of, progressTimeDescription, totalTimeDescription)
)
- Pair("$textTrack ${TextUtils.SEPARATOR} $textProgress", "$textTrackDescription. $textDescription")
+
+ val finalTextTrack = if (isRtlLocale && !textTrack.hasRtl()) textTrack.markBidi(true) else textTrack
+ val finalTextProgress = if (isRtlLocale && !textProgress.hasRtl()) textProgress.markBidi(true) else textProgress
+ Pair("$finalTextTrack ${TextUtils.SEPARATOR} $finalTextProgress", "$textTrackDescription. $textDescription")
}
binding.audioPlayProgress.text = text.first
binding.audioPlayProgress.contentDescription = text.second
More information about the Android
mailing list