From 8e6f015e30c5efb64a49f73290d0a81814c0328f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 2 May 2026 23:46:31 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Add=20tooltips=20to?= =?UTF-8?q?=20audio=20and=20video=20player=20controls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This UX improvement adds `android:tooltipText` attributes to the icon-only playback buttons (play/pause, forward, rewind) in the audio and video viewer components. 💡 What: Added static and dynamic tooltips to media playback controls. 🎯 Why: To improve accessibility and user understanding on devices supporting long-press or mouse-hover interactions for icon-only buttons. ♿ Accessibility: Tooltips mirror the `contentDescription` natively on Android API 26+ devices, assisting mouse and stylus users without disrupting screen readers. Co-authored-by: dlukt <201112286+dlukt@users.noreply.github.com> --- .jules/bolt.md | 3 +++ .../displayitems/AudioStatusDisplayItem.java | 6 +++++- .../techidon/ui/photoviewer/PhotoViewer.java | 18 +++++++++++++++--- .../src/main/res/layout/display_item_audio.xml | 3 +++ .../src/main/res/layout/photo_viewer_ui.xml | 1 + 5 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 12742c31b..54a6a3b49 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -57,3 +57,6 @@ ## 2025-10-29 - [Stream Allocation and O(N^2) trap in HomeTimelineFragment] **Learning:** `HomeTimelineFragment.loadNewPosts` used a Java Stream to collect `existingIds` into a `List`, and then called `toAdd.removeIf(s -> existingIds.contains(s.getID()))`. Since `List.contains` is O(N), this created an O(N * M) operation in a hot path, causing both allocation overhead from Streams and quadratic performance degradation for duplicate checking. **Action:** Replaced the Stream with a simple `for` loop that populates a `HashSet`. This eliminates the Stream allocations and reduces the time complexity of the duplicate check from O(N * M) to O(N + M) because `HashSet.contains` is O(1). Additionally, replaced other `Optional` and `Stream` usages in `loadNewPosts` and `onGapClick` with simple loops to further reduce GC pressure. +## 2025-02-12 - [Dynamic TooltipText Parity for Icon-only Buttons] +**Learning:** When dynamically updating the `contentDescription` of icon-only buttons (like play/pause states) in Java code, the `tooltipText` attribute must also be updated to ensure continued accessibility support for hover and long-press interactions. +**Action:** Always mirror `setContentDescription()` calls with `setTooltipText()` (wrapped in `Build.VERSION.SDK_INT >= Build.VERSION_CODES.O`) for icon-only interactive elements. diff --git a/mastodon/src/main/java/de/icod/techidon/ui/displayitems/AudioStatusDisplayItem.java b/mastodon/src/main/java/de/icod/techidon/ui/displayitems/AudioStatusDisplayItem.java index aa971d09d..b540ea27a 100644 --- a/mastodon/src/main/java/de/icod/techidon/ui/displayitems/AudioStatusDisplayItem.java +++ b/mastodon/src/main/java/de/icod/techidon/ui/displayitems/AudioStatusDisplayItem.java @@ -216,7 +216,11 @@ private void updateColors(int mainColor){ private void setPlayButtonPlaying(boolean playing, boolean animated){ playPauseBtn.setImageResource(playing ? R.drawable.ic_fluent_pause_48_regular : R.drawable.ic_fluent_play_48_regular); - playPauseBtn.setContentDescription(item.parentFragment.getString(playing ? R.string.pause : R.string.play)); + String desc = item.parentFragment.getString(playing ? R.string.pause : R.string.play); + playPauseBtn.setContentDescription(desc); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + playPauseBtn.setTooltipText(desc); + } if(playing) bgDrawable.startAnimation(); else diff --git a/mastodon/src/main/java/de/icod/techidon/ui/photoviewer/PhotoViewer.java b/mastodon/src/main/java/de/icod/techidon/ui/photoviewer/PhotoViewer.java index f4d740f08..766e5a13a 100644 --- a/mastodon/src/main/java/de/icod/techidon/ui/photoviewer/PhotoViewer.java +++ b/mastodon/src/main/java/de/icod/techidon/ui/photoviewer/PhotoViewer.java @@ -692,7 +692,11 @@ private void pauseVideo(){ return; holder.player.pause(); videoPlayPauseButton.setImageResource(R.drawable.ic_fluent_play_24_filled); - videoPlayPauseButton.setContentDescription(activity.getString(R.string.play)); + String desc = activity.getString(R.string.play); + videoPlayPauseButton.setContentDescription(desc); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + videoPlayPauseButton.setTooltipText(desc); + } stopUpdatingVideoPosition(); windowView.removeCallbacks(uiAutoHider); // Some MediaPlayer implementations clear the texture when the app goes into background. @@ -706,7 +710,11 @@ private void resumeVideo(){ return; player.start(); videoPlayPauseButton.setImageResource(R.drawable.ic_fluent_pause_24_filled); - videoPlayPauseButton.setContentDescription(activity.getString(R.string.pause)); + String desc = activity.getString(R.string.pause); + videoPlayPauseButton.setContentDescription(desc); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + videoPlayPauseButton.setTooltipText(desc); + } startUpdatingVideoPosition(player); } @@ -1161,7 +1169,11 @@ public void onSeekComplete(MediaPlayer mp){ @Override public void onCompletion(MediaPlayer mp){ videoPlayPauseButton.setImageResource(R.drawable.ic_fluent_play_24_filled); - videoPlayPauseButton.setContentDescription(activity.getString(R.string.play)); + String desc = activity.getString(R.string.play); + videoPlayPauseButton.setContentDescription(desc); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + videoPlayPauseButton.setTooltipText(desc); + } stopUpdatingVideoPosition(); if(!uiVisible) toggleUI(); diff --git a/mastodon/src/main/res/layout/display_item_audio.xml b/mastodon/src/main/res/layout/display_item_audio.xml index 65fae2e16..e71cb8204 100644 --- a/mastodon/src/main/res/layout/display_item_audio.xml +++ b/mastodon/src/main/res/layout/display_item_audio.xml @@ -38,6 +38,7 @@ android:layout_height="96dp" android:background="@drawable/bg_audio_play_button" android:contentDescription="@string/play" + android:tooltipText="@string/play" android:layout_gravity="center" android:tint="?colorM3DarkOnSurface" android:src="@drawable/ic_fluent_play_48_regular"/> @@ -45,6 +46,7 @@