[Android] Accessibility: improve talkback for the video tracks sheet

Nicolas Pomepuy git at videolan.org
Fri Jun 3 11:30:21 UTC 2022


vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Tue May 24 08:28:16 2022 +0200| [7627a9e6a1f504fea4d3acc939a74702100ba02f] | committer: Nicolas Pomepuy

Accessibility: improve talkback for the video tracks sheet

Auto expand the track options and improve the content descriptions

> https://code.videolan.org/videolan/vlc-android/commit/7627a9e6a1f504fea4d3acc939a74702100ba02f
---

 .../resources/src/main/res/values/strings.xml       |  6 +++++-
 .../vlc-android/res/layout/video_track_item.xml     |  4 ++++
 .../videolan/vlc/gui/dialogs/VideoTracksDialog.kt   | 21 ++++++++++++++++++---
 .../vlc/gui/dialogs/adapters/TrackAdapter.kt        |  5 ++++-
 .../vlc/gui/view/CollapsibleLinearLayout.kt         | 14 ++++++++++++++
 5 files changed, 45 insertions(+), 5 deletions(-)

diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml
index d4c083288..6503c00ad 100644
--- a/application/resources/src/main/res/values/strings.xml
+++ b/application/resources/src/main/res/values/strings.xml
@@ -1002,7 +1002,11 @@
     <string name="talkback_file_size">File size: %s</string>
     <string name="talkback_open_in_browser">Open in web browser</string>
     <string name="talkback_video_player">Video player. Tap to show controls. Tap the back button to hide them</string>
-
+    <string name="talkback_video_tracks">Video tracks</string>
+    <string name="talkback_audio_tracks">Audio tracks</string>
+    <string name="talkback_subtitle_tracks">Subtitle tracks</string>
+    <string name="talkback_track" translatable="false">%s: %s. %s</string>
+    <string name="selected">Selected</string>
 
 
 </resources>
diff --git a/application/vlc-android/res/layout/video_track_item.xml b/application/vlc-android/res/layout/video_track_item.xml
index e5331810f..e989d63da 100644
--- a/application/vlc-android/res/layout/video_track_item.xml
+++ b/application/vlc-android/res/layout/video_track_item.xml
@@ -15,6 +15,9 @@
         <variable
                 name="selected"
                 type="Boolean" />
+        <variable
+                name="contentDescription"
+                type="String" />
     </data>
 
     <androidx.constraintlayout.widget.ConstraintLayout
@@ -45,6 +48,7 @@
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_marginStart="16dp"
+                android:contentDescription="@{contentDescription}"
                 android:text="@{track.id == -1 ? @string/disable_track : track.name}"
                 android:textColor="@{selected ? @color/white : @color/white_transparent_50}"
                 android:maxLines="1"
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/VideoTracksDialog.kt b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/VideoTracksDialog.kt
index 08c018429..603df6cbc 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/VideoTracksDialog.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/VideoTracksDialog.kt
@@ -48,6 +48,7 @@ import org.videolan.vlc.R
 import org.videolan.vlc.databinding.PlayerOverlayTracksBinding
 import org.videolan.vlc.gui.dialogs.adapters.TrackAdapter
 import org.videolan.vlc.gui.helpers.getBitmapFromDrawable
