-
-
Notifications
You must be signed in to change notification settings - Fork 92
/
Copy pathGlobalMetadata.kt
152 lines (131 loc) · 5.35 KB
/
GlobalMetadata.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package me.hufman.androidautoidrive.carapp.music
import de.bmw.idrive.BMWRemoting
import io.bimmergestalt.idriveconnectkit.rhmi.*
import me.hufman.androidautoidrive.UnicodeCleaner
import me.hufman.androidautoidrive.carapp.L
import me.hufman.androidautoidrive.music.MusicAction
import me.hufman.androidautoidrive.music.MusicAppInfo
import me.hufman.androidautoidrive.music.MusicController
import me.hufman.androidautoidrive.music.MusicMetadata
import java.io.IOException
import kotlin.math.max
import kotlin.math.min
class GlobalMetadata(app: RHMIApplication, var controller: MusicController) {
val multimediaInfoEvent: RHMIEvent.MultimediaInfoEvent
val statusbarEvent: RHMIEvent.StatusbarEvent
val instrumentCluster: RHMIComponent.InstrumentCluster
var displayedApp: MusicAppInfo? = null
var displayedSong: MusicMetadata? = null
var displayedQueue: List<MusicMetadata>? = null
var icQueue: List<MusicMetadata> = ArrayList()
init {
multimediaInfoEvent = app.events.values.filterIsInstance<RHMIEvent.MultimediaInfoEvent>().first()
statusbarEvent = app.events.values.filterIsInstance<RHMIEvent.StatusbarEvent>().first()
instrumentCluster = app.components.values.filterIsInstance<RHMIComponent.InstrumentCluster>().first()
}
companion object {
val QUEUE_SKIPPREVIOUS = MusicMetadata(mediaId = "__QUEUE_SKIPBACK__", title="< ${L.MUSIC_SKIP_PREVIOUS}")
val QUEUE_SKIPNEXT = MusicMetadata(mediaId = "__QUEUE_SKIPNEXT__", title="${L.MUSIC_SKIP_NEXT} >")
const val QUEUE_BACK_COUNT = 15 // how far back to allow scrolling
const val QUEUE_NEXT_COUNT = 25 // how far forward to allow scrolling
}
fun initWidgets() {
instrumentCluster.getSetTrackAction()?.asRAAction()?.rhmiActionCallback = RHMIActionListCallback { onClick(it) }
}
fun forgetDisplayedInfo() {
displayedApp = null
displayedSong = null
displayedQueue = null
}
fun redraw() {
val app = controller.currentAppInfo
if (app != null && app != displayedApp) {
showApp(app)
}
val song = controller.getMetadata()
if (song != null && song != displayedSong) {
showSong(song)
}
val queue = controller.getQueue()?.songs
if (queue != displayedQueue || song != displayedSong) {
val icQueue = prepareQueue(queue, song)
showQueue(icQueue, song)
this.icQueue = icQueue
}
displayedApp = app
displayedSong = song
displayedQueue = queue
}
private fun showApp(app: MusicAppInfo) {
// set the name of the app
statusbarEvent.getTextModel()?.asRaDataModel()?.value = app.name
statusbarEvent.triggerEvent()
}
private fun showSong(song: MusicMetadata) {
// show in the sidebar
val artistModel = multimediaInfoEvent.getTextModel1()?.asRaDataModel()
val trackModel = multimediaInfoEvent.getTextModel2()?.asRaDataModel()
artistModel?.value = UnicodeCleaner.clean(song.artist ?: "")
trackModel?.value = UnicodeCleaner.clean(song.title ?: "")
// show in the IC
instrumentCluster.getTextModel()?.asRaDataModel()?.value = UnicodeCleaner.clean(song.title ?: "")
// actually tell the car to load the data
multimediaInfoEvent.triggerEvent()
}
/**
* Decorates a song queue with a Back/Next action around the current song
*/
fun prepareQueue(songQueue: List<MusicMetadata>?, currentSong: MusicMetadata?): List<MusicMetadata> {
val queue = ArrayList<MusicMetadata>(songQueue?.size ?: 0 + 3)
val index = songQueue?.indexOfFirst { it.queueId == currentSong?.queueId } ?: -1
fun addPrevious(): Unit = if (controller.isSupportedAction(MusicAction.SKIP_TO_PREVIOUS)) { queue.add(QUEUE_SKIPPREVIOUS); Unit } else Unit
fun addNext(): Unit = if (controller.isSupportedAction(MusicAction.SKIP_TO_NEXT)) { queue.add(QUEUE_SKIPNEXT); Unit } else Unit
if (songQueue != null && currentSong != null && index >= 0) {
// add the previous/next actions around the current song
// This allows for using the shuffle mode's back/next and also the queue selection
queue.addAll(songQueue.subList(max(0, index - QUEUE_BACK_COUNT), index))
addPrevious()
queue.add(currentSong)
addNext()
if (index < songQueue.count()) {
queue.addAll(songQueue.subList(index + 1, min(songQueue.count(), index + QUEUE_NEXT_COUNT)))
}
} else {
addPrevious()
if (currentSong != null) { queue.add(currentSong) }
addNext()
}
return queue
}
private fun showQueue(queue: List<MusicMetadata>, currentSong: MusicMetadata?) {
val adapter = object: RHMIModel.RaListModel.RHMIListAdapter<MusicMetadata>(7, queue) {
override fun convertRow(index: Int, item: MusicMetadata): Array<Any> {
val selected = item.queueId == currentSong?.queueId
return arrayOf(
index, // index
UnicodeCleaner.clean(item.title ?: ""), // title
UnicodeCleaner.clean(item.artist ?: ""), // artist
UnicodeCleaner.clean(item.album ?: ""), // album
-1,
if (selected) 1 else 0, // checked
true
)
}
}
try {
instrumentCluster.getUseCaseModel()?.asRaDataModel()?.value = "EntICPlaylist"
instrumentCluster.getPlaylistModel()?.asRaListModel()?.value = adapter
} catch (e: IOException) {
// This playlist model call has been observed to crash, for some reason
}
}
private fun onClick(index: Int) {
val song = icQueue.getOrNull(index)
when {
song == QUEUE_SKIPPREVIOUS -> controller.skipToPrevious()
song == QUEUE_SKIPNEXT -> controller.skipToNext()
song == controller.getMetadata() -> controller.seekTo(0)
song?.queueId != null -> controller.playQueue(song)
}
}
}