From fb70762dbe77ba464b4ae358b601835fa1e1e9c7 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Wed, 18 May 2022 14:07:27 +0200 Subject: [PATCH 01/34] Accessibility: improve talkback for the video list --- .../resources/src/main/res/values/strings.xml | 19 +++++ .../vlc-android/res/layout/toolbar.xml | 1 + .../res/layout/video_grid_card.xml | 3 + .../res/layout/video_list_card.xml | 3 + .../vlc-android/res/menu/activity_option.xml | 7 ++ .../videolan/vlc/gui/helpers/TalkbackUtil.kt | 73 +++++++++++++++++++ .../vlc/gui/video/VideoGridFragment.kt | 9 ++- .../videolan/vlc/util/AccessibilityHelper.kt | 57 +++++++++++++++ 8 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt create mode 100644 application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 7213497f7..1b63d83d7 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -975,4 +975,23 @@ Playing + + + %s - title: %s - duration: %s + Genre + History item + Playlist + Folder: %s + Track number %s + Duration: %s + Album: %s + Artist: %s + Released in %s + Video: %s + Video group: %s + hours + minutes + seconds + + diff --git a/application/vlc-android/res/layout/toolbar.xml b/application/vlc-android/res/layout/toolbar.xml index 924ab798a..cf76467d0 100644 --- a/application/vlc-android/res/layout/toolbar.xml +++ b/application/vlc-android/res/layout/toolbar.xml @@ -35,6 +35,7 @@ android:layout_height="wrap_content" android:paddingStart="16dp" android:paddingEnd="16dp" + android:importantForAccessibility="no" android:textAppearance="@style/ToolbarTitleText" android:text="@string/app_name" /> diff --git a/application/vlc-android/res/layout/video_grid_card.xml b/application/vlc-android/res/layout/video_grid_card.xml index 92193bae3..42ae5a151 100644 --- a/application/vlc-android/res/layout/video_grid_card.xml +++ b/application/vlc-android/res/layout/video_grid_card.xml @@ -87,6 +87,7 @@ android:paddingEnd="8dp" android:paddingTop="8dp" android:clipToPadding="false" + vlc:mediaContentDescription="@{media}" android:onClick="@{holder::onClick}" android:onLongClick="@{holder::onLongClick}"> @@ -97,6 +98,7 @@ android:layout_height="0dp" android:clickable="true" android:focusable="true" + android:importantForAccessibility="no" android:foreground="?android:attr/selectableItemBackground" android:longClickable="true" android:onClick="@{holder::onClick}" @@ -247,6 +249,7 @@ android:contentDescription="@string/more_actions" android:onClick="@{holder::onMoreClick}" android:scaleType="fitEnd" + android:importantForAccessibility="yes" android:visibility="@{isPresent && !inSelection ? View.VISIBLE : View.GONE}" vlc:layout_constraintEnd_toEndOf="parent" vlc:layout_constraintTop_toTopOf="parent" diff --git a/application/vlc-android/res/layout/video_list_card.xml b/application/vlc-android/res/layout/video_list_card.xml index 25698cdd3..97e704630 100644 --- a/application/vlc-android/res/layout/video_list_card.xml +++ b/application/vlc-android/res/layout/video_list_card.xml @@ -72,6 +72,7 @@ android:background="?attr/video_list_background" android:clickable="true" android:focusable="true" + vlc:mediaContentDescription="@{media}" android:longClickable="true" selected="@{selected}" android:onClick="@{holder::onClick}" @@ -89,6 +90,7 @@ android:layout_height="wrap_content" android:clickable="true" android:focusable="true" + android:importantForAccessibility="no" android:foreground="?android:attr/selectableItemBackground" android:longClickable="true" android:onClick="@{holder::onClick}" @@ -249,6 +251,7 @@ android:contentDescription="@string/more_actions" android:onClick="@{holder::onMoreClick}" android:scaleType="center" + android:importantForAccessibility="yes" android:visibility="@{isPresent && !inSelection ? View.VISIBLE : View.INVISIBLE}" vlc:layout_constraintBottom_toBottomOf="parent" vlc:layout_constraintEnd_toEndOf="parent" diff --git a/application/vlc-android/res/menu/activity_option.xml b/application/vlc-android/res/menu/activity_option.xml index 50344c060..5b7a21a19 100644 --- a/application/vlc-android/res/menu/activity_option.xml +++ b/application/vlc-android/res/menu/activity_option.xml @@ -2,6 +2,13 @@ + 0) sb.append(hours).append(" ").append(context.getString(R.string.talkback_hours)).append(" ") + if (min > 0) sb.append(min).append(" ").append(context.getString(R.string.talkback_minutes)).append(" ") + if (sec > 0) sb.append(sec).append(" ").append(context.getString(R.string.talkback_seconds)) + return sb.toString() + } + +} + +fun String.talkbackAppend(other: String?, longPause: Boolean = false) = if (other.isNullOrBlank()) this else "$this${if (longPause) "." else ","} $other" \ No newline at end of file diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt index c961d6cf2..6b09b6cd7 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt @@ -70,10 +70,7 @@ import org.videolan.vlc.media.PlaylistManager import org.videolan.vlc.media.getAll import org.videolan.vlc.providers.medialibrary.VideosProvider import org.videolan.vlc.reloadLibrary -import org.videolan.vlc.util.Permissions -import org.videolan.vlc.util.launchWhenStarted -import org.videolan.vlc.util.onAnyChange -import org.videolan.vlc.util.share +import org.videolan.vlc.util.* import org.videolan.vlc.viewmodels.mobile.VideoGroupingType import org.videolan.vlc.viewmodels.mobile.VideosViewModel import org.videolan.vlc.viewmodels.mobile.getViewModel @@ -156,6 +153,7 @@ class VideoGridFragment : MediaBrowserFragment(), SwipeRefreshL menu.findItem(R.id.ml_menu_display_list).isVisible = displayInCards menu.findItem(R.id.rename_group).isVisible = viewModel.group != null menu.findItem(R.id.ungroup).isVisible = viewModel.group != null + if (requireActivity().isTalkbackIsEnabled()) menu.findItem(R.id.play_all).isVisible = true } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -187,6 +185,9 @@ class VideoGridFragment : MediaBrowserFragment(), SwipeRefreshL R.id.ungroup -> { viewModel.group?.let { viewModel.ungroup(it) } } + R.id.play_all -> { + onFabPlayClick(binding.root) + } else -> return super.onOptionsItemSelected(item) } return true diff --git a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt new file mode 100644 index 000000000..73e648d6b --- /dev/null +++ b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt @@ -0,0 +1,57 @@ +/* + * ************************************************************************ + * AccessibilityHelper.kt + * ************************************************************************* + * Copyright © 2022 VLC authors and VideoLAN + * Author: Nicolas POMEPUY + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * ************************************************************************** + * + * + */ + +package org.videolan.vlc.util + +import android.app.Activity +import android.content.Context.ACCESSIBILITY_SERVICE +import android.view.View +import android.view.accessibility.AccessibilityManager +import androidx.databinding.BindingAdapter +import org.videolan.medialibrary.interfaces.media.* +import org.videolan.medialibrary.media.HistoryItem +import org.videolan.medialibrary.media.MediaLibraryItem +import org.videolan.resources.R +import org.videolan.vlc.gui.helpers.TalkbackUtil + +fun Activity.isTalkbackIsEnabled() = (getSystemService(ACCESSIBILITY_SERVICE) as AccessibilityManager?)?.isTouchExplorationEnabled + ?: false + +@BindingAdapter("mediaContentDescription") +fun mediaDescription(v: View, media: MediaLibraryItem) { + v.contentDescription = when (media) { + is VideoGroup -> TalkbackUtil.getVideoGroup(v.context, media) + is Album -> v.context.getString(R.string.talkback_album) + is Artist -> v.context.getString(R.string.talkback_artist) + is Folder -> TalkbackUtil.getFolder(v.context, media) + is Genre -> v.context.getString(R.string.talkback_genre) + is HistoryItem -> v.context.getString(R.string.talkback_history_item) + is Playlist -> v.context.getString(R.string.talkback_playlist) + is MediaWrapper -> when (media.type) { + MediaWrapper.TYPE_VIDEO -> TalkbackUtil.getVideo(v.context, media) + else -> v.context.getString(R.string.audio) + } + else -> throw IllegalStateException("Unknown item type") + } +} -- GitLab From 1f1b75b7660e1b49264bef9c51dac3a7d43d811f Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 20 May 2022 08:28:02 +0200 Subject: [PATCH 02/34] Accessibility: improve talkback for the audio list --- .../resources/src/main/res/values/strings.xml | 3 ++- .../res/layout/audio_browser_card_item.xml | 2 ++ .../res/layout/audio_browser_item.xml | 2 ++ .../videolan/vlc/gui/helpers/TalkbackUtil.kt | 25 ++++++++++++------- .../videolan/vlc/util/AccessibilityHelper.kt | 11 ++++---- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 1b63d83d7..6ecabe7c7 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -978,7 +978,7 @@ %s - title: %s - duration: %s - Genre + Genre: %s History item Playlist Folder: %s @@ -988,6 +988,7 @@ Artist: %s Released in %s Video: %s + Audio track: %s Video group: %s hours minutes diff --git a/application/vlc-android/res/layout/audio_browser_card_item.xml b/application/vlc-android/res/layout/audio_browser_card_item.xml index a46fc092d..58d70d38a 100644 --- a/application/vlc-android/res/layout/audio_browser_card_item.xml +++ b/application/vlc-android/res/layout/audio_browser_card_item.xml @@ -70,6 +70,7 @@ android:background="?attr/video_list_background" android:clickable="true" android:focusable="true" + vlc:mediaContentDescription="@{item}" android:foreground="?android:attr/selectableItemBackground" android:longClickable="true" android:minHeight="@dimen/audio_browser_item_size" @@ -130,6 +131,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/transparent" + android:contentDescription="@string/play_all" android:onClick="@{holder::onMainActionClick}" vlc:layout_constraintBottom_toBottomOf="parent" vlc:layout_constraintEnd_toEndOf="parent" diff --git a/application/vlc-android/res/layout/audio_browser_item.xml b/application/vlc-android/res/layout/audio_browser_item.xml index 57c60f66b..8011bd7dc 100644 --- a/application/vlc-android/res/layout/audio_browser_item.xml +++ b/application/vlc-android/res/layout/audio_browser_item.xml @@ -58,6 +58,7 @@ android:clickable="true" android:focusable="true" android:background="?attr/video_list_background" + vlc:mediaContentDescription="@{item}" selected="@{selected}" android:longClickable="true" android:foreground="?attr/selectableItemBackground" @@ -88,6 +89,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" + android:importantForAccessibility="no" android:onClick="@{holder::onImageClick}" android:scaleType="centerCrop" android:src="@{cover}" diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt index 62eb57c07..db61a759d 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt @@ -25,23 +25,30 @@ package org.videolan.vlc.gui.helpers import android.content.Context -import org.videolan.medialibrary.interfaces.media.Folder -import org.videolan.medialibrary.interfaces.media.MediaWrapper -import org.videolan.medialibrary.interfaces.media.VideoGroup +import org.videolan.medialibrary.interfaces.media.* import org.videolan.vlc.R object TalkbackUtil { - fun getArtist(context: Context, artist: String) = context.getString(R.string.talkback_artist, artist) fun getDuration(context: Context, duration: String) = context.getString(R.string.talkback_duration, duration) fun getAlbumTitle(context: Context, album: String) = context.getString(R.string.talkback_album, album) fun getReleaseDate(context: Context, date: String) = context.getString(R.string.talkback_release_date, date) - fun getVideo(context: Context, video: MediaWrapper) = - context.getString(R.string.talkback_video).talkbackAppend(getDuration(context, millisToString(context, video.length))) + fun getVideo(context: Context, video: MediaWrapper) = context.getString(R.string.talkback_video) + .talkbackAppend(getDuration(context, millisToString(context, video.length))) + fun getAudioTrack(context: Context, audio: MediaWrapper) = context.getString(R.string.talkback_audio_track, audio.title) + .talkbackAppend(getDuration(context, millisToString(context, audio.length))) + .talkbackAppend(context.getString(R.string.talkback_album, audio.album)) + .talkbackAppend(context.getString(R.string.talkback_artist, audio.artist)) + fun getVideoGroup(context: Context, video: VideoGroup) = context.getString(R.string.talkback_video_group, video.title) + .talkbackAppend(context.resources.getQuantityString(R.plurals.videos_quantity, video.mediaCount(), video.mediaCount())) + fun getGenre(context: Context, genre: Genre) = context.getString(R.string.talkback_genre, genre.title) + .talkbackAppend(context.resources.getQuantityString(R.plurals.track_quantity, genre.tracksCount, genre.tracksCount)) + fun getArtist(context: Context, artist: Artist) = context.getString(R.string.talkback_artist, artist.title) + .talkbackAppend(context.resources.getQuantityString(R.plurals.albums_quantity, artist.albumsCount, artist.albumsCount)) + fun getAlbum(context: Context, album: Album) = context.getString(R.string.talkback_album, album.title) + .talkbackAppend(context.getString(R.string.talkback_artist, album.albumArtist)) + .talkbackAppend(context.resources.getQuantityString(R.plurals.track_quantity, album.tracksCount, album.tracksCount)) - fun getVideoGroup(context: Context, video: VideoGroup) = - context.getString(R.string.talkback_video_group, video.title) - .talkbackAppend(context.resources.getQuantityString(R.plurals.videos_quantity, video.mediaCount(), video.mediaCount())) fun getFolder(context: Context, folder: Folder): String { val mediaCount = folder.mediaCount(Folder.TYPE_FOLDER_VIDEO) diff --git a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt index 73e648d6b..c721dbe7c 100644 --- a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt +++ b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt @@ -42,16 +42,17 @@ fun Activity.isTalkbackIsEnabled() = (getSystemService(ACCESSIBILITY_SERVICE) as fun mediaDescription(v: View, media: MediaLibraryItem) { v.contentDescription = when (media) { is VideoGroup -> TalkbackUtil.getVideoGroup(v.context, media) - is Album -> v.context.getString(R.string.talkback_album) - is Artist -> v.context.getString(R.string.talkback_artist) + is Album -> TalkbackUtil.getAlbum(v.context, media) + is Artist -> TalkbackUtil.getArtist(v.context, media) is Folder -> TalkbackUtil.getFolder(v.context, media) - is Genre -> v.context.getString(R.string.talkback_genre) + is Genre -> TalkbackUtil.getGenre(v.context, media) is HistoryItem -> v.context.getString(R.string.talkback_history_item) is Playlist -> v.context.getString(R.string.talkback_playlist) is MediaWrapper -> when (media.type) { MediaWrapper.TYPE_VIDEO -> TalkbackUtil.getVideo(v.context, media) - else -> v.context.getString(R.string.audio) + MediaWrapper.TYPE_AUDIO -> TalkbackUtil.getAudioTrack(v.context, media) + else -> throw NotImplementedError("") } - else -> throw IllegalStateException("Unknown item type") + else -> throw NotImplementedError("Unknown item type") } } -- GitLab From 974ae67d7820c0903492f939df9928adb8d0da5e Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 20 May 2022 09:00:56 +0200 Subject: [PATCH 03/34] Accessibility: improve talkback for the album screen --- .../res/layout/audio_album_track_item.xml | 6 +++--- .../vlc-android/res/layout/player_hud.xml | 1 + .../res/layout/playlist_activity.xml | 19 +++++++++++++++++-- .../vlc/gui/HeaderMediaListActivity.kt | 5 ++--- .../vlc/gui/helpers/PlayerOptionsDelegate.kt | 15 +++++++++++++-- .../videolan/vlc/gui/helpers/TalkbackUtil.kt | 6 ++++++ 6 files changed, 42 insertions(+), 10 deletions(-) diff --git a/application/vlc-android/res/layout/audio_album_track_item.xml b/application/vlc-android/res/layout/audio_album_track_item.xml index 552c5536b..c4a56a209 100644 --- a/application/vlc-android/res/layout/audio_album_track_item.xml +++ b/application/vlc-android/res/layout/audio_album_track_item.xml @@ -6,12 +6,10 @@ - - - + - + + + + @@ -131,6 +142,8 @@ android:layout_marginStart="@dimen/default_margin" android:layout_marginTop="@dimen/half_default_margin" android:layout_marginEnd="@dimen/default_margin" + android:text="@{Tools.millisToTextLarge(totalDuration)}" + android:contentDescription="@{TalkbackUtil.INSTANCE.getDuration(context, totalDuration)}" tools:text="30:00" vlc:layout_constraintStart_toEndOf="@+id/playlist_cover" vlc:layout_constraintTop_toBottomOf="@+id/release_date" /> @@ -155,6 +168,7 @@ android:layout_marginStart="@dimen/default_margin" android:clickable="true" android:foreground="?attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/add_to_playlist" android:padding="4dp" vlc:srcCompat="@drawable/ic_album_addtoplaylist" vlc:layout_constraintBottom_toBottomOf="@id/play_btn" @@ -168,6 +182,7 @@ android:layout_marginStart="8dp" android:clickable="true" android:foreground="?attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/shuffle_all_title" android:padding="4dp" vlc:srcCompat="@drawable/ic_album_shuffle" vlc:layout_constraintBottom_toBottomOf="@id/play_btn" diff --git a/application/vlc-android/src/org/videolan/vlc/gui/HeaderMediaListActivity.kt b/application/vlc-android/src/org/videolan/vlc/gui/HeaderMediaListActivity.kt index 33449942f..74f13ac8d 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/HeaderMediaListActivity.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/HeaderMediaListActivity.kt @@ -44,7 +44,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.* -import org.videolan.medialibrary.Tools import org.videolan.medialibrary.interfaces.Medialibrary import org.videolan.medialibrary.interfaces.media.Album import org.videolan.medialibrary.interfaces.media.MediaWrapper @@ -128,7 +127,7 @@ open class HeaderMediaListActivity : AudioPlayerContainerActivity(), IEventsHand var totalDuration = 0L for (item in viewModel.playlist.tracks) totalDuration += item.length - binding.duration.text = Tools.millisToTextLarge(totalDuration) + binding.totalDuration = totalDuration if (isPlaylist) { audioBrowserAdapter = AudioBrowserAdapter(MediaLibraryItem.TYPE_MEDIA, this, this, isPlaylist) itemTouchHelperCallback = SwipeDragItemTouchHelperCallback(audioBrowserAdapter) @@ -140,7 +139,7 @@ open class HeaderMediaListActivity : AudioPlayerContainerActivity(), IEventsHand binding.songs.addItemDecoration(RecyclerSectionItemDecoration(resources.getDimensionPixelSize(R.dimen.recycler_section_header_height), true, viewModel.tracksProvider)) if (viewModel.playlist is Album) { val releaseYear = (viewModel.playlist as Album).releaseYear - binding.releaseDate.text = if (releaseYear > 0) releaseYear.toString() else "" + binding.releaseYear = if (releaseYear > 0) releaseYear.toString() else "" if (releaseYear <= 0) binding.releaseDate.visibility = View.GONE } binding.btnShuffle.setOnClickListener { diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt index e150c2ecb..8cf013267 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt @@ -6,6 +6,7 @@ import android.support.v4.media.session.PlaybackStateCompat import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent import android.widget.FrameLayout import android.widget.Toast import androidx.appcompat.widget.ViewStubCompat @@ -16,7 +17,10 @@ import androidx.leanback.widget.BrowseFrameLayout.OnFocusSearchListener import androidx.lifecycle.LifecycleObserver import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.videolan.resources.AndroidDevices import org.videolan.resources.VLCOptions import org.videolan.tools.AppScope @@ -31,6 +35,7 @@ import org.videolan.vlc.gui.dialogs.* import org.videolan.vlc.gui.helpers.UiTools.addToPlaylist import org.videolan.vlc.gui.video.VideoPlayerActivity import org.videolan.vlc.media.PlayerController +import org.videolan.vlc.util.isTalkbackIsEnabled private const val ACTION_AUDIO_DELAY = 2 private const val ACTION_SPU_DELAY = 3 @@ -136,9 +141,15 @@ class PlayerOptionsDelegate(val activity: FragmentActivity, val service: Playbac setup() rootView.visibility = View.VISIBLE if (Settings.showTvUi) AppScope.launch { - delay(100L) + withContext(Dispatchers.IO){ delay(100L) } val position = (recyclerview.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() (recyclerview.layoutManager as LinearLayoutManager).findViewByPosition(position)?.requestFocus() + } else if (activity.isTalkbackIsEnabled()) { + AppScope.launch { + withContext(Dispatchers.IO){ delay(100L) } + val linearLayoutManager = recyclerview.layoutManager as LinearLayoutManager + linearLayoutManager.findViewByPosition(linearLayoutManager.findFirstVisibleItemPosition())?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) + } } } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt index db61a759d..b51ddd902 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt @@ -30,6 +30,7 @@ import org.videolan.vlc.R object TalkbackUtil { + fun getDuration(context: Context, duration: Long) = context.getString(R.string.talkback_duration, millisToString(context, duration)) fun getDuration(context: Context, duration: String) = context.getString(R.string.talkback_duration, duration) fun getAlbumTitle(context: Context, album: String) = context.getString(R.string.talkback_album, album) fun getReleaseDate(context: Context, date: String) = context.getString(R.string.talkback_release_date, date) @@ -48,6 +49,11 @@ object TalkbackUtil { fun getAlbum(context: Context, album: Album) = context.getString(R.string.talkback_album, album.title) .talkbackAppend(context.getString(R.string.talkback_artist, album.albumArtist)) .talkbackAppend(context.resources.getQuantityString(R.plurals.track_quantity, album.tracksCount, album.tracksCount)) + fun getArtist(context: Context, artist: String) = context.getString(R.string.talkback_artist, artist) + fun getTrackNumber(context: Context, item: MediaWrapper) = context.getString(R.string.talkback_track_number, item.trackNumber.toString()) + fun getTimeAndArtist(context: Context, item: MediaWrapper) = millisToString(context, item.length) + .talkbackAppend(getArtist(context, item.artist)) + fun getFolder(context: Context, folder: Folder): String { -- GitLab From 909801c4aee5b5130d17687a8cb38e8ec8b78110 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 20 May 2022 13:14:12 +0200 Subject: [PATCH 04/34] Accessibility: improve talkback for folders --- .../resources/src/main/res/values/strings.xml | 4 ++++ .../vlc-android/res/layout/browser_item.xml | 12 +++++++--- .../res/layout/card_browser_item.xml | 12 +++++++--- .../res/layout/title_list_view.xml | 1 + .../vlc/gui/browser/BaseBrowserAdapter.kt | 9 ++++--- .../browser/BrowserItemBindingContainer.kt | 12 ++++++++++ .../videolan/vlc/gui/helpers/TalkbackUtil.kt | 22 ++++++++++++++++- .../videolan/vlc/gui/view/TitleListView.kt | 5 ++-- .../src/org/videolan/vlc/util/Kextensions.kt | 24 +++++++++++++++++++ 9 files changed, 87 insertions(+), 14 deletions(-) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 6ecabe7c7..9e956fe19 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -982,6 +982,8 @@ History item Playlist Folder: %s + File: %s + Favorite: %s Track number %s Duration: %s Album: %s @@ -993,6 +995,8 @@ hours minutes seconds + Header: %s + File size: %s diff --git a/application/vlc-android/res/layout/browser_item.xml b/application/vlc-android/res/layout/browser_item.xml index 53bd3dfd9..094ae956a 100644 --- a/application/vlc-android/res/layout/browser_item.xml +++ b/application/vlc-android/res/layout/browser_item.xml @@ -7,10 +7,9 @@ - - + + + @@ -58,6 +61,8 @@ android:layout_height="wrap_content" android:background="@{bgColor}" android:clickable="@{holder != null}" + android:id="@+id/browser_container" + android:contentDescription="@{TalkbackUtil.INSTANCE.getDir(context, item, favorite)}" android:focusable="true" android:foreground="?android:attr/selectableItemBackground" android:longClickable="@{hasContextMenu || item.getItemType() == MediaLibraryItem.TYPE_STORAGE}" @@ -85,7 +90,7 @@ android:layout_marginStart="12dp" android:layout_marginTop="4dp" android:layout_marginBottom="4dp" - android:contentDescription="@string/cover_art" + android:importantForAccessibility="no" android:onClick="@{holder::onImageClick}" android:scaleType="centerCrop" android:src="@{cover}" @@ -163,6 +168,7 @@ android:visibility="@{hasContextMenu ? View.VISIBLE : View.GONE, default=gone}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" + android:contentDescription="@string/more_actions" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_more" tools:visibility="visible" /> diff --git a/application/vlc-android/res/layout/card_browser_item.xml b/application/vlc-android/res/layout/card_browser_item.xml index e0a548d3f..cb3649c50 100644 --- a/application/vlc-android/res/layout/card_browser_item.xml +++ b/application/vlc-android/res/layout/card_browser_item.xml @@ -30,10 +30,9 @@ - - + + + @@ -71,7 +74,10 @@ @@ -102,7 +108,6 @@ android:layout_width="64dp" android:layout_height="64dp" android:layout_marginTop="8dp" - android:contentDescription="@string/cover_art" android:scaleType="centerCrop" android:src="@{cover}" android:visibility="@{item.getItemType() == MediaLibraryItem.TYPE_STORAGE ? View.GONE : View.VISIBLE}" @@ -184,6 +189,7 @@ android:visibility="@{hasContextMenu ? View.VISIBLE : View.GONE, default=gone}" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" + android:contentDescription="@string/more_actions" app:srcCompat="@drawable/ic_more" tools:visibility="visible" /> diff --git a/application/vlc-android/res/layout/title_list_view.xml b/application/vlc-android/res/layout/title_list_view.xml index e6ca67db6..90025f37d 100644 --- a/application/vlc-android/res/layout/title_list_view.xml +++ b/application/vlc-android/res/layout/title_list_view.xml @@ -32,6 +32,7 @@ android:id="@+id/title_content" android:layout_width="0dp" android:layout_height="0dp" + android:importantForAccessibility="no" android:background="?attr/selectableItemBackground" app:layout_constraintBottom_toTopOf="@+id/list" app:layout_constraintEnd_toEndOf="parent" diff --git a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt index 648de515d..2d11d55ef 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserAdapter.kt @@ -49,12 +49,8 @@ import org.videolan.vlc.databinding.BrowserItemBinding import org.videolan.vlc.databinding.BrowserItemSeparatorBinding import org.videolan.vlc.databinding.CardBrowserItemBinding import org.videolan.vlc.gui.DiffUtilAdapter -import org.videolan.vlc.gui.helpers.MarqueeViewHolder -import org.videolan.vlc.gui.helpers.SelectorViewHolder -import org.videolan.vlc.gui.helpers.enableMarqueeEffect -import org.videolan.vlc.gui.helpers.getBitmapFromDrawable +import org.videolan.vlc.gui.helpers.* import org.videolan.vlc.util.getDescriptionSpan -import java.util.* open class BaseBrowserAdapter(val browserContainer: BrowserContainer) : DiffUtilAdapter>(), MultiSelectAdapter { @@ -131,6 +127,8 @@ open class BaseBrowserAdapter(val browserContainer: BrowserContainer binding.favorite = favorite + is BrowserItemBinding -> binding.favorite = favorite + else -> throw IllegalStateException("Binding should be either a CardBrowserItemBinding or BrowserItemBinding") + } + } + fun setHolder(holder: BaseBrowserAdapter.ViewHolder) { when (binding) { is CardBrowserItemBinding -> binding.holder = holder @@ -109,6 +118,7 @@ class BrowserItemBindingContainer(val binding: ViewDataBinding) { var itemIcon: ImageView var browserCheckbox: ThreeStatesCheckbox var text: TextView + var container: View var moreIcon: ImageView init { @@ -120,6 +130,7 @@ class BrowserItemBindingContainer(val binding: ViewDataBinding) { itemIcon = binding.itemIcon browserCheckbox = binding.browserCheckbox moreIcon = binding.itemMore + container = binding.browserContainer } is BrowserItemBinding -> { text = binding.text @@ -127,6 +138,7 @@ class BrowserItemBindingContainer(val binding: ViewDataBinding) { itemIcon = binding.itemIcon browserCheckbox = binding.browserCheckbox moreIcon = binding.itemMore + container = binding.browserContainer } else -> throw IllegalStateException("Binding should be either a CardBrowserItemBinding or BrowserItemBinding") } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt index b51ddd902..1d77a28f6 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt @@ -26,7 +26,10 @@ package org.videolan.vlc.gui.helpers import android.content.Context import org.videolan.medialibrary.interfaces.media.* +import org.videolan.medialibrary.media.MediaLibraryItem import org.videolan.vlc.R +import org.videolan.vlc.util.getFilesNumber +import org.videolan.vlc.util.getFolderNumber object TalkbackUtil { @@ -44,7 +47,7 @@ object TalkbackUtil { .talkbackAppend(context.resources.getQuantityString(R.plurals.videos_quantity, video.mediaCount(), video.mediaCount())) fun getGenre(context: Context, genre: Genre) = context.getString(R.string.talkback_genre, genre.title) .talkbackAppend(context.resources.getQuantityString(R.plurals.track_quantity, genre.tracksCount, genre.tracksCount)) - fun getArtist(context: Context, artist: Artist) = context.getString(R.string.talkback_artist, artist.title) + fun getArtist(context: Context, artist: Artist?) = if (artist == null) null else context.getString(R.string.talkback_artist, artist.title) .talkbackAppend(context.resources.getQuantityString(R.plurals.albums_quantity, artist.albumsCount, artist.albumsCount)) fun getAlbum(context: Context, album: Album) = context.getString(R.string.talkback_album, album.title) .talkbackAppend(context.getString(R.string.talkback_artist, album.albumArtist)) @@ -62,6 +65,23 @@ object TalkbackUtil { .talkbackAppend(context.resources.getQuantityString(R.plurals.videos_quantity, mediaCount, mediaCount)) } + fun getDir(context: Context, folder: MediaLibraryItem, favorite: Boolean): String { + if (folder !is MediaWrapper) throw IllegalStateException("Folder should be an instance of MediaWrapper") + var text = "" + if (folder.type == MediaWrapper.TYPE_DIR) { + val folders = folder.description?.getFolderNumber() ?: 0 + val files = folder.description?.getFilesNumber() ?: 0 + text = context.getString(if (favorite) R.string.talkback_favorite else R.string.talkback_folder, folder.title) + if (folders > 0) text = text.talkbackAppend(context.resources.getQuantityString(R.plurals.subfolders_quantity, folders, folders)) + if (files > 0) text = text.talkbackAppend(context.resources.getQuantityString(R.plurals.mediafiles_quantity, files, files)) + if (files < 1 && folders < 1) text = text.talkbackAppend(context.getString(R.string.empty_directory)) + } else { + text = context.getString(R.string.talkback_file, folder.title) + if (!folder.description.isNullOrEmpty()) text = text.talkbackAppend(context.getString(R.string.talkback_file_size, folder.description)) + } + return text + } + fun millisToString(context: Context, duration: Long): String { var millis = duration val sb = StringBuilder() diff --git a/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt b/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt index 926596369..54db0ae60 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt @@ -33,7 +33,6 @@ import android.view.View import android.widget.ImageButton import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import org.videolan.tools.dp @@ -90,7 +89,9 @@ class TitleListView : ConstraintLayout { attrs.let { val a = context.theme.obtainStyledAttributes(attrs, R.styleable.TitleListView, 0, defStyle) try { - titleView.text = a.getString(R.styleable.TitleListView_title) + val titleString = a.getString(R.styleable.TitleListView_title) + titleView.text = titleString + titleView.contentDescription = context.getString(R.string.talkback_list_section, titleString) if (!a.getBoolean(R.styleable.TitleListView_show_button, false)) actionButton.setGone() actionButton.setOnClickListener { actionClickListener?.let { listener -> listener(actionButton) } diff --git a/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt b/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt index 28aa383c2..099d8cf37 100644 --- a/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt +++ b/application/vlc-android/src/org/videolan/vlc/util/Kextensions.kt @@ -221,6 +221,30 @@ fun CharSequence.getDescriptionSpan(context: Context):SpannableString { return string } +/** + * Get the folder number from the formatted string + * + * @return the folder number + */ +fun CharSequence?.getFolderNumber():Int { + if (isNullOrBlank()) return 0 + if (!contains(folderReplacementMarker)) return 0 + val cutString = replace(Regex("[^0-9 ]"), "") + return cutString.trim().split(" ")[0].toInt() +} + +/** + * Get the file number from the formatted string + * + * @return the file number + */ +fun CharSequence?.getFilesNumber():Int { + if (isNullOrBlank()) return 0 + if (!contains(fileReplacementMarker)) return 0 + val cutString = replace(Regex("[^0-9 ]"), "").trim().split(" ") + return cutString[cutString.size -1].toInt() +} + const val presentReplacementMarker = "§*§" const val missingReplacementMarker = "*§*" -- GitLab From cb46c3e35f7ff2c169bdeb9bcee59bf9186e328c Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 20 May 2022 13:56:25 +0200 Subject: [PATCH 05/34] Accessibility: improve talkback for playlists --- .../resources/src/main/res/values/strings.xml | 2 +- .../org/videolan/vlc/gui/helpers/TalkbackUtil.kt | 14 +++++++++++--- .../org/videolan/vlc/util/AccessibilityHelper.kt | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 9e956fe19..50335fb66 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -980,7 +980,7 @@ %s - title: %s - duration: %s Genre: %s History item - Playlist + Playlist: %s Folder: %s File: %s Favorite: %s diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt index 1d77a28f6..2078db940 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt @@ -36,29 +36,37 @@ object TalkbackUtil { fun getDuration(context: Context, duration: Long) = context.getString(R.string.talkback_duration, millisToString(context, duration)) fun getDuration(context: Context, duration: String) = context.getString(R.string.talkback_duration, duration) fun getAlbumTitle(context: Context, album: String) = context.getString(R.string.talkback_album, album) - fun getReleaseDate(context: Context, date: String) = context.getString(R.string.talkback_release_date, date) + fun getReleaseDate(context: Context, date: String?) = if (date == null) "" else context.getString(R.string.talkback_release_date, date) fun getVideo(context: Context, video: MediaWrapper) = context.getString(R.string.talkback_video) .talkbackAppend(getDuration(context, millisToString(context, video.length))) + fun getAudioTrack(context: Context, audio: MediaWrapper) = context.getString(R.string.talkback_audio_track, audio.title) .talkbackAppend(getDuration(context, millisToString(context, audio.length))) .talkbackAppend(context.getString(R.string.talkback_album, audio.album)) .talkbackAppend(context.getString(R.string.talkback_artist, audio.artist)) + fun getVideoGroup(context: Context, video: VideoGroup) = context.getString(R.string.talkback_video_group, video.title) .talkbackAppend(context.resources.getQuantityString(R.plurals.videos_quantity, video.mediaCount(), video.mediaCount())) + fun getGenre(context: Context, genre: Genre) = context.getString(R.string.talkback_genre, genre.title) .talkbackAppend(context.resources.getQuantityString(R.plurals.track_quantity, genre.tracksCount, genre.tracksCount)) + fun getArtist(context: Context, artist: Artist?) = if (artist == null) null else context.getString(R.string.talkback_artist, artist.title) .talkbackAppend(context.resources.getQuantityString(R.plurals.albums_quantity, artist.albumsCount, artist.albumsCount)) + fun getAlbum(context: Context, album: Album) = context.getString(R.string.talkback_album, album.title) .talkbackAppend(context.getString(R.string.talkback_artist, album.albumArtist)) .talkbackAppend(context.resources.getQuantityString(R.plurals.track_quantity, album.tracksCount, album.tracksCount)) - fun getArtist(context: Context, artist: String) = context.getString(R.string.talkback_artist, artist) + + fun getPlaylist(context: Context, playlist: Playlist) = context.getString(R.string.talkback_playlist, playlist.title) + .talkbackAppend(context.resources.getQuantityString(R.plurals.track_quantity, playlist.tracksCount, playlist.tracksCount)) + + fun getArtist(context: Context, artist: String?) = if (artist == null) "" else context.getString(R.string.talkback_artist, artist) fun getTrackNumber(context: Context, item: MediaWrapper) = context.getString(R.string.talkback_track_number, item.trackNumber.toString()) fun getTimeAndArtist(context: Context, item: MediaWrapper) = millisToString(context, item.length) .talkbackAppend(getArtist(context, item.artist)) - fun getFolder(context: Context, folder: Folder): String { val mediaCount = folder.mediaCount(Folder.TYPE_FOLDER_VIDEO) return context.getString(R.string.talkback_folder, folder.title) diff --git a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt index c721dbe7c..368c3eb4e 100644 --- a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt +++ b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt @@ -47,7 +47,7 @@ fun mediaDescription(v: View, media: MediaLibraryItem) { is Folder -> TalkbackUtil.getFolder(v.context, media) is Genre -> TalkbackUtil.getGenre(v.context, media) is HistoryItem -> v.context.getString(R.string.talkback_history_item) - is Playlist -> v.context.getString(R.string.talkback_playlist) + is Playlist -> TalkbackUtil.getPlaylist(v.context, media) is MediaWrapper -> when (media.type) { MediaWrapper.TYPE_VIDEO -> TalkbackUtil.getVideo(v.context, media) MediaWrapper.TYPE_AUDIO -> TalkbackUtil.getAudioTrack(v.context, media) -- GitLab From bef40e6859e2efd12e97078664d5f0b96c8ebe82 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 20 May 2022 14:41:25 +0200 Subject: [PATCH 06/34] Accessibility: improve talkback for the more screen --- .../resources/src/main/res/values/strings.xml | 3 +++ application/vlc-android/res/layout/about.xml | 1 + .../vlc-android/res/layout/dialog_license.xml | 1 + .../vlc-android/res/layout/history_item.xml | 2 ++ .../vlc-android/res/layout/history_item_card.xml | 2 ++ .../vlc-android/res/layout/mrl_card_item.xml | 16 ++++++++++++++-- application/vlc-android/res/layout/mrl_item.xml | 12 ++++++++++++ .../org/videolan/vlc/gui/helpers/TalkbackUtil.kt | 6 ++++-- .../org/videolan/vlc/gui/network/MRLAdapter.kt | 2 ++ .../org/videolan/vlc/gui/view/TitleListView.kt | 1 + .../org/videolan/vlc/util/AccessibilityHelper.kt | 1 + 11 files changed, 43 insertions(+), 4 deletions(-) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 50335fb66..2378bb644 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -990,13 +990,16 @@ Artist: %s Released in %s Video: %s + Stream: %s Audio track: %s Video group: %s hours minutes seconds Header: %s + Navigate to %s File size: %s + Open in web browser diff --git a/application/vlc-android/res/layout/about.xml b/application/vlc-android/res/layout/about.xml index 5f465f0af..90e687365 100644 --- a/application/vlc-android/res/layout/about.xml +++ b/application/vlc-android/res/layout/about.xml @@ -14,6 +14,7 @@ android:id="@+id/logo" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:importantForAccessibility="no" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent" diff --git a/application/vlc-android/res/layout/dialog_license.xml b/application/vlc-android/res/layout/dialog_license.xml index bd527f56d..a5f22fda5 100644 --- a/application/vlc-android/res/layout/dialog_license.xml +++ b/application/vlc-android/res/layout/dialog_license.xml @@ -52,6 +52,7 @@ android:clickable="true" android:focusable="true" android:padding="8dp" + android:contentDescription="@string/talkback_open_in_browser" android:visibility="@{ TextUtils.isEmpty(library.licenseLink) ? View.GONE : View.VISIBLE, default=gone}" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/application/vlc-android/res/layout/history_item.xml b/application/vlc-android/res/layout/history_item.xml index 4d9e9cbb9..818d27df9 100644 --- a/application/vlc-android/res/layout/history_item.xml +++ b/application/vlc-android/res/layout/history_item.xml @@ -45,6 +45,7 @@ android:layout_height="wrap_content" android:background="@{bgColor}" android:clickable="true" + vlc:mediaContentDescription="@{media}" android:focusable="true" android:minHeight="@dimen/audio_browser_item_size" android:onClick="@{holder::onClick}" @@ -76,6 +77,7 @@ android:id="@+id/icon" android:layout_width="0dp" android:layout_height="@dimen/audio_browser_item_size" + android:importantForAccessibility="no" android:layout_gravity="center" android:onClick="@{holder::onImageClick}" android:scaleType="centerCrop" diff --git a/application/vlc-android/res/layout/history_item_card.xml b/application/vlc-android/res/layout/history_item_card.xml index 750421b68..2489746c6 100644 --- a/application/vlc-android/res/layout/history_item_card.xml +++ b/application/vlc-android/res/layout/history_item_card.xml @@ -74,6 +74,7 @@ android:background="@{bgColor}" android:clickable="true" android:focusable="true" + vlc:mediaContentDescription="@{media}" android:onClick="@{holder::onClick}" android:onLongClick="@{holder::onLongClick}"> @@ -103,6 +104,7 @@ android:onClick="@{holder::onImageClick}" android:scaleType="centerCrop" android:src="@{cover}" + android:importantForAccessibility="no" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="1" app:layout_constraintEnd_toEndOf="parent" diff --git a/application/vlc-android/res/layout/mrl_card_item.xml b/application/vlc-android/res/layout/mrl_card_item.xml index caf8e4fb9..65c7eec55 100644 --- a/application/vlc-android/res/layout/mrl_card_item.xml +++ b/application/vlc-android/res/layout/mrl_card_item.xml @@ -22,16 +22,26 @@ ~ --> - + + + + + + + + diff --git a/application/vlc-android/res/layout/mrl_item.xml b/application/vlc-android/res/layout/mrl_item.xml index 2a91db78e..1f7643a05 100644 --- a/application/vlc-android/res/layout/mrl_item.xml +++ b/application/vlc-android/res/layout/mrl_item.xml @@ -2,10 +2,20 @@ + + + + + + + @@ -16,6 +26,7 @@ android:background="@drawable/rectangle_circle_right_white_selector" android:clickable="true" android:focusable="true" + android:importantForAccessibility="no" android:nextFocusRight="@+id/mrl_ctx" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" @@ -61,6 +72,7 @@ android:nextFocusLeft="@+id/selector" android:padding="8dp" app:layout_constraintBottom_toBottomOf="parent" + android:contentDescription="@string/more_actions" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/ic_more" /> diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt index 2078db940..43dadb8f3 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt @@ -37,9 +37,11 @@ object TalkbackUtil { fun getDuration(context: Context, duration: String) = context.getString(R.string.talkback_duration, duration) fun getAlbumTitle(context: Context, album: String) = context.getString(R.string.talkback_album, album) fun getReleaseDate(context: Context, date: String?) = if (date == null) "" else context.getString(R.string.talkback_release_date, date) - fun getVideo(context: Context, video: MediaWrapper) = context.getString(R.string.talkback_video) + fun getVideo(context: Context, video: MediaWrapper) = context.getString(R.string.talkback_video, video.title) .talkbackAppend(getDuration(context, millisToString(context, video.length))) + fun getStream(context: Context, stream: MediaWrapper) = context.getString(R.string.talkback_stream, stream.title) + fun getAudioTrack(context: Context, audio: MediaWrapper) = context.getString(R.string.talkback_audio_track, audio.title) .talkbackAppend(getDuration(context, millisToString(context, audio.length))) .talkbackAppend(context.getString(R.string.talkback_album, audio.album)) @@ -74,7 +76,7 @@ object TalkbackUtil { } fun getDir(context: Context, folder: MediaLibraryItem, favorite: Boolean): String { - if (folder !is MediaWrapper) throw IllegalStateException("Folder should be an instance of MediaWrapper") + if (folder !is MediaWrapper) return context.getString(R.string.talkback_folder, folder.title) var text = "" if (folder.type == MediaWrapper.TYPE_DIR) { val folders = folder.description?.getFolderNumber() ?: 0 diff --git a/application/vlc-android/src/org/videolan/vlc/gui/network/MRLAdapter.kt b/application/vlc-android/src/org/videolan/vlc/gui/network/MRLAdapter.kt index c3e28e8a1..e6dc1b3e9 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/network/MRLAdapter.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/network/MRLAdapter.kt @@ -67,10 +67,12 @@ internal class MRLAdapter(private val eventActor: SendChannel, privat is ListViewHolder -> { holder.binding.mrlItemUri.text = Uri.decode(item.location) holder.binding.mrlItemTitle.text = Uri.decode(item.title) + holder.binding.item = item } is CardViewHolder -> { holder.binding.mrlItemUri.text = Uri.decode(item.location) holder.binding.mrlItemTitle.text = Uri.decode(item.title) + holder.binding.item = item } } } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt b/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt index 54db0ae60..12bea3c37 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/view/TitleListView.kt @@ -92,6 +92,7 @@ class TitleListView : ConstraintLayout { val titleString = a.getString(R.styleable.TitleListView_title) titleView.text = titleString titleView.contentDescription = context.getString(R.string.talkback_list_section, titleString) + actionButton.contentDescription = context.getString(R.string.talkback_enter_screen, titleString) if (!a.getBoolean(R.styleable.TitleListView_show_button, false)) actionButton.setGone() actionButton.setOnClickListener { actionClickListener?.let { listener -> listener(actionButton) } diff --git a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt index 368c3eb4e..80e3db98e 100644 --- a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt +++ b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt @@ -51,6 +51,7 @@ fun mediaDescription(v: View, media: MediaLibraryItem) { is MediaWrapper -> when (media.type) { MediaWrapper.TYPE_VIDEO -> TalkbackUtil.getVideo(v.context, media) MediaWrapper.TYPE_AUDIO -> TalkbackUtil.getAudioTrack(v.context, media) + MediaWrapper.TYPE_STREAM -> TalkbackUtil.getStream(v.context, media) else -> throw NotImplementedError("") } else -> throw NotImplementedError("Unknown item type") -- GitLab From 26378ff1cf85a7237aab76f01dd5c9c6b36518f9 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Mon, 23 May 2022 09:41:27 +0200 Subject: [PATCH 07/34] Improve disable track wording --- application/resources/src/main/res/values/strings.xml | 1 + application/vlc-android/res/layout/video_track_item.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 2378bb644..4aa258076 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -973,6 +973,7 @@ Seek %s seconds forwards Open VLC Playing + Disable track diff --git a/application/vlc-android/res/layout/video_track_item.xml b/application/vlc-android/res/layout/video_track_item.xml index 098039ad7..e5331810f 100644 --- a/application/vlc-android/res/layout/video_track_item.xml +++ b/application/vlc-android/res/layout/video_track_item.xml @@ -45,7 +45,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:text="@{track.name}" + android:text="@{track.id == -1 ? @string/disable_track : track.name}" android:textColor="@{selected ? @color/white : @color/white_transparent_50}" android:maxLines="1" android:singleLine="true" -- GitLab From 6157ea403e052c6e50b2576bccf136c47de2810a Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Tue, 24 May 2022 08:17:16 +0200 Subject: [PATCH 08/34] Accessibility: improve talkback for the video player --- .../resources/src/main/res/values/strings.xml | 2 ++ application/vlc-android/res/layout/player.xml | 2 ++ application/vlc-android/res/layout/player_hud.xml | 5 ++++- .../videolan/vlc/gui/video/VideoPlayerActivity.kt | 1 + .../vlc/gui/video/VideoPlayerOverlayDelegate.kt | 13 +++++++++---- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 4aa258076..d4c083288 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -1001,6 +1001,8 @@ Navigate to %s File size: %s Open in web browser + Video player. Tap to show controls. Tap the back button to hide them + diff --git a/application/vlc-android/res/layout/player.xml b/application/vlc-android/res/layout/player.xml index 7fa95bbc8..00a9da399 100644 --- a/application/vlc-android/res/layout/player.xml +++ b/application/vlc-android/res/layout/player.xml @@ -5,6 +5,8 @@ android:id="@+id/player_root" android:layout_width="match_parent" android:layout_height="match_parent" + android:importantForAccessibility="yes" + android:contentDescription="@string/talkback_video_player" android:keepScreenOn="true"> - Date: Wed, 1 Jun 2022 10:58:16 +0200 Subject: [PATCH 20/34] Accessibility: improve talkback for the video player seek actions --- application/vlc-android/res/layout/player_hud.xml | 4 ++-- .../org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/application/vlc-android/res/layout/player_hud.xml b/application/vlc-android/res/layout/player_hud.xml index 8f47e31ca..bfa06db98 100644 --- a/application/vlc-android/res/layout/player_hud.xml +++ b/application/vlc-android/res/layout/player_hud.xml @@ -370,7 +370,6 @@ android:layout_marginEnd="@dimen/large_margins_center" android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true" - android:contentDescription="@string/playback_rewind" android:focusable="true" android:longClickable="true" android:scaleType="center" @@ -388,6 +387,7 @@ android:layout_height="wrap_content" android:textColor="?attr/player_icon_color" android:visibility="gone" + android:importantForAccessibility="no" tools:visibility="visible" android:textSize="8dp" android:textStyle="bold" @@ -420,7 +420,6 @@ android:layout_marginStart="@dimen/large_margins_center" android:background="?attr/selectableItemBackgroundBorderless" android:clickable="true" - android:contentDescription="@string/playback_forward" android:focusable="true" android:longClickable="true" android:scaleType="center" @@ -441,6 +440,7 @@ android:visibility="gone" tools:visibility="visible" android:textSize="8dp" + android:importantForAccessibility="no" android:textStyle="bold" vlc:layout_constraintBottom_toBottomOf="@+id/player_overlay_forward" vlc:layout_constraintEnd_toEndOf="@+id/player_overlay_forward" diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt index b94e25573..05871a3da 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt @@ -802,9 +802,11 @@ class VideoPlayerOverlayDelegate (private val player: VideoPlayerActivity) { if (seekButtons) { hudBinding.playerOverlayRewind.visibility = if (show) View.VISIBLE else View.INVISIBLE hudBinding.playerOverlayRewindText.text = "${Settings.videoJumpDelay}" + hudBinding.playerOverlayRewind.contentDescription = player.getString(R.string.talkback_action_rewind, Settings.videoJumpDelay.toString()) hudBinding.playerOverlayRewindText.visibility = if (show) View.VISIBLE else View.INVISIBLE hudBinding.playerOverlayForward.visibility = if (show) View.VISIBLE else View.INVISIBLE hudBinding.playerOverlayForwardText.text = "${Settings.videoJumpDelay}" + hudBinding.playerOverlayForward.contentDescription = player.getString(R.string.talkback_action_forward, Settings.videoJumpDelay.toString()) hudBinding.playerOverlayForwardText.visibility = if (show) View.VISIBLE else View.INVISIBLE } hudBinding.playerOverlayTracks.visibility = if (show) View.VISIBLE else View.INVISIBLE -- GitLab From 219bce90e9dc299b63ca72bec806d6ff71abe4a2 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Wed, 1 Jun 2022 11:57:27 +0200 Subject: [PATCH 21/34] Accessibility: improve talkback for Android TV navigation --- application/resources/src/main/res/values/strings.xml | 1 + .../television/ui/browser/BaseBrowserTvFragment.kt | 2 ++ .../television/ui/browser/FileBrowserTvFragment.kt | 8 +++++++- .../src/main/res/layout/media_browser_tv_item.xml | 1 + .../src/main/res/layout/media_browser_tv_item_list.xml | 1 + .../television/src/main/res/layout/song_browser.xml | 5 +++++ .../src/org/videolan/vlc/util/AccessibilityHelper.kt | 3 ++- 7 files changed, 19 insertions(+), 2 deletions(-) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index eaea750f0..d9bed6bd9 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -1025,6 +1025,7 @@ Downloading Rewind %s seconds Seek forward %s seconds + Display settings diff --git a/application/television/src/main/java/org/videolan/television/ui/browser/BaseBrowserTvFragment.kt b/application/television/src/main/java/org/videolan/television/ui/browser/BaseBrowserTvFragment.kt index 0ab3b4af9..ae59e6c62 100644 --- a/application/television/src/main/java/org/videolan/television/ui/browser/BaseBrowserTvFragment.kt +++ b/application/television/src/main/java/org/videolan/television/ui/browser/BaseBrowserTvFragment.kt @@ -265,6 +265,8 @@ abstract class BaseBrowserTvFragment : Fragment(), BrowserFragmentInterface, binding.imageButtonDisplay.setImageResource(if (inGrid) R.drawable.ic_fabtvmini_list else R.drawable.ic_fabtvmini_grid) binding.displayButton.setImageResource(if (inGrid) R.drawable.ic_list else R.drawable.ic_grid) binding.displayDescription.setText(if (inGrid) R.string.display_in_list else R.string.display_in_grid) + binding.displayButton.contentDescription = getString(if (inGrid) R.string.display_in_list else R.string.display_in_grid) + binding.imageButtonDisplay.contentDescription = getString(if (inGrid) R.string.display_in_list else R.string.display_in_grid) } override fun onStart() { diff --git a/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt b/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt index 17b946277..b5615f586 100644 --- a/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt +++ b/application/television/src/main/java/org/videolan/television/ui/browser/FileBrowserTvFragment.kt @@ -16,7 +16,9 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import com.google.android.material.snackbar.Snackbar -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.videolan.libvlc.Dialog import org.videolan.medialibrary.MLServiceLocator import org.videolan.medialibrary.interfaces.Medialibrary @@ -196,7 +198,9 @@ class FileBrowserTvFragment : BaseBrowserTvFragment(), PathAda animationDelegate.setVisibility(binding.favoriteDescription, View.VISIBLE) favExists = (currentItem as? MediaWrapper)?.let { browserFavRepository.browserFavExists(it.uri) } ?: false binding.favoriteButton.setImageResource(if (favExists) R.drawable.ic_favorite else R.drawable.ic_favorite_outline) + binding.favoriteButton.contentDescription = getString(if (favExists) R.string.favorites_remove else R.string.favorites_add) binding.imageButtonFavorite.setImageResource(if (favExists) R.drawable.ic_fabtvmini_bookmark else R.drawable.ic_fabtvmini_bookmark_outline) + binding.imageButtonFavorite.contentDescription = getString(if (favExists) R.string.favorites_remove else R.string.favorites_add) } binding.favoriteButton.setOnClickListener(favoriteClickListener) binding.imageButtonFavorite.setOnClickListener(favoriteClickListener) @@ -270,7 +274,9 @@ class FileBrowserTvFragment : BaseBrowserTvFragment(), PathAda } favExists = browserFavRepository.browserFavExists(mw.uri) binding.favoriteButton.setImageResource(if (favExists) R.drawable.ic_favorite else R.drawable.ic_favorite_outline) + binding.favoriteButton.contentDescription = getString(if (favExists) R.string.favorites_remove else R.string.favorites_add) binding.imageButtonFavorite.setImageResource(if (favExists) R.drawable.ic_fabtvmini_bookmark else R.drawable.ic_fabtvmini_bookmark_outline) + binding.imageButtonFavorite.contentDescription = getString(if (favExists) R.string.favorites_remove else R.string.favorites_add) } } } diff --git a/application/television/src/main/res/layout/media_browser_tv_item.xml b/application/television/src/main/res/layout/media_browser_tv_item.xml index 7ee254861..ed52e5129 100644 --- a/application/television/src/main/res/layout/media_browser_tv_item.xml +++ b/application/television/src/main/res/layout/media_browser_tv_item.xml @@ -88,6 +88,7 @@ android:focusable="true" android:longClickable="true" android:minHeight="@dimen/audio_browser_item_size" + vlc:mediaContentDescription="@{item}" android:onClick="@{holder::onClick}" android:onLongClick="@{holder::onLongClick}" android:paddingBottom="@dimen/kl_small" diff --git a/application/television/src/main/res/layout/media_browser_tv_item_list.xml b/application/television/src/main/res/layout/media_browser_tv_item_list.xml index c9afcb23b..b7a0146b4 100644 --- a/application/television/src/main/res/layout/media_browser_tv_item_list.xml +++ b/application/television/src/main/res/layout/media_browser_tv_item_list.xml @@ -107,6 +107,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/tv_list_background_transition" + vlc:mediaContentDescription="@{item}" android:clickable="true" android:focusable="true" android:longClickable="true" diff --git a/application/television/src/main/res/layout/song_browser.xml b/application/television/src/main/res/layout/song_browser.xml index 227da2b86..ac099721b 100644 --- a/application/television/src/main/res/layout/song_browser.xml +++ b/application/television/src/main/res/layout/song_browser.xml @@ -97,6 +97,7 @@ android:layout_marginEnd="8dp" android:background="@color/transparent" android:elevation="6dp" + android:contentDescription="@string/sortby" app:srcCompat="@drawable/ic_sort" app:layout_constraintEnd_toStartOf="@+id/headerButton" app:layout_constraintTop_toTopOf="parent" /> @@ -109,6 +110,7 @@ android:layout_marginEnd="@dimen/tv_overscan_horizontal" android:background="@color/transparent" android:elevation="6dp" + android:contentDescription="@string/jump_to" app:srcCompat="@drawable/ic_jumptoheader" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -162,6 +164,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" + android:contentDescription="@string/sortby" android:elevation="6dp" android:nextFocusDown="@id/imageButtonHeader" app:srcCompat="@drawable/ic_fabtvmini_sort" @@ -190,6 +193,7 @@ android:layout_height="wrap_content" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" + android:contentDescription="@string/jump_to" android:elevation="6dp" android:nextFocusUp="@id/imageButtonSort" android:nextFocusDown="@id/imageButtonSettings" @@ -209,6 +213,7 @@ android:elevation="6dp" android:nextFocusUp="@id/imageButtonHeader" android:nextFocusForward="@id/imageButtonHeader" + android:contentDescription="@string/talkback_display_settings" app:srcCompat="@drawable/ic_fabtv_preferences" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/headerListContainer" /> diff --git a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt index 80e3db98e..abf724520 100644 --- a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt +++ b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt @@ -52,7 +52,8 @@ fun mediaDescription(v: View, media: MediaLibraryItem) { MediaWrapper.TYPE_VIDEO -> TalkbackUtil.getVideo(v.context, media) MediaWrapper.TYPE_AUDIO -> TalkbackUtil.getAudioTrack(v.context, media) MediaWrapper.TYPE_STREAM -> TalkbackUtil.getStream(v.context, media) - else -> throw NotImplementedError("") + MediaWrapper.TYPE_DIR, MediaWrapper.TYPE_SUBTITLE, MediaWrapper.TYPE_PLAYLIST-> TalkbackUtil.getDir(v.context, media, false) + else -> throw NotImplementedError("Media type not found: ${media.type}") } else -> throw NotImplementedError("Unknown item type") } -- GitLab From a882b29468e6ffa4745b36b056f476cd26763441 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Wed, 1 Jun 2022 12:26:27 +0200 Subject: [PATCH 22/34] Accessibility: improve talkback for Android TV audio player --- .../ui/audioplayer/AudioPlayerActivity.kt | 13 +++++++++++-- .../src/main/res/layout/tv_audio_player.xml | 7 ++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/application/television/src/main/java/org/videolan/television/ui/audioplayer/AudioPlayerActivity.kt b/application/television/src/main/java/org/videolan/television/ui/audioplayer/AudioPlayerActivity.kt index 0f5dba4f2..420379bcd 100644 --- a/application/television/src/main/java/org/videolan/television/ui/audioplayer/AudioPlayerActivity.kt +++ b/application/television/src/main/java/org/videolan/television/ui/audioplayer/AudioPlayerActivity.kt @@ -35,7 +35,9 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.videolan.medialibrary.interfaces.media.MediaWrapper import org.videolan.resources.AndroidDevices import org.videolan.television.R @@ -55,7 +57,6 @@ import org.videolan.vlc.util.getScreenWidth import org.videolan.vlc.viewmodels.BookmarkModel import org.videolan.vlc.viewmodels.PlayerState import org.videolan.vlc.viewmodels.PlaylistModel -import java.lang.Runnable import kotlin.math.abs @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @@ -179,6 +180,7 @@ class AudioPlayerActivity : BaseTvActivity(),KeycodeListener { } wasPlaying = state.playing + binding.buttonPlay.contentDescription = getString(if (state.playing) org.videolan.vlc.R.string.pause else org.videolan.vlc.R.string.play) val mw = model.currentMediaWrapper lifecycleScope.launch { @@ -192,6 +194,7 @@ class AudioPlayerActivity : BaseTvActivity(),KeycodeListener { R.drawable.ic_shuffle_on else R.drawable.ic_shuffle_audio) + binding.buttonShuffle.contentDescription = getString(if (shuffling) org.videolan.vlc.R.string.shuffle_on else org.videolan.vlc.R.string.shuffle) if (mw == null || currentCoverArt == mw.artworkMrl) return@launch currentCoverArt = mw.artworkMrl updateBackground() @@ -352,13 +355,16 @@ class AudioPlayerActivity : BaseTvActivity(),KeycodeListener { when (model.repeatType) { PlaybackStateCompat.REPEAT_MODE_ALL -> { binding.buttonRepeat.setImageResource(R.drawable.ic_repeat_all_audio) + binding.buttonRepeat.contentDescription = getString(R.string.repeat_all) } PlaybackStateCompat.REPEAT_MODE_ONE -> { binding.buttonRepeat.setImageResource(R.drawable.ic_repeat_one_audio) + binding.buttonRepeat.contentDescription = getString(R.string.repeat_single) } PlaybackStateCompat.REPEAT_MODE_NONE -> { model.repeatType = PlaybackStateCompat.REPEAT_MODE_NONE binding.buttonRepeat.setImageResource(R.drawable.ic_repeat_audio) + binding.buttonRepeat.contentDescription = getString(R.string.repeat) } } } @@ -368,14 +374,17 @@ class AudioPlayerActivity : BaseTvActivity(),KeycodeListener { PlaybackStateCompat.REPEAT_MODE_NONE -> { model.repeatType = PlaybackStateCompat.REPEAT_MODE_ALL binding.buttonRepeat.setImageResource(R.drawable.ic_repeat_all_audio) + binding.buttonRepeat.contentDescription = getString(R.string.repeat_all) } PlaybackStateCompat.REPEAT_MODE_ALL -> { model.repeatType = PlaybackStateCompat.REPEAT_MODE_ONE binding.buttonRepeat.setImageResource(R.drawable.ic_repeat_one_audio) + binding.buttonRepeat.contentDescription = getString(R.string.repeat_single) } PlaybackStateCompat.REPEAT_MODE_ONE -> { model.repeatType = PlaybackStateCompat.REPEAT_MODE_NONE binding.buttonRepeat.setImageResource(R.drawable.ic_repeat_audio) + binding.buttonRepeat.contentDescription = getString(R.string.repeat) } } } diff --git a/application/television/src/main/res/layout/tv_audio_player.xml b/application/television/src/main/res/layout/tv_audio_player.xml index 8c35bda9a..e0c2eaabf 100644 --- a/application/television/src/main/res/layout/tv_audio_player.xml +++ b/application/television/src/main/res/layout/tv_audio_player.xml @@ -199,11 +199,12 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="@{progress.timeText}" + android:importantForAccessibility="no" app:layout_constraintBottom_toTopOf="@+id/media_progress" app:layout_constraintStart_toStartOf="@+id/media_progress" tools:text="0:30" /> - Date: Wed, 1 Jun 2022 13:25:06 +0200 Subject: [PATCH 23/34] Accessibility: improve talkback for onboarding --- application/resources/src/main/res/values/strings.xml | 1 + application/vlc-android/res/layout/activity_onboarding.xml | 1 + application/vlc-android/res/layout/onboarding_theme.xml | 3 +++ 3 files changed, 5 insertions(+) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index d9bed6bd9..1aa3b878b 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -297,6 +297,7 @@ Landscape reverse Last locked orientation Black theme + Auto theme Subtitle text encoding Preferred subtitle language DayNight mode diff --git a/application/vlc-android/res/layout/activity_onboarding.xml b/application/vlc-android/res/layout/activity_onboarding.xml index b187390ae..5457decf5 100644 --- a/application/vlc-android/res/layout/activity_onboarding.xml +++ b/application/vlc-android/res/layout/activity_onboarding.xml @@ -22,6 +22,7 @@ android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:background="?attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/close" android:padding="8dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/application/vlc-android/res/layout/onboarding_theme.xml b/application/vlc-android/res/layout/onboarding_theme.xml index eb74d18d6..77f7f1c4e 100644 --- a/application/vlc-android/res/layout/onboarding_theme.xml +++ b/application/vlc-android/res/layout/onboarding_theme.xml @@ -13,6 +13,7 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" android:background="@drawable/theme_selection_rounded" + android:contentDescription="@string/theme_auto" android:padding="16dp" app:layout_constraintBottom_toBottomOf="@+id/lightTheme" app:layout_constraintEnd_toStartOf="@+id/lightTheme" @@ -36,6 +37,7 @@ android:padding="16dp" android:scaleX="0.8" android:scaleY="0.8" + android:contentDescription="@string/light_theme" app:layout_constraintBottom_toTopOf="@+id/textView5" app:layout_constraintEnd_toStartOf="@+id/darkTheme" app:layout_constraintHorizontal_bias="0.5" @@ -54,6 +56,7 @@ android:layout_height="wrap_content" android:layout_marginEnd="16dp" android:padding="16dp" + android:contentDescription="@string/enable_black_theme" android:scaleX="0.8" android:scaleY="0.8" app:layout_constraintBottom_toBottomOf="@+id/lightTheme" -- GitLab From a489f09ba11d41a8a0f88a95c3e0270e252d74fe Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 07:04:38 +0200 Subject: [PATCH 24/34] Accessibility: add a content description for FABs --- .../src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt | 3 ++- .../src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt | 1 + .../src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt | 1 + .../src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt | 1 + .../src/org/videolan/vlc/gui/video/VideoGridFragment.kt | 1 + 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt index 9f02d5b30..21ce00e38 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioAlbumsSongsFragment.kt @@ -40,8 +40,8 @@ import org.videolan.tools.dp import org.videolan.tools.putSingle import org.videolan.vlc.R import org.videolan.vlc.gui.ContentActivity -import org.videolan.vlc.gui.helpers.UiTools import org.videolan.vlc.gui.HeaderMediaListActivity +import org.videolan.vlc.gui.helpers.UiTools import org.videolan.vlc.gui.view.FastScroller import org.videolan.vlc.gui.view.RecyclerSectionItemGridDecoration import org.videolan.vlc.media.MediaUtils @@ -127,6 +127,7 @@ class AudioAlbumsSongsFragment : BaseAudioBrowser(), SwipeR } fabPlay?.setImageResource(R.drawable.ic_fab_play) + fabPlay?.contentDescription = getString(R.string.play) viewModel.albumsProvider.pagedList.observe(requireActivity()) { albums -> @Suppress("UNCHECKED_CAST") (albums as? PagedList)?.let { albumsAdapter.submitList(it) } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt index 5c42a752a..aab8bea75 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt @@ -233,6 +233,7 @@ class AudioBrowserFragment : BaseAudioBrowser() { super.onStart() setFabPlayShuffleAllVisibility() fabPlay?.setImageResource(R.drawable.ic_fab_shuffle) + fabPlay?.contentDescription = getString(R.string.shuffle_play) } override fun onPrepareOptionsMenu(menu: Menu) { diff --git a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt index 76327e9d8..9d266023e 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt @@ -205,6 +205,7 @@ abstract class BaseBrowserFragment : MediaBrowserFragment(), IRefr fabPlay?.run { setImageResource(R.drawable.ic_fab_play) updateFab() + fabPlay?.contentDescription = getString(R.string.play) } (activity as? AudioPlayerContainerActivity)?.expandAppBar() } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt index 0f0fbe1b7..1ba46d60d 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt @@ -100,6 +100,7 @@ class NetworkBrowserFragment : BaseBrowserFragment(), IDialogManager { override fun onStart() { super.onStart() fabPlay?.setImageResource(if (isRootDirectory) R.drawable.ic_fab_add else R.drawable.ic_fab_play) + fabPlay?.contentDescription = getString(if (isRootDirectory) R.string.add else R.string.play) fabPlay?.setOnClickListener(this) } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt index 6b09b6cd7..7973a58c3 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoGridFragment.kt @@ -243,6 +243,7 @@ class VideoGridFragment : MediaBrowserFragment(), SwipeRefreshL updateViewMode() setFabPlayVisibility(true) fabPlay?.setImageResource(R.drawable.ic_fab_play) + fabPlay?.contentDescription = getString(R.string.play) if (!viewModel.isEmpty() && getFilterQuery() == null) viewModel.refresh() } -- GitLab From 6cddeac860f58149c847aa7ce19c81aea9f965c5 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 07:24:15 +0200 Subject: [PATCH 25/34] Accessibility: fix crash for MediaWrapper of type ALL --- .../src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt | 2 ++ .../src/org/videolan/vlc/util/AccessibilityHelper.kt | 1 + 2 files changed, 3 insertions(+) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt index 43dadb8f3..5b39bb1f5 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/TalkbackUtil.kt @@ -92,6 +92,8 @@ object TalkbackUtil { return text } + fun getAll(media: MediaLibraryItem): String = media.title + fun millisToString(context: Context, duration: Long): String { var millis = duration val sb = StringBuilder() diff --git a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt index abf724520..6d8e172c2 100644 --- a/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt +++ b/application/vlc-android/src/org/videolan/vlc/util/AccessibilityHelper.kt @@ -53,6 +53,7 @@ fun mediaDescription(v: View, media: MediaLibraryItem) { MediaWrapper.TYPE_AUDIO -> TalkbackUtil.getAudioTrack(v.context, media) MediaWrapper.TYPE_STREAM -> TalkbackUtil.getStream(v.context, media) MediaWrapper.TYPE_DIR, MediaWrapper.TYPE_SUBTITLE, MediaWrapper.TYPE_PLAYLIST-> TalkbackUtil.getDir(v.context, media, false) + MediaWrapper.TYPE_ALL -> TalkbackUtil.getAll(media) else -> throw NotImplementedError("Media type not found: ${media.type}") } else -> throw NotImplementedError("Unknown item type") -- GitLab From 9594bc8cb92d0e004cb6d7a109aa6d3d4abb0e85 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 07:41:30 +0200 Subject: [PATCH 26/34] Accessibility: use an option item instead of a FAB for all fragments --- .../src/main/res/drawable/ic_am_shuffle.xml | 11 +++++++++++ .../vlc-android/res/menu/activity_option.xml | 8 ++++++++ .../vlc/gui/audio/AudioAlbumsSongsFragment.kt | 6 ++++++ .../vlc/gui/audio/AudioBrowserFragment.kt | 8 +++++++- .../vlc/gui/browser/BaseBrowserFragment.kt | 16 ++++++++++------ .../vlc/gui/browser/NetworkBrowserFragment.kt | 2 +- 6 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 application/resources/src/main/res/drawable/ic_am_shuffle.xml diff --git a/application/resources/src/main/res/drawable/ic_am_shuffle.xml b/application/resources/src/main/res/drawable/ic_am_shuffle.xml new file mode 100644 index 000000000..f79aab6d0 --- /dev/null +++ b/application/resources/src/main/res/drawable/ic_am_shuffle.xml @@ -0,0 +1,11 @@ + + + diff --git a/application/vlc-android/res/menu/activity_option.xml b/application/vlc-android/res/menu/activity_option.xml index 5b7a21a19..3817a4731 100644 --- a/application/vlc-android/res/menu/activity_option.xml +++ b/application/vlc-android/res/menu/activity_option.xml @@ -9,6 +9,14 @@ android:title="@string/play_all" vlc:showAsAction="always" android:visible="false"/> + + (), SwipeR menu.findItem(R.id.ml_menu_display_grid).isVisible = !viewModel.providersInCard[currentTab] menu.findItem(R.id.ml_menu_display_list).isVisible = viewModel.providersInCard[currentTab] menu.findItem(R.id.ml_menu_sortby_media_number).isVisible = canSortByMediaNumber() + if (requireActivity().isTalkbackIsEnabled()) menu.findItem(R.id.play_all).isVisible = true } sortMenuTitles() reopenSearchIfNeeded() @@ -197,6 +199,10 @@ class AudioAlbumsSongsFragment : BaseAudioBrowser(), SwipeR Settings.getInstance(requireActivity()).putSingle(viewModel.displayModeKeys[currentTab], item.itemId == R.id.ml_menu_display_grid) true } + R.id.play_all -> { + onFabPlayClick(fastScroller) + true + } else -> super.onOptionsItemSelected(item) } } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt index aab8bea75..2c244b1d4 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioBrowserFragment.kt @@ -59,6 +59,7 @@ import org.videolan.vlc.gui.view.FastScroller import org.videolan.vlc.media.MediaUtils import org.videolan.vlc.providers.medialibrary.MedialibraryProvider import org.videolan.vlc.util.Permissions +import org.videolan.vlc.util.isTalkbackIsEnabled import org.videolan.vlc.viewmodels.mobile.AudioBrowserViewModel import org.videolan.vlc.viewmodels.mobile.getViewModel @@ -257,7 +258,8 @@ class AudioBrowserFragment : BaseAudioBrowser() { } sortMenuTitles(currentTab) reopenSearchIfNeeded() - } + if (requireActivity().isTalkbackIsEnabled()) menu.findItem(R.id.shuffle_all).isVisible = true + } override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { @@ -276,6 +278,10 @@ class AudioBrowserFragment : BaseAudioBrowser() { viewModel.refresh() true } + R.id.shuffle_all -> { + onFabPlayClick(emptyView) + true + } else -> super.onOptionsItemSelected(item) } } diff --git a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt index 9d266023e..fad826ff7 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/browser/BaseBrowserFragment.kt @@ -69,6 +69,7 @@ import org.videolan.vlc.media.PlaylistManager import org.videolan.vlc.repository.BrowserFavRepository import org.videolan.vlc.util.Permissions import org.videolan.vlc.util.isSchemeSupported +import org.videolan.vlc.util.isTalkbackIsEnabled import org.videolan.vlc.viewmodels.browser.BrowserModel import java.util.* @@ -82,7 +83,7 @@ private const val MSG_REFRESH = 3 private const val MSG_SHOW_ENQUEUING = 4 private const val MSG_HIDE_ENQUEUING = 5 -abstract class BaseBrowserFragment : MediaBrowserFragment(), IRefreshable, SwipeRefreshLayout.OnRefreshListener, View.OnClickListener, IEventsHandler, CtxActionReceiver, PathAdapterListener, BrowserContainer { +abstract class BaseBrowserFragment : MediaBrowserFragment(), IRefreshable, SwipeRefreshLayout.OnRefreshListener, IEventsHandler, CtxActionReceiver, PathAdapterListener, BrowserContainer { private lateinit var addPlaylistFolderOnly: MenuItem protected val handler = BrowserFragmentHandler(this) @@ -132,6 +133,7 @@ abstract class BaseBrowserFragment : MediaBrowserFragment(), IRefr val browserShowHiddenFiles = menu.findItem(R.id.browser_show_hidden_files) browserShowHiddenFiles.isVisible = true browserShowHiddenFiles.isChecked = Settings.getInstance(requireActivity()).getBoolean("browser_show_hidden_files", true) + if (requireActivity().isTalkbackIsEnabled()) menu.findItem(R.id.play_all).isVisible = true } protected open fun defineIsRoot() = mrl == null @@ -302,10 +304,8 @@ abstract class BaseBrowserFragment : MediaBrowserFragment(), IRefr override fun refresh() = viewModel.refresh() - override fun onClick(v: View) { - when (v.id) { - R.id.fab, R.id.fab_large -> playAll(null) - } + override fun onFabPlayClick(view: View) { + playAll(null) } class BrowserFragmentHandler(owner: BaseBrowserFragment) : WeakHandler(owner) { @@ -463,6 +463,10 @@ abstract class BaseBrowserFragment : MediaBrowserFragment(), IRefr currentMedia?.let { requireActivity().addToPlaylistAsync(it.uri.toString(), true) } true } + R.id.play_all -> { + onFabPlayClick(binding.root) + true + } else -> super.onOptionsItemSelected(item) } } @@ -614,7 +618,7 @@ abstract class BaseBrowserFragment : MediaBrowserFragment(), IRefr fabPlay?.let { if (adapter.mediaCount > 0 || viewModel.url?.startsWith("file") == true) { setFabPlayVisibility(true) - it.setOnClickListener(this) + it.setOnClickListener{onFabPlayClick(it)} } else { setFabPlayVisibility(false) it.setOnClickListener(null) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt b/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt index 1ba46d60d..8ab91f93d 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/browser/NetworkBrowserFragment.kt @@ -101,7 +101,7 @@ class NetworkBrowserFragment : BaseBrowserFragment(), IDialogManager { super.onStart() fabPlay?.setImageResource(if (isRootDirectory) R.drawable.ic_fab_add else R.drawable.ic_fab_play) fabPlay?.contentDescription = getString(if (isRootDirectory) R.string.add else R.string.play) - fabPlay?.setOnClickListener(this) + fabPlay?.setOnClickListener { onFabPlayClick(it) } } override fun refresh() { -- GitLab From 5e4abf0149757a5cb9cf5843176736c8f3e629c6 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 07:55:06 +0200 Subject: [PATCH 27/34] Accessibility: improve video player play queue --- application/vlc-android/res/layout/player.xml | 1 + .../src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/application/vlc-android/res/layout/player.xml b/application/vlc-android/res/layout/player.xml index 00a9da399..a7934696d 100644 --- a/application/vlc-android/res/layout/player.xml +++ b/application/vlc-android/res/layout/player.xml @@ -158,6 +158,7 @@ app:srcCompat="@drawable/ic_close_small" android:background="?attr/selectableItemBackgroundBorderless" android:id="@+id/close_button" + android:contentDescription="@string/close" android:padding="8dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt index 05871a3da..b8fde2ba9 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt @@ -793,6 +793,7 @@ class VideoPlayerOverlayDelegate (private val player: VideoPlayerActivity) { playlistContainer.setVisible() playlist.adapter = playlistAdapter player.update() + if (player.isTalkbackIsEnabled()) playlistSearchText.editText?.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) } fun showControls(show: Boolean) { -- GitLab From fb1b6f0926491970697495eef364fee7ee3e5b31 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 08:15:07 +0200 Subject: [PATCH 28/34] Accessibility: audio player playlist toggle improve content description --- application/resources/src/main/res/values/strings.xml | 1 + .../src/org/videolan/vlc/gui/audio/AudioPlayerAnimator.kt | 2 ++ 2 files changed, 3 insertions(+) diff --git a/application/resources/src/main/res/values/strings.xml b/application/resources/src/main/res/values/strings.xml index 1aa3b878b..4ca462875 100644 --- a/application/resources/src/main/res/values/strings.xml +++ b/application/resources/src/main/res/values/strings.xml @@ -254,6 +254,7 @@ Audio player tips Previous/Next song Show playlist + Hide playlist Hold to stop diff --git a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayerAnimator.kt b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayerAnimator.kt index e98f216c6..aa9de5b2b 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayerAnimator.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/audio/AudioPlayerAnimator.kt @@ -102,6 +102,8 @@ internal class AudioPlayerAnimator : IAudioPlayerAnimator, LifecycleObserver { field = value onSlide(1F) binding.playlistSwitch.setImageResource(if (value) R.drawable.ic_playlist_audio else R.drawable.ic_playlist_audio_on) + binding.playlistSwitch.contentDescription = audioPlayer.getString(if (value) R.string.hide_playlist else R.string.show_playlist) + binding.playlistSwitch.announceForAccessibility(audioPlayer.getString(if (value) R.string.hide_playlist else R.string.show_playlist)) audioPlayer.setBottomMargin() } -- GitLab From 1963d43103e72b187f3a3805b58723c48710131b Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 08:28:18 +0200 Subject: [PATCH 29/34] Accessibility: video player update shuffle/repeat content descriptions --- .../videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt b/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt index 8cf013267..2d6b2b7fb 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/helpers/PlayerOptionsDelegate.kt @@ -281,29 +281,35 @@ class PlayerOptionsDelegate(val activity: FragmentActivity, val service: Playbac PlaybackStateCompat.REPEAT_MODE_NONE -> { repeatBinding.optionIcon.setImageResource(R.drawable.ic_repeat_one) service.repeatType = PlaybackStateCompat.REPEAT_MODE_ONE + repeatBinding.root.contentDescription = repeatBinding.root.context.getString(R.string.repeat_single) } PlaybackStateCompat.REPEAT_MODE_ONE -> if (service.hasPlaylist()) { repeatBinding.optionIcon.setImageResource(R.drawable.ic_repeat_all) service.repeatType = PlaybackStateCompat.REPEAT_MODE_ALL + repeatBinding.root.contentDescription = repeatBinding.root.context.getString(R.string.repeat_all) } else { repeatBinding.optionIcon.setImageResource(R.drawable.ic_repeat) service.repeatType = PlaybackStateCompat.REPEAT_MODE_NONE + repeatBinding.root.contentDescription = repeatBinding.root.context.getString(R.string.repeat) } PlaybackStateCompat.REPEAT_MODE_ALL -> { repeatBinding.optionIcon.setImageResource(R.drawable.ic_repeat) service.repeatType = PlaybackStateCompat.REPEAT_MODE_NONE + repeatBinding.root.contentDescription = repeatBinding.root.context.getString(R.string.repeat) } } } private fun setShuffle() { shuffleBinding.optionIcon.setImageResource(if (service.isShuffling) R.drawable.ic_shuffle_on_48dp else R.drawable.ic_shuffle) + shuffleBinding.root.contentDescription = shuffleBinding.root.context.getString(if (service.isShuffling) R.string.shuffle_on else R.string.shuffle) } private fun initShuffle(binding: PlayerOptionItemBinding) { shuffleBinding = binding AppScope.launch(Dispatchers.Main) { shuffleBinding.optionIcon.setImageResource(if (service.isShuffling) R.drawable.ic_shuffle_on_48dp else R.drawable.ic_shuffle) + shuffleBinding.root.contentDescription = shuffleBinding.root.context.getString(if (service.isShuffling) R.string.shuffle_on else R.string.shuffle) } } @@ -315,6 +321,11 @@ class PlayerOptionsDelegate(val activity: FragmentActivity, val service: Playbac PlaybackStateCompat.REPEAT_MODE_ALL -> R.drawable.ic_repeat_all else -> R.drawable.ic_repeat }) + repeatBinding.root.contentDescription = repeatBinding.root.context.getString(when (service.repeatType) { + PlaybackStateCompat.REPEAT_MODE_ONE -> R.string.repeat_single + PlaybackStateCompat.REPEAT_MODE_ALL -> R.string.repeat_all + else -> R.string.repeat + }) } } -- GitLab From 28e463584e901618069a42d839cc554600f3568f Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 08:32:55 +0200 Subject: [PATCH 30/34] Accessibility: add a content description for close icon --- application/vlc-android/res/layout/audio_player_tips.xml | 1 + application/vlc-android/res/layout/audio_playlist_tips.xml | 1 + application/vlc-android/res/layout/player_tips.xml | 3 +-- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/application/vlc-android/res/layout/audio_player_tips.xml b/application/vlc-android/res/layout/audio_player_tips.xml index e946a58ba..c3a85f42b 100644 --- a/application/vlc-android/res/layout/audio_player_tips.xml +++ b/application/vlc-android/res/layout/audio_player_tips.xml @@ -32,6 +32,7 @@ android:background="?attr/actionBarItemBackground" android:nextFocusRight="@+id/add_bookmark" android:onClick="onClickDismissTips" + android:contentDescription="@string/close" android:padding="8dp" app:layout_constraintEnd_toStartOf="@+id/title" app:layout_constraintHorizontal_bias="0.5" diff --git a/application/vlc-android/res/layout/audio_playlist_tips.xml b/application/vlc-android/res/layout/audio_playlist_tips.xml index 83d8efd20..c7da122f2 100644 --- a/application/vlc-android/res/layout/audio_playlist_tips.xml +++ b/application/vlc-android/res/layout/audio_playlist_tips.xml @@ -46,6 +46,7 @@ android:background="?attr/actionBarItemBackground" android:nextFocusRight="@+id/add_bookmark" android:onClick="onClickDismissPlaylistTips" + android:contentDescription="@string/close" android:padding="8dp" app:layout_constraintEnd_toStartOf="@+id/title" app:layout_constraintHorizontal_bias="0.5" diff --git a/application/vlc-android/res/layout/player_tips.xml b/application/vlc-android/res/layout/player_tips.xml index 8873d4bbf..6500e709d 100644 --- a/application/vlc-android/res/layout/player_tips.xml +++ b/application/vlc-android/res/layout/player_tips.xml @@ -15,8 +15,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="?attr/actionBarItemBackground" - - + android:contentDescription="@string/close" android:nextFocusRight="@+id/add_bookmark" android:onClick="onClickDismissTips" android:padding="8dp" -- GitLab From 37ba919724e02dc0aa2902a4481e42de8fa99b9d Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 15:55:37 +0200 Subject: [PATCH 31/34] Accessibility: fix explore by touch issue --- .../src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt index c5cfe94ce..680f01af4 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt @@ -930,7 +930,8 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback, } override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { - return !isLoading && ::touchDelegate.isInitialized && touchDelegate.dispatchGenericMotionEvent(event) + val result = !isLoading && ::touchDelegate.isInitialized && touchDelegate.dispatchGenericMotionEvent(event) + return if (result) true else super.dispatchGenericMotionEvent(event) } override fun onBackPressed() { -- GitLab From f4b8da408a321ea7bfe2fdd045db2d9ba3099292 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Thu, 2 Jun 2022 15:56:43 +0200 Subject: [PATCH 32/34] Accessibility: use the compat version of the AccessibilityDelegate --- .../src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt b/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt index 288f1e4f4..311e8de21 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt @@ -6,13 +6,15 @@ import android.util.Log import android.view.View import android.view.accessibility.AccessibilityEvent import androidx.appcompat.widget.AppCompatSeekBar +import androidx.core.view.AccessibilityDelegateCompat +import androidx.core.view.ViewCompat import org.videolan.resources.BuildConfig import org.videolan.vlc.R import org.videolan.vlc.gui.helpers.TalkbackUtil class AccessibleSeekBar : AppCompatSeekBar { - private val customAccessibilityDelegate = object : View.AccessibilityDelegate() { + private val customAccessibilityDelegate = object : AccessibilityDelegateCompat() { var force = false set(value) { field = value @@ -56,7 +58,7 @@ class AccessibleSeekBar : AppCompatSeekBar { } private fun initialize() { - accessibilityDelegate = customAccessibilityDelegate + ViewCompat.setAccessibilityDelegate(this, customAccessibilityDelegate) } fun forceAccessibilityUpdate() { -- GitLab From 976565a0b254421568076a45590b8b0eac252b72 Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 3 Jun 2022 07:11:39 +0200 Subject: [PATCH 33/34] Accessibility: hide the video player hud when taping the back button --- .../src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt index 680f01af4..0dc0f3a1a 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt @@ -952,7 +952,7 @@ open class VideoPlayerActivity : AppCompatActivity(), PlaybackService.Callback, service?.playlistManager?.videoStatsOn?.postValue(false) } else if (overlayDelegate.isBookmarkShown()) { overlayDelegate.hideBookmarks() - } else if (AndroidDevices.isAndroidTv && isShowing && !isLocked) { + } else if ((AndroidDevices.isAndroidTv || isTalkbackIsEnabled()) && isShowing && !isLocked) { overlayDelegate.hideOverlay(true) } else { exitOK() -- GitLab From 43066f47de77d9aface2384c935dca9744b4343a Mon Sep 17 00:00:00 2001 From: Nicolas Pomepuy Date: Fri, 3 Jun 2022 07:45:21 +0200 Subject: [PATCH 34/34] Accessibility: fix the video player root view selection and announcement Fixes an issue where the video player root view is selected and announced every now and then. It was triggered by the AccessibleSeekBar custom accessibility delegate. --- .../vlc/gui/video/VideoPlayerOverlayDelegate.kt | 1 + .../org/videolan/vlc/gui/view/AccessibleSeekBar.kt | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt index b8fde2ba9..f36fc0f05 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerOverlayDelegate.kt @@ -842,6 +842,7 @@ class VideoPlayerOverlayDelegate (private val player: VideoPlayerActivity) { hudRightBinding.audioDelayQuickAction.text = "${(player.service?.audioDelay ?: 0L) / 1000L} ms" } + if (!show) hudBinding.playerOverlaySeekbar.disableAccessibilityEvents() else hudBinding.playerOverlaySeekbar.enableAccessibilityEvents() } /** diff --git a/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt b/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt index 311e8de21..46a72feb7 100644 --- a/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt +++ b/application/vlc-android/src/org/videolan/vlc/gui/view/AccessibleSeekBar.kt @@ -20,6 +20,7 @@ class AccessibleSeekBar : AppCompatSeekBar { field = value if( value) sendAccessibilityEventUnchecked(this@AccessibleSeekBar, AccessibilityEvent.obtain().apply { eventType = AccessibilityEvent.TYPE_VIEW_SELECTED }) } + var disabled = true /** @@ -32,6 +33,10 @@ class AccessibleSeekBar : AppCompatSeekBar { * @param event the accessibility event to send */ override fun sendAccessibilityEventUnchecked(host: View?, event: AccessibilityEvent) { + if (disabled) { + super.sendAccessibilityEventUnchecked(host, event) + return + } if (BuildConfig.DEBUG) Log.d(this::class.java.simpleName, "sendAccessibilityEventUnchecked: ${event.eventType}") contentDescription = context.getString(R.string.talkback_out_of, TalkbackUtil.millisToString(context, progress.toLong()), TalkbackUtil.millisToString(context, max.toLong()) ) if (event.eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED @@ -62,9 +67,18 @@ class AccessibleSeekBar : AppCompatSeekBar { } fun forceAccessibilityUpdate() { + if (BuildConfig.DEBUG) Log.d(this::class.java.simpleName, "forceAccessibilityUpdate", Exception("Give me a stack")) customAccessibilityDelegate.force = true } + fun disableAccessibilityEvents() { + customAccessibilityDelegate.disabled = true + } + + fun enableAccessibilityEvents() { + customAccessibilityDelegate.disabled = false + } + -- GitLab