+import org.videolan.vlc.util.isTalkbackIsEnabled
 
 class VideoTracksDialog : VLCBottomSheetDialogFragment() {
     override fun getDefaultState(): Int = STATE_EXPANDED
@@ -73,22 +74,24 @@ class VideoTracksDialog : VLCBottomSheetDialogFragment() {
             }
 
             playbackService.videoTracks?.let { trackList ->
-                val trackAdapter = TrackAdapter(trackList as Array<MediaPlayer.TrackDescription>, trackList.firstOrNull { it.id == playbackService.videoTrack })
+                val trackAdapter = TrackAdapter(trackList as Array<MediaPlayer.TrackDescription>, trackList.firstOrNull { it.id == playbackService.videoTrack }, getString(R.string.track_video))
                 trackAdapter.setOnTrackSelectedListener { track ->
                     trackSelectionListener.invoke(track.id, TrackType.VIDEO)
                 }
                 binding.videoTracks.trackList.adapter = trackAdapter
+                binding.videoTracks.trackTitle.contentDescription = getString(R.string.talkback_video_tracks)
             }
             playbackService.audioTracks?.let { trackList ->
-                val trackAdapter = TrackAdapter(trackList as Array<MediaPlayer.TrackDescription>, trackList.firstOrNull { it.id == playbackService.audioTrack })
+                val trackAdapter = TrackAdapter(trackList as Array<MediaPlayer.TrackDescription>, trackList.firstOrNull { it.id == playbackService.audioTrack }, getString(R.string.track_audio))
                 trackAdapter.setOnTrackSelectedListener { track ->
                     trackSelectionListener.invoke(track.id, TrackType.AUDIO)
                 }
                 binding.audioTracks.trackList.adapter = trackAdapter
+                binding.audioTracks.trackTitle.contentDescription = getString(R.string.talkback_audio_tracks)
             }
             playbackService.spuTracks?.let { trackList ->
                 if (!playbackService.hasRenderer()) {
-                    val trackAdapter = TrackAdapter(trackList as Array<MediaPlayer.TrackDescription>, trackList.firstOrNull { it.id == playbackService.spuTrack })
+                    val trackAdapter = TrackAdapter(trackList as Array<MediaPlayer.TrackDescription>, trackList.firstOrNull { it.id == playbackService.spuTrack }, getString(R.string.track_text))
                     trackAdapter.setOnTrackSelectedListener { track ->
                         trackSelectionListener.invoke(track.id, TrackType.SPU)
                     }
@@ -99,6 +102,7 @@ class VideoTracksDialog : VLCBottomSheetDialogFragment() {
                     binding.subtitleTracks.trackMore.setGone()
                 }
                 if (trackList.isEmpty()) binding.subtitleTracks.emptyView.setVisible()
+                binding.subtitleTracks.trackTitle.contentDescription = getString(R.string.talkback_subtitle_tracks)
             }
             if (playbackService.spuTracks == null) binding.subtitleTracks.emptyView.setVisible()
         }
@@ -148,6 +152,17 @@ class VideoTracksDialog : VLCBottomSheetDialogFragment() {
             binding.subtitleTracks.options.collapse()
         }
 
+        if (requireActivity().isTalkbackIsEnabled()) {
+            binding.subtitleTracks.options.onReady {
+                binding.subtitleTracks.trackMore.setGone()
+                binding.subtitleTracks.options.lock()
+            }
+            binding.audioTracks.options.onReady {
+                binding.audioTracks.trackMore.setGone()
+                binding.audioTracks.options.lock()
+            }
+        }
+
         binding.subtitleTracks.trackMore.setOnClickListener {
             binding.subtitleTracks.options.toggle()
             binding.audioTracks.options.collapse()
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/adapters/TrackAdapter.kt b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/adapters/TrackAdapter.kt
index 40b9b6d55..8eeff2182 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/dialogs/adapters/TrackAdapter.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/dialogs/adapters/TrackAdapter.kt
@@ -30,10 +30,11 @@ import android.view.ViewGroup
 import androidx.recyclerview.widget.RecyclerView
 import org.videolan.libvlc.MediaPlayer
 import org.videolan.tools.Settings
+import org.videolan.vlc.R
 import org.videolan.vlc.databinding.VideoTrackItemBinding
 import org.videolan.vlc.gui.helpers.enableMarqueeEffect
 
-class TrackAdapter(private val tracks: Array<MediaPlayer.TrackDescription>, var selectedTrack: MediaPlayer.TrackDescription?) : RecyclerView.Adapter<TrackAdapter.ViewHolder>() {
+class TrackAdapter(private val tracks: Array<MediaPlayer.TrackDescription>, var selectedTrack: MediaPlayer.TrackDescription?, val trackTypePrefix:String) : RecyclerView.Adapter<TrackAdapter.ViewHolder>() {
 
     lateinit var trackSelectedListener: (MediaPlayer.TrackDescription) -> Unit
     private val handler by lazy(LazyThreadSafetyMode.NONE) { Handler() }
@@ -73,6 +74,8 @@ class TrackAdapter(private val tracks: Array<MediaPlayer.TrackDescription>, var
 
         fun bind(trackDescription: MediaPlayer.TrackDescription, selected: Boolean) {
             binding.track = trackDescription
+            val context = binding.root.context
+            binding.contentDescription = context.getString(R.string.talkback_track, trackTypePrefix, if (trackDescription.id == -1) context.getString(R.string.disable_track) else trackDescription.name, if (selected) context.getString(R.string.selected) else "")
             binding.selected = selected
             binding.executePendingBindings()
         }
diff --git a/application/vlc-android/src/org/videolan/vlc/gui/view/CollapsibleLinearLayout.kt b/application/vlc-android/src/org/videolan/vlc/gui/view/CollapsibleLinearLayout.kt
index 896fe06aa..6452f281d 100644
--- a/application/vlc-android/src/org/videolan/vlc/gui/view/CollapsibleLinearLayout.kt
+++ b/application/vlc-android/src/org/videolan/vlc/gui/view/CollapsibleLinearLayout.kt
@@ -34,6 +34,8 @@ import androidx.constraintlayout.widget.ConstraintLayout
 
 class CollapsibleLinearLayout : LinearLayout {
 
+    private var locked: Boolean = false
+    private var onReadyListener: (() -> Unit)? = null
     var isCollapsed = true
     private var animationUpdateListener: ((Float) -> Unit)? = null
     private var maxHeight: Int = -1
@@ -49,6 +51,10 @@ class CollapsibleLinearLayout : LinearLayout {
         }
     }
 
+    fun onReady(listener:() -> Unit) {
+        this.onReadyListener = listener
+    }
+
     fun setAnimationUpdateListener(listener: (Float) -> Unit) {
         this.animationUpdateListener = listener
     }
@@ -78,11 +84,13 @@ class CollapsibleLinearLayout : LinearLayout {
                         viewTreeObserver.removeOnGlobalLayoutListener(this)
                         layoutParams.height = 0
                         requestLayout()
+                        onReadyListener?.invoke()
                     }
                 })
     }
 
     fun toggle() {
+        if (locked) return
         val fromHeight = if (!isCollapsed) maxHeight else 0
         val toHeight = if (!isCollapsed) 0 else maxHeight
         isCollapsed = !isCollapsed
@@ -91,8 +99,14 @@ class CollapsibleLinearLayout : LinearLayout {
     }
 
     fun collapse() {
+        if (locked) return
         if (!isCollapsed) toggle()
     }
 
+    fun lock() {
+        if (isCollapsed) toggle()
+        locked = true
+    }
+
 
 }
\ No newline at end of file



More information about the Android mailing list