App 2.45.0: now playing on the Overview
NowPlayingWatcher follows the head unit's media sessions (listening only, never connecting to a player); NowPlaying picks what plays and cleans the text. Tiles shows source, title, artist, cover and progress under the battery; Halloween carves title and artist into the hill. Settings: "Show what's playing", on by default. Nothing is logged or sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
package com.evsuite.abrp;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class NowPlayingTest {
|
||||
|
||||
private static NowPlaying.Session session(String pkg, int state, String title, String artist, long updatedAt) {
|
||||
NowPlaying.Session s = new NowPlaying.Session();
|
||||
s.packageName = pkg;
|
||||
s.state = state;
|
||||
s.title = title;
|
||||
s.artist = artist;
|
||||
s.positionUpdatedAt = updatedAt;
|
||||
return s;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nothingToShow() {
|
||||
assertNull(NowPlaying.pick(null));
|
||||
assertNull(NowPlaying.pick(Collections.emptyList()));
|
||||
// Bluetooth's empty session after the phone stopped, and one that only connects.
|
||||
assertNull(NowPlaying.pick(Arrays.asList(
|
||||
session("com.android.bluetooth", 1, "", "", 5),
|
||||
session("com.android.car.radio", 8, "Radio", null, 6),
|
||||
session("com.android.bluetooth", NowPlaying.STATE_PLAYING, " ", null, 7))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whatPlaysWinsOverWhatIsPaused() {
|
||||
// As on the car on 2026-10-28: CarPlay paused, Bluetooth playing.
|
||||
NowPlaying.Track t = NowPlaying.pick(Arrays.asList(
|
||||
session("com.allgo.remoteui.mediabrowserservice", NowPlaying.STATE_PAUSED, "A Mix", "Bass Bangers", 900),
|
||||
session("com.android.bluetooth", NowPlaying.STATE_PLAYING, "Paramount", "Mark Petrie & Andrew Prahlow", 100)));
|
||||
assertEquals(NowPlaying.Source.BLUETOOTH, t.source);
|
||||
assertEquals("Paramount", t.title);
|
||||
assertEquals("Mark Petrie & Andrew Prahlow", t.subtitle);
|
||||
assertTrue(t.playing);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofTwoPlayingTheNewerWins() {
|
||||
NowPlaying.Track t = NowPlaying.pick(Arrays.asList(
|
||||
session("com.android.bluetooth", NowPlaying.STATE_PLAYING, "Old", null, 100),
|
||||
session("com.allgo.remoteui.mediabrowserservice", NowPlaying.STATE_BUFFERING, "New", null, 200)));
|
||||
assertEquals(NowPlaying.Source.CARPLAY, t.source);
|
||||
assertEquals("New", t.title);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNothingPlayingThePausedOneShows() {
|
||||
NowPlaying.Track t = NowPlaying.pick(Arrays.asList(
|
||||
session("com.android.bluetooth", NowPlaying.STATE_PAUSED, "Earlier", null, 100),
|
||||
session("com.allgo.remoteui.mediabrowserservice", NowPlaying.STATE_PAUSED, "Later", null, 300)));
|
||||
assertEquals("Later", t.title);
|
||||
assertFalse(t.playing);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theRadioShowsItsTextWithTheStationBelow() {
|
||||
NowPlaying.Track t = NowPlaying.pick(Collections.singletonList(
|
||||
session("com.saicmotor.radio", NowPlaying.STATE_PLAYING, "Brillux Radio", "BLIND MELON - NO RAIN", 1)));
|
||||
assertEquals(NowPlaying.Source.RADIO, t.source);
|
||||
assertEquals("BLIND MELON - NO RAIN", t.title);
|
||||
assertEquals("Brillux Radio", t.subtitle);
|
||||
NowPlaying.Track station = NowPlaying.pick(Collections.singletonList(
|
||||
session("com.saicmotor.radio", NowPlaying.STATE_PLAYING, "Brillux Radio", "", 1)));
|
||||
assertEquals("Brillux Radio", station.title);
|
||||
assertNull(station.subtitle);
|
||||
assertEquals(-1f, station.progressAt(5), 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void textIsCleaned() {
|
||||
assertEquals("A Letter to the Past (A Seasonal Feels Mix) Ft. Porter Robinson & Friends By SAINT",
|
||||
NowPlaying.clean("A Letter to the Past (A Seasonal Feels Mix) Ft. Porter Robinson & Friends By SAINT [X4nayMeMBuQ]"));
|
||||
assertEquals("Live [Remastered 2011]", NowPlaying.clean("Live [Remastered 2011]"));
|
||||
assertEquals("two lines", NowPlaying.clean(" two\n lines "));
|
||||
assertNull(NowPlaying.clean(" "));
|
||||
assertNull(NowPlaying.clean(null));
|
||||
StringBuilder longText = new StringBuilder();
|
||||
for (int i = 0; i < 300; i++) longText.append('x');
|
||||
assertEquals(200, NowPlaying.clean(longText.toString()).length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyAnArtistStillShows() {
|
||||
NowPlaying.Track t = NowPlaying.pick(Collections.singletonList(
|
||||
session("com.example.player", NowPlaying.STATE_PLAYING, null, "Someone", 1)));
|
||||
assertEquals(NowPlaying.Source.OTHER, t.source);
|
||||
assertEquals("Someone", t.title);
|
||||
assertNull(t.subtitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thePositionRunsOnWhilePlayingAndStopsAtTheEnd() {
|
||||
NowPlaying.Session s = session("com.android.bluetooth", NowPlaying.STATE_PLAYING, "Paramount", "M", 10_000);
|
||||
s.positionMs = 3_000;
|
||||
s.durationMs = 215_000;
|
||||
NowPlaying.Track t = NowPlaying.pick(Collections.singletonList(s));
|
||||
assertEquals(3_000, t.positionAt(10_000));
|
||||
assertEquals(8_000, t.positionAt(15_000));
|
||||
assertEquals(215_000, t.positionAt(10_000_000));
|
||||
assertEquals(1f, t.progressAt(10_000_000), 0f);
|
||||
assertEquals(8_000 / 215_000f, t.progressAt(15_000), 1e-6f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aPausedPositionStaysPut() {
|
||||
NowPlaying.Session s = session("com.android.bluetooth", NowPlaying.STATE_PAUSED, "Paramount", null, 10_000);
|
||||
s.positionMs = 3_000;
|
||||
s.durationMs = 215_000;
|
||||
assertEquals(3_000, NowPlaying.pick(Collections.singletonList(s)).positionAt(99_000));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noPositionNoBar() {
|
||||
NowPlaying.Session s = session("com.android.bluetooth", NowPlaying.STATE_PLAYING, "Paramount", null, 10_000);
|
||||
s.durationMs = 215_000;
|
||||
assertEquals(-1f, NowPlaying.pick(Collections.singletonList(s)).progressAt(20_000), 0f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sourcesAreKnownByTheirApps() {
|
||||
assertEquals(NowPlaying.Source.BLUETOOTH, NowPlaying.source("com.android.bluetooth"));
|
||||
assertEquals(NowPlaying.Source.CARPLAY, NowPlaying.source("com.allgo.remoteui.mediabrowserservice"));
|
||||
assertEquals(NowPlaying.Source.RADIO, NowPlaying.source("com.saicmotor.radio"));
|
||||
assertEquals(NowPlaying.Source.OTHER, NowPlaying.source("com.android.music"));
|
||||
assertEquals(NowPlaying.Source.OTHER, NowPlaying.source(null));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user