Auto commit Sat Mar 21 10:01:01 PM CDT 2026

This commit is contained in:
Tretzi
2026-03-21 22:01:01 -05:00
parent 93607c0d6a
commit 5c48387dc2
6 changed files with 495 additions and 153 deletions

View File

@@ -0,0 +1,144 @@
package com.mindmachine.mvp.data
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.mindmachine.mvp.session.TimelineCurve
import com.mindmachine.mvp.session.TimelineEditorState
import com.mindmachine.mvp.session.TimelinePoint
import com.mindmachine.mvp.session.TimelineSelection
import com.mindmachine.mvp.session.TimelineViewport
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import org.json.JSONArray
import org.json.JSONObject
private val Context.programDataStore by preferencesDataStore("mindmachine_programs")
class UserProgramRepository(private val context: Context) {
private object Keys {
val programsJson = stringPreferencesKey("user_programs_json")
}
val userPrograms: Flow<List<UserProgramEntity>> = context.programDataStore.data.map { prefs ->
parsePrograms(prefs[Keys.programsJson])
}
suspend fun upsert(program: UserProgramEntity) {
context.programDataStore.edit { prefs ->
val current = parsePrograms(prefs[Keys.programsJson]).toMutableList()
val idx = current.indexOfFirst { it.id == program.id }
if (idx >= 0) {
current[idx] = program
} else {
current += program
}
prefs[Keys.programsJson] = toJson(current)
}
}
suspend fun delete(id: String) {
context.programDataStore.edit { prefs ->
val current = parsePrograms(prefs[Keys.programsJson]).filterNot { it.id == id }
prefs[Keys.programsJson] = toJson(current)
}
}
private fun parsePrograms(raw: String?): List<UserProgramEntity> {
if (raw.isNullOrBlank()) return emptyList()
return runCatching {
val arr = JSONArray(raw)
buildList {
for (i in 0 until arr.length()) {
val o = arr.getJSONObject(i)
add(
UserProgramEntity(
id = o.getString("id"),
name = o.getString("name"),
description = o.optString("description", "Custom program"),
timeline = parseTimeline(o.getJSONObject("timeline")),
sortOrder = o.optInt("sortOrder", i + 1000),
)
)
}
}
}.getOrDefault(emptyList())
}
private fun parseTimeline(o: JSONObject): TimelineEditorState {
fun parseCurve(name: String): TimelineCurve {
val points = o.getJSONArray(name)
return TimelineCurve(
points = buildList {
for (i in 0 until points.length()) {
val p = points.getJSONObject(i)
add(TimelinePoint(tSec = p.getInt("t"), value01 = p.getDouble("v").toFloat()))
}
}
)
}
return TimelineEditorState(
durationSec = o.getInt("durationSec"),
selection = TimelineSelection(
startSec = o.optInt("selectionStartSec", 0),
endSec = o.optInt("selectionEndSec", o.getInt("durationSec")),
),
viewport = TimelineViewport(
startSec = o.optDouble("viewportStartSec", 0.0).toFloat(),
secondsPerScreen = o.optDouble("viewportSecondsPerScreen", 600.0).toFloat(),
),
activeCurve = TimelineEditorState.ActiveCurve.valueOf(o.optString("activeCurve", TimelineEditorState.ActiveCurve.VISUAL_LEFT.name)),
visualLeft = parseCurve("visualLeft"),
visualRight = parseCurve("visualRight"),
audioLeft = parseCurve("audioLeft"),
audioRight = parseCurve("audioRight"),
isPlaying = false,
playheadSec = 0f,
).setDurationSec(o.getInt("durationSec"))
}
private fun toJson(programs: List<UserProgramEntity>): String {
val arr = JSONArray()
programs.sortedBy { it.sortOrder }.forEach { program ->
val o = JSONObject()
o.put("id", program.id)
o.put("name", program.name)
o.put("description", program.description)
o.put("sortOrder", program.sortOrder)
val timeline = JSONObject()
timeline.put("durationSec", program.timeline.durationSec)
timeline.put("selectionStartSec", program.timeline.selection.startSec)
timeline.put("selectionEndSec", program.timeline.selection.endSec)
timeline.put("viewportStartSec", program.timeline.viewport.startSec)
timeline.put("viewportSecondsPerScreen", program.timeline.viewport.secondsPerScreen)
timeline.put("activeCurve", program.timeline.activeCurve.name)
timeline.put("visualLeft", pointsArray(program.timeline.visualLeft))
timeline.put("visualRight", pointsArray(program.timeline.visualRight))
timeline.put("audioLeft", pointsArray(program.timeline.audioLeft))
timeline.put("audioRight", pointsArray(program.timeline.audioRight))
o.put("timeline", timeline)
arr.put(o)
}
return arr.toString()
}
private fun pointsArray(curve: TimelineCurve): JSONArray {
val arr = JSONArray()
curve.points.forEach { p ->
arr.put(JSONObject().put("t", p.tSec).put("v", p.value01.toDouble()))
}
return arr
}
}
data class UserProgramEntity(
val id: String,
val name: String,
val description: String,
val timeline: TimelineEditorState,
val sortOrder: Int,
)