Recently Written · git

subread-overlay

Shows the lines of an .srt over any Android media player, in time with it. Tap a word to look it up.

git clone https://github.com/equwal/subread-overlay

Log | Files | Refs


app/src/androidTest/kotlin/space/subread/overlay/PlayerProviderTest.kt (4205 bytes)

1 package space.subread.overlay
2 
3 import android.media.session.MediaSession
4 import android.media.session.PlaybackState
5 import android.os.SystemClock
6 import androidx.core.net.toUri
7 import androidx.test.ext.junit.runners.AndroidJUnit4
8 import androidx.test.platform.app.InstrumentationRegistry
9 import org.junit.Assert.assertEquals
10 import org.junit.Assert.assertTrue
11 import org.junit.Assume.assumeTrue
12 import org.junit.Test
13 import org.junit.runner.RunWith
14 
15 /**
16  * The provider that a reader app asks for the position. The test app plays the part of the
17  * player with its own media session. The device must give this app notification access, else
18  * the test is skipped.
19  */
20 @RunWith(AndroidJUnit4::class)
21 class PlayerProviderTest {
22 
23     private val context = InstrumentationRegistry.getInstrumentation().targetContext
24     private val uri = "content://${PlayerProvider.AUTHORITY}/state".toUri()
25 
26     private fun state(state: Int, position: Long, speed: Float = 1f) = PlaybackState.Builder()
27         .setState(state, position, speed, SystemClock.elapsedRealtime())
28         .setActions(PlaybackState.ACTION_PLAY or PlaybackState.ACTION_PAUSE or PlaybackState.ACTION_SEEK_TO)
29         .build()
30 
31     private fun read(): Map<String, String> {
32         val cursor = context.contentResolver.query(uri, null, null, null, null)!!
33         cursor.use {
34             assertTrue(it.moveToFirst())
35             val line = it.getString(it.getColumnIndexOrThrow(PlayerProvider.COLUMN_STATE))
36             return line.split(';').associate { part -> part.substringBefore('=') to part.substringAfter('=') }
37         }
38     }
39 
40     @Test
41     fun theStateLineHasThePositionOfNow() {
42         val paused = PlaybackState.Builder().setState(PlaybackState.STATE_PAUSED, 1_000, 1f, 5_000).build()
43         assertEquals(
44             "playing=0;position=1000;speed=1.0;package=p",
45             PlayerProvider.stateLine(paused, "p", 9_000),
46         )
47         val playing = PlaybackState.Builder().setState(PlaybackState.STATE_PLAYING, 1_000, 2f, 5_000).build()
48         assertEquals(
49             "playing=1;position=9000;speed=2.0;package=p",
50             PlayerProvider.stateLine(playing, "p", 9_000),
51         )
52         val unknown = PlaybackState.Builder()
53             .setState(PlaybackState.STATE_PLAYING, PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1f, 0).build()
54         assertEquals("playing=1;position=-1;speed=1.0;package=p", PlayerProvider.stateLine(unknown, "p", 9_000))
55     }
56 
57     @Test
58     fun aReaderAppReadsThePlayerAndControlsIt() {
59         assumeTrue("notification access is not given to this app", MediaListener.isAllowed(context))
60         val session = MediaSession(context, "test player")
61         val seeks = mutableListOf<Long>()
62         var plays = 0
63         var pauses = 0
64         session.setCallback(object : MediaSession.Callback() {
65             override fun onPlay() { plays++ }
66             override fun onPause() { pauses++ }
67             override fun onSeekTo(pos: Long) { seeks += pos }
68         })
69         try {
70             session.setPlaybackState(state(PlaybackState.STATE_PLAYING, 96_000, speed = 1.5f))
71             session.isActive = true
72 
73             val seen = read()
74             assertEquals("1", seen["playing"])
75             assertEquals("1.5", seen["speed"])
76             assertEquals(context.packageName, seen["package"])
77             val position = seen["position"]!!.toLong()
78             assertTrue("position $position is not near 96000", position in 96_000..97_500)
79 
80             val resolver = context.contentResolver
81             resolver.call(uri, PlayerProvider.METHOD_PAUSE, null, null)
82             resolver.call(uri, PlayerProvider.METHOD_PLAY, null, null)
83             val answer = resolver.call(uri, PlayerProvider.METHOD_SEEK, "12345", null)!!
84             val until = SystemClock.elapsedRealtime() + 5_000
85             while ((plays < 1 || pauses < 1 || seeks.isEmpty()) && SystemClock.elapsedRealtime() < until) Thread.sleep(20)
86             assertEquals(1, plays)
87             assertEquals(1, pauses)
88             assertEquals(listOf(12_345L), seeks)
89             assertTrue(answer.getString(PlayerProvider.COLUMN_STATE)!!.startsWith("playing="))
90         } finally {
91             session.release()
92         }
93     }
94 }