react-native-audio-browser 0.2.0-next.12 → 0.2.0-next.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/android/build.gradle +20 -1
- package/android/src/main/java/com/audiobrowser/AudioBrowser.kt +8 -1
- package/android/src/main/java/com/audiobrowser/browser/BrowserManager.kt +52 -0
- package/android/src/main/java/com/audiobrowser/browser/InertStyleDiagnostic.kt +267 -0
- package/android/src/main/java/com/audiobrowser/browser/JsonModels.kt +27 -11
- package/android/src/main/java/com/audiobrowser/browser/SectionScope.kt +1 -1
- package/android/src/main/java/com/audiobrowser/browser/StyleResolver.kt +11 -0
- package/android/src/main/java/com/audiobrowser/extension/ResolvedTrackExt.kt +11 -1
- package/android/src/main/java/com/audiobrowser/model/PlayerUpdateOptions.kt +1 -1
- package/android/src/main/java/com/audiobrowser/player/MediaSessionCallback.kt +21 -22
- package/android/src/main/java/com/audiobrowser/player/PlaybackStateStore.kt +17 -2
- package/android/src/main/java/com/audiobrowser/player/Player.kt +3 -0
- package/android/src/main/java/com/audiobrowser/player/ShuffleOrderLedBy.kt +30 -0
- package/android/src/main/java/com/audiobrowser/util/StyleWire.kt +73 -0
- package/android/src/main/java/com/audiobrowser/util/TrackFactory.kt +16 -6
- package/android/src/main/res/values/strings.xml +37 -2
- package/android/src/test/java/com/audiobrowser/TestFixtures.kt +50 -5
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerLayerResolutionTest.kt +9 -0
- package/android/src/test/java/com/audiobrowser/browser/BrowserManagerTest.kt +3 -2
- package/android/src/test/java/com/audiobrowser/browser/InertStyleDiagnosticTest.kt +277 -0
- package/android/src/test/java/com/audiobrowser/browser/JsonStyleTest.kt +56 -0
- package/android/src/test/java/com/audiobrowser/browser/SectionScopeTest.kt +4 -4
- package/android/src/test/java/com/audiobrowser/browser/StyleResolverTest.kt +77 -10
- package/android/src/test/java/com/audiobrowser/player/PlaybackStateStoreTrackJsonTest.kt +2 -2
- package/android/src/test/java/com/audiobrowser/player/ShuffleOrderLedByTest.kt +79 -0
- package/android/src/test/java/com/audiobrowser/util/MediaExtrasBuilderTest.kt +10 -9
- package/ios/Browser/BrowserManager.swift +106 -45
- package/ios/Browser/InertStyleDiagnostic.swift +279 -0
- package/ios/Browser/JsonModels.swift +81 -15
- package/ios/Browser/SectionPresentation.swift +30 -0
- package/ios/Browser/StyleResolver.swift +11 -0
- package/ios/Browser/TabBarEntries.swift +7 -1
- package/ios/CarPlay/CarPlayController.swift +139 -35
- package/ios/CarPlay/CarPlayListItemFactory.swift +99 -27
- package/ios/Extension/Section+Copying.swift +1 -3
- package/ios/Extension/UIApplication+ActiveScene.swift +27 -0
- package/ios/Http/HttpClient.swift +8 -2
- package/ios/HybridAudioBrowser.swift +56 -12
- package/ios/Model/NitroTypeStubs.swift +60 -1
- package/ios/Player/MediaIntentCriteria.swift +13 -5
- package/ios/Player/MediaLoader.swift +33 -9
- package/ios/Player/NowPlayingUpdater.swift +53 -5
- package/ios/Player/PlaybackCoordinator.swift +11 -4
- package/ios/Player/PlaybackTimer.swift +8 -2
- package/ios/Player/QueueManager.swift +36 -10
- package/ios/Player/ShuffleOrder.swift +23 -0
- package/ios/Player/SleepTimerHandling.swift +13 -0
- package/ios/RemoteCommand/RemoteCommandController.swift +53 -18
- package/ios/Tests/InertStyleDiagnosticTests.swift +200 -0
- package/ios/Tests/JsonModelsTests.swift +76 -1
- package/ios/Tests/QueueManagerTests.swift +72 -6
- package/ios/Tests/RemoteCommandControllerTests.swift +16 -0
- package/ios/Tests/ResolvedTrackCopyingTests.swift +2 -2
- package/ios/Tests/SectionPresentationTests.swift +29 -0
- package/ios/Tests/StyleResolverTests.swift +60 -0
- package/ios/Tests/Support/TestTypes.swift +7 -0
- package/ios/TrackPlayer.swift +57 -34
- package/ios/Util/NetworkMonitor.swift +43 -26
- package/ios/Util/SFSymbolRenderer.swift +24 -2
- package/ios/Util/SVGProcessor.swift +9 -2
- package/lib/commonjs/features/browser-config.js +1 -0
- package/lib/commonjs/features/browser-config.js.map +1 -1
- package/lib/commonjs/features/playback/progress.js +2 -1
- package/lib/commonjs/features/playback/progress.js.map +1 -1
- package/lib/commonjs/features/playback/rate.js +8 -1
- package/lib/commonjs/features/playback/rate.js.map +1 -1
- package/lib/commonjs/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/commonjs/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/module/features/browser-config.js +1 -0
- package/lib/module/features/browser-config.js.map +1 -1
- package/lib/module/features/playback/progress.js +2 -1
- package/lib/module/features/playback/progress.js.map +1 -1
- package/lib/module/features/playback/rate.js +8 -1
- package/lib/module/features/playback/rate.js.map +1 -1
- package/lib/module/web/TrackPlayer/QueueManager.js +14 -0
- package/lib/module/web/TrackPlayer/QueueManager.js.map +1 -1
- package/lib/typescript/src/features/browser-config.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/progress.d.ts.map +1 -1
- package/lib/typescript/src/features/playback/rate.d.ts +8 -1
- package/lib/typescript/src/features/playback/rate.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-native.d.ts +2 -1
- package/lib/typescript/src/types/browser-native.d.ts.map +1 -1
- package/lib/typescript/src/types/browser-nodes.d.ts +116 -9
- package/lib/typescript/src/types/browser-nodes.d.ts.map +1 -1
- package/lib/typescript/src/types/browser.d.ts +36 -0
- package/lib/typescript/src/types/browser.d.ts.map +1 -1
- package/nitrogen/generated/android/AudioBrowserOnLoad.cpp +2 -0
- package/nitrogen/generated/android/c++/JArtworkRequestConfig.hpp +4 -0
- package/nitrogen/generated/android/c++/JBrowseResult.hpp +6 -0
- package/nitrogen/generated/android/c++/JCardImage.hpp +58 -0
- package/nitrogen/generated/android/c++/JFavoriteChangedEvent.hpp +4 -0
- package/nitrogen/generated/android/c++/JFormatNowPlayingParams.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_RequestConfig___Track.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_NowPlayingUpdate____FormatNowPlayingParams.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__optional_std__string____Track.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_RequestConfig_____Track.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_std__vector_Track______SearchParams.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__shared_ptr_Promise_void_____TrackLoadEvent.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__string__.hpp +102 -0
- package/nitrogen/generated/android/c++/JFunc_std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError__std__shared_ptr_Promise_std__variant_ResolvedTrack__BrowseError_______BrowserSourceCallbackParam.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_FavoriteChangedEvent.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_void_PlaybackActiveTrackChangedEvent.hpp +4 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__optional_ResolvedTrack_.hpp +6 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__vector_Track_.hpp +4 -0
- package/nitrogen/generated/android/c++/JGridTile.hpp +61 -0
- package/nitrogen/generated/android/c++/JHybridAudioBrowserSpec.cpp +13 -0
- package/nitrogen/generated/android/c++/JImageShape.hpp +58 -0
- package/nitrogen/generated/android/c++/JMediaRequestConfig.hpp +4 -0
- package/nitrogen/generated/android/c++/JNativeBrowserConfiguration.hpp +20 -1
- package/nitrogen/generated/android/c++/JNativeRouteEntry.hpp +6 -0
- package/nitrogen/generated/android/c++/JNativeSetupPlayerOptions.hpp +4 -0
- package/nitrogen/generated/android/c++/JPlaybackActiveTrackChangedEvent.hpp +4 -0
- package/nitrogen/generated/android/c++/JResolvedTrack.hpp +6 -0
- package/nitrogen/generated/android/c++/JSection.hpp +7 -5
- package/nitrogen/generated/android/c++/JSectionStyle.hpp +30 -3
- package/nitrogen/generated/android/c++/JTrack.hpp +4 -0
- package/nitrogen/generated/android/c++/JTrackLoadEvent.hpp +4 -0
- package/nitrogen/generated/android/c++/JTrackStyle.hpp +24 -3
- package/nitrogen/generated/android/c++/JVariant_ResolvedTrack_BrowseError_Promise_BrowseResult_.hpp +6 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/CardImage.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Func_std__shared_ptr_Promise_std__string__.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/GridTile.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/ImageShape.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/NativeBrowserConfiguration.kt +7 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/Section.kt +2 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/SectionStyle.kt +18 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/audiobrowser/TrackStyle.kt +15 -3
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.cpp +9 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Bridge.hpp +104 -0
- package/nitrogen/generated/ios/AudioBrowser-Swift-Cxx-Umbrella.hpp +9 -0
- package/nitrogen/generated/ios/c++/HybridAudioBrowserSpecSwift.hpp +9 -0
- package/nitrogen/generated/ios/swift/CardImage.swift +40 -0
- package/nitrogen/generated/ios/swift/Func_std__shared_ptr_Promise_std__string__.swift +54 -0
- package/nitrogen/generated/ios/swift/GridTile.swift +44 -0
- package/nitrogen/generated/ios/swift/ImageShape.swift +40 -0
- package/nitrogen/generated/ios/swift/NativeBrowserConfiguration.swift +48 -1
- package/nitrogen/generated/ios/swift/Section.swift +1 -19
- package/nitrogen/generated/ios/swift/SectionStyle.swift +70 -1
- package/nitrogen/generated/ios/swift/TrackStyle.swift +59 -1
- package/nitrogen/generated/shared/c++/CardImage.hpp +76 -0
- package/nitrogen/generated/shared/c++/GridTile.hpp +80 -0
- package/nitrogen/generated/shared/c++/ImageShape.hpp +76 -0
- package/nitrogen/generated/shared/c++/NativeBrowserConfiguration.hpp +6 -2
- package/nitrogen/generated/shared/c++/Section.hpp +1 -5
- package/nitrogen/generated/shared/c++/SectionStyle.hpp +32 -2
- package/nitrogen/generated/shared/c++/TrackStyle.hpp +25 -2
- package/package.json +7 -2
- package/src/features/browser-config.ts +1 -0
- package/src/features/playback/progress.ts +2 -1
- package/src/features/playback/rate.ts +8 -1
- package/src/style-resolution.test.ts +72 -1
- package/src/types/browser-native.ts +5 -1
- package/src/types/browser-nodes.ts +124 -9
- package/src/types/browser.ts +38 -0
- package/src/web/TrackPlayer/QueueManager.test.ts +29 -0
- package/src/web/TrackPlayer/QueueManager.ts +14 -0
- package/website/guide/android-auto.md +114 -0
- package/website/guide/android-certificates.md +409 -0
- package/website/guide/artwork.md +255 -0
- package/website/guide/audio-output.md +135 -0
- package/website/guide/automotive.md +86 -0
- package/website/guide/basic-usage.md +151 -0
- package/website/guide/battery.md +115 -0
- package/website/guide/browser.md +581 -0
- package/website/guide/carplay.md +620 -0
- package/website/guide/configuration.md +173 -0
- package/website/guide/equalizer.md +231 -0
- package/website/guide/errors.md +310 -0
- package/website/guide/favorites.md +154 -0
- package/website/guide/gate.md +143 -0
- package/website/guide/getting-started.md +46 -0
- package/website/guide/hooks.md +238 -0
- package/website/guide/live-streams.md +125 -0
- package/website/guide/metadata.md +239 -0
- package/website/guide/migrating-from-track-player.md +244 -0
- package/website/guide/native-callback-fetch.md +127 -0
- package/website/guide/network.md +86 -0
- package/website/guide/now-playing.md +175 -0
- package/website/guide/playback.md +328 -0
- package/website/guide/queue.md +300 -0
- package/website/guide/remote-controls.md +256 -0
- package/website/guide/search.md +255 -0
- package/website/guide/sleep-timer.md +268 -0
- package/website/guide/track.md +246 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
package com.audiobrowser.browser
|
|
2
|
+
|
|
3
|
+
import com.audiobrowser.TestFixtures.browseTrack
|
|
4
|
+
import com.audiobrowser.TestFixtures.section
|
|
5
|
+
import com.audiobrowser.TestFixtures.sectionStyle
|
|
6
|
+
import com.audiobrowser.TestFixtures.track
|
|
7
|
+
import com.audiobrowser.TestFixtures.trackStyle
|
|
8
|
+
import com.margelo.nitro.audiobrowser.ArtworkRendering
|
|
9
|
+
import com.margelo.nitro.audiobrowser.CardImage
|
|
10
|
+
import com.margelo.nitro.audiobrowser.GridTile
|
|
11
|
+
import com.margelo.nitro.audiobrowser.ImageShape
|
|
12
|
+
import com.margelo.nitro.audiobrowser.Section
|
|
13
|
+
import com.margelo.nitro.audiobrowser.SectionStyle
|
|
14
|
+
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
15
|
+
import org.junit.Assert.assertEquals
|
|
16
|
+
import org.junit.Assert.assertTrue
|
|
17
|
+
import org.junit.Test
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The dev diagnostic for declarations that can never render (ADR 0011): the recovery for the
|
|
21
|
+
* compile-time invalid-combination errors the block model deliberately gave up. Structural
|
|
22
|
+
* inertness only — a surface that can't draw a property it understands is intended usage, never a
|
|
23
|
+
* finding. Mirrors `ios/Tests/InertStyleDiagnosticTests.swift`.
|
|
24
|
+
*/
|
|
25
|
+
class InertStyleDiagnosticTest {
|
|
26
|
+
|
|
27
|
+
private fun findings(
|
|
28
|
+
sections: List<Section>,
|
|
29
|
+
page: SectionStyle? = null,
|
|
30
|
+
path: String = "/home",
|
|
31
|
+
) = InertStyleDiagnostic.findings(path, page, sections)
|
|
32
|
+
|
|
33
|
+
// Container properties
|
|
34
|
+
|
|
35
|
+
@Test
|
|
36
|
+
fun `gridWrap on a list section is inert`() {
|
|
37
|
+
val found = findings(listOf(section(style = sectionStyle(gridWrap = false), title = "Recent")))
|
|
38
|
+
assertEquals(1, found.size)
|
|
39
|
+
assertTrue(found[0], found[0].contains("section 'Recent'"))
|
|
40
|
+
assertTrue(found[0], found[0].contains("style.gridWrap"))
|
|
41
|
+
assertTrue(found[0], found[0].contains("display 'list'"))
|
|
42
|
+
assertTrue(found[0], found[0].contains("/home"))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Test
|
|
46
|
+
fun `gridWrap in a grid is live`() {
|
|
47
|
+
val style = sectionStyle(gridWrap = false, display = StyleDisplay.GRID)
|
|
48
|
+
assertTrue(findings(listOf(section(style = style))).isEmpty())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `a section inherits the page's grid display`() {
|
|
53
|
+
// `display` resolves by scope override, so the page's grid makes the section's `gridWrap` live
|
|
54
|
+
// without the section restating it.
|
|
55
|
+
val found =
|
|
56
|
+
findings(
|
|
57
|
+
listOf(section(style = sectionStyle(gridWrap = false))),
|
|
58
|
+
page = sectionStyle(display = StyleDisplay.GRID),
|
|
59
|
+
)
|
|
60
|
+
assertTrue(found.isEmpty())
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Test
|
|
64
|
+
fun `gridTile on a list section is inert`() {
|
|
65
|
+
val found = findings(listOf(section(style = sectionStyle(gridTile = GridTile.CARD))))
|
|
66
|
+
assertEquals(1, found.size)
|
|
67
|
+
assertTrue(found[0], found[0].contains("style.gridTile"))
|
|
68
|
+
assertTrue(found[0], found[0].contains("section #1"))
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Item properties
|
|
72
|
+
|
|
73
|
+
@Test
|
|
74
|
+
fun `card properties need the card treatment`() {
|
|
75
|
+
val plainGrid =
|
|
76
|
+
sectionStyle(
|
|
77
|
+
display = StyleDisplay.GRID,
|
|
78
|
+
cardTint = "#1e3a8a",
|
|
79
|
+
cardImage = CardImage.BACKGROUND,
|
|
80
|
+
)
|
|
81
|
+
val found = findings(listOf(section(style = plainGrid, title = "Featured")))
|
|
82
|
+
assertEquals(2, found.size)
|
|
83
|
+
assertTrue(found.any { it.contains("style.cardTint") })
|
|
84
|
+
assertTrue(found.any { it.contains("style.cardImage") })
|
|
85
|
+
assertTrue(found.all { it.contains("display 'grid', gridTile 'plain'") })
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@Test
|
|
89
|
+
fun `card properties in a card grid are live`() {
|
|
90
|
+
val cards =
|
|
91
|
+
sectionStyle(
|
|
92
|
+
display = StyleDisplay.GRID,
|
|
93
|
+
gridTile = GridTile.CARD,
|
|
94
|
+
cardTint = "#1e3a8a",
|
|
95
|
+
cardImage = CardImage.BACKGROUND,
|
|
96
|
+
)
|
|
97
|
+
assertTrue(findings(listOf(section(style = cards))).isEmpty())
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@Test
|
|
101
|
+
fun `imageShape needs a shaped tile`() {
|
|
102
|
+
val list = sectionStyle(imageShape = ImageShape.CIRCULAR)
|
|
103
|
+
val cards =
|
|
104
|
+
sectionStyle(
|
|
105
|
+
display = StyleDisplay.GRID,
|
|
106
|
+
gridTile = GridTile.CARD,
|
|
107
|
+
imageShape = ImageShape.CIRCULAR,
|
|
108
|
+
)
|
|
109
|
+
val condensed =
|
|
110
|
+
sectionStyle(
|
|
111
|
+
display = StyleDisplay.GRID,
|
|
112
|
+
gridTile = GridTile.CONDENSED,
|
|
113
|
+
imageShape = ImageShape.CIRCULAR,
|
|
114
|
+
)
|
|
115
|
+
assertEquals(1, findings(listOf(section(style = list))).size)
|
|
116
|
+
assertEquals(1, findings(listOf(section(style = cards))).size)
|
|
117
|
+
// Plain and condensed tiles both take a shape.
|
|
118
|
+
assertTrue(findings(listOf(section(style = condensed))).isEmpty())
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Test
|
|
122
|
+
fun `accessorySymbol is inert only on cards`() {
|
|
123
|
+
val rows = sectionStyle(accessorySymbol = "lock.fill")
|
|
124
|
+
val cards =
|
|
125
|
+
sectionStyle(
|
|
126
|
+
display = StyleDisplay.GRID,
|
|
127
|
+
gridTile = GridTile.CARD,
|
|
128
|
+
accessorySymbol = "lock.fill",
|
|
129
|
+
)
|
|
130
|
+
// List rows draw accessories; card elements have no slot for one.
|
|
131
|
+
assertTrue(findings(listOf(section(style = rows))).isEmpty())
|
|
132
|
+
val found = findings(listOf(section(style = cards)))
|
|
133
|
+
assertEquals(1, found.size)
|
|
134
|
+
assertTrue(found[0], found[0].contains("no accessory slot"))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Test
|
|
138
|
+
fun `artworkRendering is never inert`() {
|
|
139
|
+
val stencilled =
|
|
140
|
+
sectionStyle(
|
|
141
|
+
display = StyleDisplay.GRID,
|
|
142
|
+
gridTile = GridTile.CARD,
|
|
143
|
+
artworkRendering = ArtworkRendering.STENCIL,
|
|
144
|
+
)
|
|
145
|
+
assertTrue(findings(listOf(section(style = stencilled))).isEmpty())
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Track-level declarations
|
|
149
|
+
|
|
150
|
+
@Test
|
|
151
|
+
fun `track declarations are reported per section`() {
|
|
152
|
+
val results =
|
|
153
|
+
section(
|
|
154
|
+
style = sectionStyle(display = StyleDisplay.GRID),
|
|
155
|
+
title = "Results",
|
|
156
|
+
children =
|
|
157
|
+
arrayOf(
|
|
158
|
+
track(style = trackStyle(cardTint = "#111")),
|
|
159
|
+
track(src = "https://s/b.mp3", style = trackStyle(cardTint = "#222")),
|
|
160
|
+
track(src = "https://s/c.mp3"),
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
val found = findings(listOf(results))
|
|
164
|
+
assertEquals(1, found.size)
|
|
165
|
+
assertTrue(found[0], found[0].contains("2 tracks in section 'Results'"))
|
|
166
|
+
assertTrue(found[0], found[0].contains("style.cardTint"))
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@Test
|
|
170
|
+
fun `a track's own shape is live in a plain grid`() {
|
|
171
|
+
val grid =
|
|
172
|
+
section(
|
|
173
|
+
style = sectionStyle(display = StyleDisplay.GRID),
|
|
174
|
+
children = arrayOf(track(style = trackStyle(imageShape = ImageShape.CIRCULAR))),
|
|
175
|
+
)
|
|
176
|
+
assertTrue(findings(listOf(grid)).isEmpty())
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// The page block
|
|
180
|
+
|
|
181
|
+
@Test
|
|
182
|
+
fun `a page declaration live in one section is not reported`() {
|
|
183
|
+
// Card tint reaches both sections; one renders cards. Live is live.
|
|
184
|
+
val found =
|
|
185
|
+
findings(
|
|
186
|
+
listOf(
|
|
187
|
+
section(style = sectionStyle(display = StyleDisplay.GRID, gridTile = GridTile.CARD)),
|
|
188
|
+
section(style = sectionStyle(display = StyleDisplay.GRID)),
|
|
189
|
+
),
|
|
190
|
+
page = sectionStyle(cardTint = "#1e3a8a"),
|
|
191
|
+
)
|
|
192
|
+
assertTrue(found.isEmpty())
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
@Test
|
|
196
|
+
fun `a page declaration no section renders is reported once`() {
|
|
197
|
+
val found =
|
|
198
|
+
findings(
|
|
199
|
+
listOf(
|
|
200
|
+
section(style = sectionStyle(display = StyleDisplay.GRID), title = "A"),
|
|
201
|
+
section(title = "B"),
|
|
202
|
+
),
|
|
203
|
+
page = sectionStyle(cardTint = "#1e3a8a"),
|
|
204
|
+
)
|
|
205
|
+
assertEquals(1, found.size)
|
|
206
|
+
assertTrue(found[0], found[0].contains("the page block"))
|
|
207
|
+
assertTrue(found[0], found[0].contains("no section that inherits it"))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@Test
|
|
211
|
+
fun `a section override shadows the page value`() {
|
|
212
|
+
// Every section declares its own tint, so the page's is never resolved — shadowed, not inert;
|
|
213
|
+
// the diagnostic reports what can't render, not what isn't reached.
|
|
214
|
+
val found =
|
|
215
|
+
findings(
|
|
216
|
+
listOf(
|
|
217
|
+
section(
|
|
218
|
+
style =
|
|
219
|
+
sectionStyle(display = StyleDisplay.GRID, gridTile = GridTile.CARD, cardTint = "#000")
|
|
220
|
+
)
|
|
221
|
+
),
|
|
222
|
+
page = sectionStyle(cardTint = "#1e3a8a"),
|
|
223
|
+
)
|
|
224
|
+
assertTrue(found.isEmpty())
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@Test
|
|
228
|
+
fun `a page-wide grid makes section declarations live`() {
|
|
229
|
+
val found =
|
|
230
|
+
findings(
|
|
231
|
+
listOf(section(style = sectionStyle(cardTint = "#111"))),
|
|
232
|
+
page = sectionStyle(display = StyleDisplay.GRID, gridTile = GridTile.CARD),
|
|
233
|
+
)
|
|
234
|
+
assertTrue(found.isEmpty())
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// The positional `display`
|
|
238
|
+
|
|
239
|
+
@Test
|
|
240
|
+
fun `display on a playable track is inert`() {
|
|
241
|
+
val tracks =
|
|
242
|
+
section(
|
|
243
|
+
title = "Tracks",
|
|
244
|
+
children =
|
|
245
|
+
arrayOf(
|
|
246
|
+
track(style = trackStyle(display = StyleDisplay.GRID)),
|
|
247
|
+
browseTrack(style = trackStyle(display = StyleDisplay.GRID)),
|
|
248
|
+
),
|
|
249
|
+
)
|
|
250
|
+
val found = findings(listOf(tracks))
|
|
251
|
+
assertEquals(1, found.size)
|
|
252
|
+
assertTrue(found[0], found[0].contains("1 track in section 'Tracks'"))
|
|
253
|
+
assertTrue(found[0], found[0].contains("style.display"))
|
|
254
|
+
assertTrue(found[0], found[0].contains("opens none"))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@Test
|
|
258
|
+
fun `display on a section or page is never inert`() {
|
|
259
|
+
val found =
|
|
260
|
+
findings(
|
|
261
|
+
listOf(
|
|
262
|
+
section(
|
|
263
|
+
style = sectionStyle(display = StyleDisplay.LIST),
|
|
264
|
+
children = arrayOf(browseTrack()),
|
|
265
|
+
)
|
|
266
|
+
),
|
|
267
|
+
page = sectionStyle(display = StyleDisplay.GRID),
|
|
268
|
+
)
|
|
269
|
+
assertTrue(found.isEmpty())
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@Test
|
|
273
|
+
fun `an unstyled page is silent`() {
|
|
274
|
+
assertTrue(findings(listOf(section(children = arrayOf(track(), browseTrack())))).isEmpty())
|
|
275
|
+
assertTrue(findings(emptyList()).isEmpty())
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
package com.audiobrowser.browser
|
|
2
2
|
|
|
3
3
|
import com.margelo.nitro.audiobrowser.ArtworkRendering
|
|
4
|
+
import com.margelo.nitro.audiobrowser.CardImage
|
|
5
|
+
import com.margelo.nitro.audiobrowser.GridTile
|
|
6
|
+
import com.margelo.nitro.audiobrowser.ImageShape
|
|
4
7
|
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
5
8
|
import kotlinx.serialization.json.Json
|
|
6
9
|
import org.junit.Assert.assertEquals
|
|
@@ -88,4 +91,57 @@ class JsonStyleTest {
|
|
|
88
91
|
val track = json.decodeFromString<JsonTrack>("""{"title":"X","src":"s","disabled":true}""")
|
|
89
92
|
assertEquals(true, track.toNitro().disabled)
|
|
90
93
|
}
|
|
94
|
+
|
|
95
|
+
@Test
|
|
96
|
+
fun `decodes imageShape and accessorySymbol`() {
|
|
97
|
+
val track =
|
|
98
|
+
json.decodeFromString<JsonTrack>(
|
|
99
|
+
"""{"title":"X","style":{"imageShape":"circular","accessorySymbol":"lock.fill"}}"""
|
|
100
|
+
)
|
|
101
|
+
val style = track.toNitro().style
|
|
102
|
+
assertEquals(ImageShape.CIRCULAR, style?.imageShape)
|
|
103
|
+
assertEquals("lock.fill", style?.accessorySymbol)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@Test
|
|
107
|
+
fun `accessorySymbol 'none' is a value - not emptiness`() {
|
|
108
|
+
// The inheritance-escape sentinel must survive the wire — collapsing it
|
|
109
|
+
// to "no declaration" would let the section value flow back in.
|
|
110
|
+
val track =
|
|
111
|
+
json.decodeFromString<JsonTrack>("""{"title":"X","style":{"accessorySymbol":"none"}}""")
|
|
112
|
+
assertEquals("none", track.toNitro().style?.accessorySymbol)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@Test
|
|
116
|
+
fun `unknown imageShape decodes as no declaration`() {
|
|
117
|
+
val track =
|
|
118
|
+
json.decodeFromString<JsonTrack>("""{"title":"X","style":{"imageShape":"hexagonal"}}""")
|
|
119
|
+
assertNull(track.toNitro().style)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun `decodes the card family`() {
|
|
124
|
+
val resolved =
|
|
125
|
+
json.decodeFromString<JsonResolvedTrack>(
|
|
126
|
+
"""{"path":"/home","title":"Home","sections":[
|
|
127
|
+
{"style":{"display":"grid","gridTile":"card","cardTint":"#1e3a8a","cardImage":"background"},
|
|
128
|
+
"children":[{"title":"C","src":"s"}]}
|
|
129
|
+
]}"""
|
|
130
|
+
)
|
|
131
|
+
val style = resolved.toNitro().sections?.first()?.style
|
|
132
|
+
assertEquals(GridTile.CARD, style?.gridTile)
|
|
133
|
+
assertEquals("#1e3a8a", style?.cardTint)
|
|
134
|
+
assertEquals(CardImage.BACKGROUND, style?.cardImage)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Test
|
|
138
|
+
fun `unknown gridTile decodes as no declaration`() {
|
|
139
|
+
val resolved =
|
|
140
|
+
json.decodeFromString<JsonResolvedTrack>(
|
|
141
|
+
"""{"path":"/home","title":"Home","sections":[
|
|
142
|
+
{"style":{"gridTile":"hexagon"},"children":[{"title":"C","src":"s"}]}
|
|
143
|
+
]}"""
|
|
144
|
+
)
|
|
145
|
+
assertNull(resolved.toNitro().sections?.first()?.style)
|
|
146
|
+
}
|
|
91
147
|
}
|
|
@@ -2,8 +2,8 @@ package com.audiobrowser.browser
|
|
|
2
2
|
|
|
3
3
|
import com.audiobrowser.TestFixtures.resolvedTrack
|
|
4
4
|
import com.audiobrowser.TestFixtures.section
|
|
5
|
+
import com.audiobrowser.TestFixtures.sectionStyle
|
|
5
6
|
import com.audiobrowser.TestFixtures.track
|
|
6
|
-
import com.margelo.nitro.audiobrowser.SectionStyle
|
|
7
7
|
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
8
8
|
import org.junit.Assert.assertEquals
|
|
9
9
|
import org.junit.Assert.assertNull
|
|
@@ -138,7 +138,7 @@ class SectionScopeTest {
|
|
|
138
138
|
val page =
|
|
139
139
|
resolvedTrack(
|
|
140
140
|
style =
|
|
141
|
-
|
|
141
|
+
sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = false),
|
|
142
142
|
sections = arrayOf(section(children = arrayOf(track(src = "a")))),
|
|
143
143
|
)
|
|
144
144
|
|
|
@@ -153,12 +153,12 @@ class SectionScopeTest {
|
|
|
153
153
|
fun `a section's own block overrides the page's in the fold`() {
|
|
154
154
|
val page =
|
|
155
155
|
resolvedTrack(
|
|
156
|
-
style =
|
|
156
|
+
style = sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = null),
|
|
157
157
|
sections =
|
|
158
158
|
arrayOf(
|
|
159
159
|
section(
|
|
160
160
|
style =
|
|
161
|
-
|
|
161
|
+
sectionStyle(display = StyleDisplay.LIST, artworkRendering = null, gridWrap = null),
|
|
162
162
|
children = arrayOf(track(src = "a")),
|
|
163
163
|
)
|
|
164
164
|
),
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
package com.audiobrowser.browser
|
|
2
2
|
|
|
3
|
+
import com.audiobrowser.TestFixtures.sectionStyle
|
|
4
|
+
import com.audiobrowser.TestFixtures.trackStyle
|
|
3
5
|
import com.margelo.nitro.audiobrowser.ArtworkRendering
|
|
4
|
-
import com.margelo.nitro.audiobrowser.
|
|
6
|
+
import com.margelo.nitro.audiobrowser.CardImage
|
|
7
|
+
import com.margelo.nitro.audiobrowser.GridTile
|
|
8
|
+
import com.margelo.nitro.audiobrowser.ImageShape
|
|
5
9
|
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
6
|
-
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
7
10
|
import org.junit.Assert.assertEquals
|
|
8
11
|
import org.junit.Assert.assertNull
|
|
9
12
|
import org.junit.Test
|
|
@@ -20,9 +23,9 @@ class StyleResolverTest {
|
|
|
20
23
|
val resolved =
|
|
21
24
|
StyleResolver.sectionStyle(
|
|
22
25
|
section =
|
|
23
|
-
|
|
26
|
+
sectionStyle(display = StyleDisplay.LIST, artworkRendering = null, gridWrap = null),
|
|
24
27
|
page =
|
|
25
|
-
|
|
28
|
+
sectionStyle(
|
|
26
29
|
display = StyleDisplay.GRID,
|
|
27
30
|
artworkRendering = ArtworkRendering.STENCIL,
|
|
28
31
|
gridWrap = false,
|
|
@@ -39,7 +42,7 @@ class StyleResolverTest {
|
|
|
39
42
|
val resolved =
|
|
40
43
|
StyleResolver.sectionStyle(
|
|
41
44
|
section = null,
|
|
42
|
-
page =
|
|
45
|
+
page = sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = true),
|
|
43
46
|
)
|
|
44
47
|
assertEquals(StyleDisplay.GRID, resolved.display)
|
|
45
48
|
assertEquals(true, resolved.gridWrap)
|
|
@@ -49,9 +52,9 @@ class StyleResolverTest {
|
|
|
49
52
|
fun `track overrides the section per property`() {
|
|
50
53
|
val resolved =
|
|
51
54
|
StyleResolver.trackStyle(
|
|
52
|
-
track =
|
|
55
|
+
track = trackStyle(display = null, artworkRendering = ArtworkRendering.ORIGINAL),
|
|
53
56
|
section =
|
|
54
|
-
|
|
57
|
+
sectionStyle(
|
|
55
58
|
display = StyleDisplay.GRID,
|
|
56
59
|
artworkRendering = ArtworkRendering.STENCIL,
|
|
57
60
|
gridWrap = null,
|
|
@@ -66,7 +69,7 @@ class StyleResolverTest {
|
|
|
66
69
|
StyleResolver.trackStyle(
|
|
67
70
|
track = null,
|
|
68
71
|
section =
|
|
69
|
-
|
|
72
|
+
sectionStyle(display = null, artworkRendering = ArtworkRendering.STENCIL, gridWrap = null),
|
|
70
73
|
)
|
|
71
74
|
assertEquals(ArtworkRendering.STENCIL, resolved.artworkRendering)
|
|
72
75
|
}
|
|
@@ -78,10 +81,74 @@ class StyleResolverTest {
|
|
|
78
81
|
// item's resolved display.
|
|
79
82
|
val resolved =
|
|
80
83
|
StyleResolver.trackStyle(
|
|
81
|
-
track =
|
|
84
|
+
track = trackStyle(display = StyleDisplay.GRID, artworkRendering = null),
|
|
82
85
|
section =
|
|
83
|
-
|
|
86
|
+
sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = null),
|
|
84
87
|
)
|
|
85
88
|
assertNull(resolved.display)
|
|
86
89
|
}
|
|
90
|
+
|
|
91
|
+
@Test
|
|
92
|
+
fun `imageShape inherits and overrides per item`() {
|
|
93
|
+
// Section-wide circular (an artists shelf); one album overrides.
|
|
94
|
+
val inherited =
|
|
95
|
+
StyleResolver.trackStyle(
|
|
96
|
+
track = null,
|
|
97
|
+
section = sectionStyle(imageShape = ImageShape.CIRCULAR),
|
|
98
|
+
)
|
|
99
|
+
assertEquals(ImageShape.CIRCULAR, inherited.imageShape)
|
|
100
|
+
val overridden =
|
|
101
|
+
StyleResolver.trackStyle(
|
|
102
|
+
track = trackStyle(imageShape = ImageShape.ROUNDED_RECTANGLE),
|
|
103
|
+
section = sectionStyle(imageShape = ImageShape.CIRCULAR),
|
|
104
|
+
)
|
|
105
|
+
assertEquals(ImageShape.ROUNDED_RECTANGLE, overridden.imageShape)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Test
|
|
109
|
+
fun `accessorySymbol inherits and 'none' resolves as a value`() {
|
|
110
|
+
val inherited =
|
|
111
|
+
StyleResolver.trackStyle(track = null, section = sectionStyle(accessorySymbol = "lock.fill"))
|
|
112
|
+
assertEquals("lock.fill", inherited.accessorySymbol)
|
|
113
|
+
// 'none' is the inheritance escape — it must survive resolution intact
|
|
114
|
+
// (the renderer, not the resolver, maps it to "no accessory").
|
|
115
|
+
val escaped =
|
|
116
|
+
StyleResolver.trackStyle(
|
|
117
|
+
track = trackStyle(accessorySymbol = "none"),
|
|
118
|
+
section = sectionStyle(accessorySymbol = "lock.fill"),
|
|
119
|
+
)
|
|
120
|
+
assertEquals("none", escaped.accessorySymbol)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@Test
|
|
124
|
+
fun `gridTile resolves by scope override`() {
|
|
125
|
+
// A page-wide card treatment; one section opts back to plain tiles.
|
|
126
|
+
val inherited =
|
|
127
|
+
StyleResolver.sectionStyle(section = null, page = sectionStyle(gridTile = GridTile.CARD))
|
|
128
|
+
assertEquals(GridTile.CARD, inherited.gridTile)
|
|
129
|
+
val overridden =
|
|
130
|
+
StyleResolver.sectionStyle(
|
|
131
|
+
section = sectionStyle(gridTile = GridTile.PLAIN),
|
|
132
|
+
page = sectionStyle(gridTile = GridTile.CARD),
|
|
133
|
+
)
|
|
134
|
+
assertEquals(GridTile.PLAIN, overridden.gridTile)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@Test
|
|
138
|
+
fun `card properties inherit and override per item`() {
|
|
139
|
+
val inherited =
|
|
140
|
+
StyleResolver.trackStyle(
|
|
141
|
+
track = null,
|
|
142
|
+
section = sectionStyle(cardTint = "#1e3a8a", cardImage = CardImage.BACKGROUND),
|
|
143
|
+
)
|
|
144
|
+
assertEquals("#1e3a8a", inherited.cardTint)
|
|
145
|
+
assertEquals(CardImage.BACKGROUND, inherited.cardImage)
|
|
146
|
+
val overridden =
|
|
147
|
+
StyleResolver.trackStyle(
|
|
148
|
+
track = trackStyle(cardImage = CardImage.NORMAL),
|
|
149
|
+
section = sectionStyle(cardTint = "#1e3a8a", cardImage = CardImage.BACKGROUND),
|
|
150
|
+
)
|
|
151
|
+
assertEquals(CardImage.NORMAL, overridden.cardImage)
|
|
152
|
+
assertEquals("#1e3a8a", overridden.cardTint)
|
|
153
|
+
}
|
|
87
154
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
package com.audiobrowser.player
|
|
2
2
|
|
|
3
3
|
import com.audiobrowser.TestFixtures
|
|
4
|
+
import com.audiobrowser.TestFixtures.trackStyle
|
|
4
5
|
import com.margelo.nitro.audiobrowser.ArtworkRendering
|
|
5
6
|
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
6
|
-
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
7
7
|
import org.junit.Assert.assertEquals
|
|
8
8
|
import org.junit.Assert.assertNull
|
|
9
9
|
import org.junit.Test
|
|
@@ -25,7 +25,7 @@ class PlaybackStateStoreTrackJsonTest {
|
|
|
25
25
|
TestFixtures.track(title = "Night Mix", src = "https://s/a.mp3", disabled = true)
|
|
26
26
|
.copy(
|
|
27
27
|
style =
|
|
28
|
-
|
|
28
|
+
trackStyle(display = StyleDisplay.GRID, artworkRendering = ArtworkRendering.STENCIL)
|
|
29
29
|
)
|
|
30
30
|
|
|
31
31
|
val restored = PlaybackStateStore.trackFromJson(PlaybackStateStore.trackToJson(track))
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
package com.audiobrowser.player
|
|
2
|
+
|
|
3
|
+
import androidx.media3.common.C
|
|
4
|
+
import org.junit.Assert.assertEquals
|
|
5
|
+
import org.junit.Assert.assertNotEquals
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Media3's `ShuffleOrder.cloneAndSet(insertionCount, startIndex)` discards the start index (the
|
|
11
|
+
* interface default clears and re-inserts from 0, and `DefaultShuffleOrder` does not override it),
|
|
12
|
+
* so `setMediaItems(items, startIndex, …)` leaves the starting track at a random shuffle position —
|
|
13
|
+
* last, for a 1-in-N share of queues, which reads as end-of-queue from the very first track.
|
|
14
|
+
*/
|
|
15
|
+
class ShuffleOrderLedByTest {
|
|
16
|
+
@Test
|
|
17
|
+
fun `the start index leads the order`() {
|
|
18
|
+
for (start in 0 until 6) {
|
|
19
|
+
val order = shuffleOrderLedBy(startIndex = start, count = 6)
|
|
20
|
+
assertEquals("start index must be at shuffle position 0", start, order.firstIndex)
|
|
21
|
+
assertNotEquals(
|
|
22
|
+
"a queue of 6 must have somewhere to advance to from its start track",
|
|
23
|
+
C.INDEX_UNSET.toLong(),
|
|
24
|
+
order.getNextIndex(start).toLong(),
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun `every index appears exactly once`() {
|
|
31
|
+
val order = shuffleOrderLedBy(startIndex = 3, count = 8)
|
|
32
|
+
assertEquals(8, order.length)
|
|
33
|
+
val seen = mutableListOf<Int>()
|
|
34
|
+
var index = order.firstIndex
|
|
35
|
+
while (index != C.INDEX_UNSET) {
|
|
36
|
+
seen.add(index)
|
|
37
|
+
index = order.getNextIndex(index)
|
|
38
|
+
}
|
|
39
|
+
assertEquals((0 until 8).toList(), seen.sorted())
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Test
|
|
43
|
+
fun `a single-track queue leads with its only track and ends there`() {
|
|
44
|
+
val order = shuffleOrderLedBy(startIndex = 0, count = 1)
|
|
45
|
+
assertEquals(0, order.firstIndex)
|
|
46
|
+
assertEquals(C.INDEX_UNSET, order.getNextIndex(0))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Test
|
|
50
|
+
fun `an out-of-range start index is clamped rather than throwing`() {
|
|
51
|
+
assertEquals(4, shuffleOrderLedBy(startIndex = 99, count = 5).firstIndex)
|
|
52
|
+
assertEquals(0, shuffleOrderLedBy(startIndex = -3, count = 5).firstIndex)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@Test
|
|
56
|
+
fun `an empty queue produces an empty order`() {
|
|
57
|
+
assertEquals(0, shuffleOrderLedBy(startIndex = 0, count = 0).length)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Test
|
|
61
|
+
fun `the tail is actually shuffled`() {
|
|
62
|
+
// Not a strict guarantee of any single draw — but across many draws an
|
|
63
|
+
// unshuffled tail would be impossible to miss.
|
|
64
|
+
val tails =
|
|
65
|
+
(0 until 50)
|
|
66
|
+
.map { shuffleOrderLedBy(startIndex = 0, count = 8) }
|
|
67
|
+
.map { order ->
|
|
68
|
+
buildList {
|
|
69
|
+
var index = order.getNextIndex(order.firstIndex)
|
|
70
|
+
while (index != C.INDEX_UNSET) {
|
|
71
|
+
add(index)
|
|
72
|
+
index = order.getNextIndex(index)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
.toSet()
|
|
77
|
+
assertTrue("50 draws produced one identical tail — the order is not shuffled", tails.size > 1)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -2,10 +2,10 @@ package com.audiobrowser.util
|
|
|
2
2
|
|
|
3
3
|
import androidx.media.utils.MediaConstants
|
|
4
4
|
import com.audiobrowser.TestFixtures
|
|
5
|
+
import com.audiobrowser.TestFixtures.sectionStyle
|
|
6
|
+
import com.audiobrowser.TestFixtures.trackStyle
|
|
5
7
|
import com.audiobrowser.browser.styleResolvedSections
|
|
6
|
-
import com.margelo.nitro.audiobrowser.SectionStyle
|
|
7
8
|
import com.margelo.nitro.audiobrowser.StyleDisplay
|
|
8
|
-
import com.margelo.nitro.audiobrowser.TrackStyle
|
|
9
9
|
import org.junit.Assert.assertEquals
|
|
10
10
|
import org.junit.Assert.assertFalse
|
|
11
11
|
import org.junit.Test
|
|
@@ -23,7 +23,7 @@ class MediaExtrasBuilderTest {
|
|
|
23
23
|
private val track = TestFixtures.track()
|
|
24
24
|
|
|
25
25
|
private fun sectionStyle(display: StyleDisplay?) =
|
|
26
|
-
|
|
26
|
+
sectionStyle(display = display, artworkRendering = null, gridWrap = null)
|
|
27
27
|
|
|
28
28
|
@Test
|
|
29
29
|
fun `owning section title stamps the group-title hint`() {
|
|
@@ -65,7 +65,7 @@ class MediaExtrasBuilderTest {
|
|
|
65
65
|
@Test
|
|
66
66
|
fun `a track's own display never sets its item hint - display is positional`() {
|
|
67
67
|
val gridPromiseTrack =
|
|
68
|
-
track.copy(style =
|
|
68
|
+
track.copy(style = trackStyle(display = StyleDisplay.GRID, artworkRendering = null))
|
|
69
69
|
val section = TestFixtures.section(arrayOf(gridPromiseTrack), style = null)
|
|
70
70
|
val extras = MediaExtrasBuilder.build(gridPromiseTrack, section)
|
|
71
71
|
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM))
|
|
@@ -75,7 +75,7 @@ class MediaExtrasBuilderTest {
|
|
|
75
75
|
fun `a browsable track's declared display emits the parent-level promise`() {
|
|
76
76
|
val handle =
|
|
77
77
|
TestFixtures.browseTrack()
|
|
78
|
-
.copy(style =
|
|
78
|
+
.copy(style = trackStyle(display = StyleDisplay.GRID, artworkRendering = null))
|
|
79
79
|
val extras = MediaExtrasBuilder.build(handle)
|
|
80
80
|
val expected = MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
|
|
81
81
|
assertEquals(
|
|
@@ -99,7 +99,7 @@ class MediaExtrasBuilderTest {
|
|
|
99
99
|
@Test
|
|
100
100
|
fun `a playable track's display is inert - playables open no page`() {
|
|
101
101
|
val playable =
|
|
102
|
-
track.copy(style =
|
|
102
|
+
track.copy(style = trackStyle(display = StyleDisplay.GRID, artworkRendering = null))
|
|
103
103
|
val extras = MediaExtrasBuilder.build(playable)
|
|
104
104
|
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE))
|
|
105
105
|
assertFalse(extras.containsKey(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE))
|
|
@@ -128,10 +128,11 @@ class MediaExtrasBuilderTest {
|
|
|
128
128
|
arrayOf(track),
|
|
129
129
|
title = "Popular",
|
|
130
130
|
path = "/popular",
|
|
131
|
-
style =
|
|
131
|
+
style = sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = false),
|
|
132
132
|
)
|
|
133
|
-
val navigation = TrackFactory.navigationTrack(section)
|
|
133
|
+
val navigation = TrackFactory.navigationTrack(section, "View more")
|
|
134
134
|
assertEquals(StyleDisplay.GRID, navigation.style?.display)
|
|
135
|
+
assertEquals("View more", navigation.title)
|
|
135
136
|
|
|
136
137
|
val extras = MediaExtrasBuilder.build(navigation)
|
|
137
138
|
assertEquals(
|
|
@@ -147,7 +148,7 @@ class MediaExtrasBuilderTest {
|
|
|
147
148
|
// narrower unit test stays green while the page tier goes dark.
|
|
148
149
|
val page =
|
|
149
150
|
TestFixtures.resolvedTrack(
|
|
150
|
-
style =
|
|
151
|
+
style = sectionStyle(display = StyleDisplay.GRID, artworkRendering = null, gridWrap = null),
|
|
151
152
|
sections = arrayOf(TestFixtures.section(arrayOf(track))),
|
|
152
153
|
)
|
|
153
154
|
|