Fill in years 2–4 of networks and hardware.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pepaskopec-blip 2026-10-07 21:07:47 +02:00
commit 10980f24b5
27 changed files with 6345 additions and 64 deletions

View file

@ -156,6 +156,11 @@ class AppViewModel(app: Application) : AndroidViewModel(app) {
refresh()
}
fun markCourse(course: String, id: Int) {
progress.markCourse(course, id)
refresh()
}
fun markLit(id: Int) {
progress.markLit(id)
refresh()

View file

@ -51,7 +51,9 @@ import org.maturita.maturita.ui.On4QuizScreen
import org.maturita.maturita.ui.OnQuizScreen
import org.maturita.maturita.ui.OnYearsScreen
import org.maturita.maturita.ui.EnYearsScreen
import org.maturita.maturita.ui.ChoiceLessonScreen
import org.maturita.maturita.ui.DeHomeScreen
import org.maturita.maturita.ui.ItYearScreen
import org.maturita.maturita.ui.EngExScreen
import org.maturita.maturita.ui.PlotScreen
import org.maturita.maturita.ui.RoadmapScreen
@ -244,6 +246,30 @@ private fun MaturitaAppBody(vm: AppViewModel) {
}
}
is Route.NetEx -> NetQuizScreen(vm, r.id)
Route.Net2Map -> ItYearScreen(vm, "net2", "net_year2", "net2_sub") { Route.Net2Lesson(it) }
is Route.Net2Lesson -> {
val l = vm.content.itLesson("net2", r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Net2Ex(r.id))
}
}
is Route.Net2Ex -> ChoiceLessonScreen(vm, vm.content.itLesson("net2", r.id)) { vm.markCourse("net2", r.id) }
Route.Net3Map -> ItYearScreen(vm, "net3", "net_year3", "net3_sub") { Route.Net3Lesson(it) }
is Route.Net3Lesson -> {
val l = vm.content.itLesson("net3", r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Net3Ex(r.id))
}
}
is Route.Net3Ex -> ChoiceLessonScreen(vm, vm.content.itLesson("net3", r.id)) { vm.markCourse("net3", r.id) }
Route.Net4Map -> ItYearScreen(vm, "net4", "net_year4", "net4_sub") { Route.Net4Lesson(it) }
is Route.Net4Lesson -> {
val l = vm.content.itLesson("net4", r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Net4Ex(r.id))
}
}
is Route.Net4Ex -> ChoiceLessonScreen(vm, vm.content.itLesson("net4", r.id)) { vm.markCourse("net4", r.id) }
Route.HwYears -> HwYearsScreen(vm)
Route.HwMap -> LessonListScreen(
vm, vm.tr("hw_year1"), vm.tr("hw_sub"),
@ -259,6 +285,30 @@ private fun MaturitaAppBody(vm: AppViewModel) {
}
}
is Route.HwEx -> HwQuizScreen(vm, r.id)
Route.Hw2Map -> ItYearScreen(vm, "hw2", "hw_year2", "hw2_sub") { Route.Hw2Lesson(it) }
is Route.Hw2Lesson -> {
val l = vm.content.itLesson("hw2", r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Hw2Ex(r.id))
}
}
is Route.Hw2Ex -> ChoiceLessonScreen(vm, vm.content.itLesson("hw2", r.id)) { vm.markCourse("hw2", r.id) }
Route.Hw3Map -> ItYearScreen(vm, "hw3", "hw_year3", "hw3_sub") { Route.Hw3Lesson(it) }
is Route.Hw3Lesson -> {
val l = vm.content.itLesson("hw3", r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Hw3Ex(r.id))
}
}
is Route.Hw3Ex -> ChoiceLessonScreen(vm, vm.content.itLesson("hw3", r.id)) { vm.markCourse("hw3", r.id) }
Route.Hw4Map -> ItYearScreen(vm, "hw4", "hw_year4", "hw4_sub") { Route.Hw4Lesson(it) }
is Route.Hw4Lesson -> {
val l = vm.content.itLesson("hw4", r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Hw4Ex(r.id))
}
}
is Route.Hw4Ex -> ChoiceLessonScreen(vm, vm.content.itLesson("hw4", r.id)) { vm.markCourse("hw4", r.id) }
Route.EnYears -> EnYearsScreen(vm)
is Route.EnMap -> LessonListScreen(
vm, vm.tr("en_year${r.year}"), vm.tr("en_y${r.year}_sub"),

View file

@ -17,10 +17,28 @@ sealed class Route(val page: String) {
data object NetMap : Route("netmap")
data class NetLesson(val id: Int) : Route("netunit$id")
data class NetEx(val id: Int) : Route("netex$id")
data object Net2Map : Route("net2map")
data class Net2Lesson(val id: Int) : Route("net2unit$id")
data class Net2Ex(val id: Int) : Route("net2ex$id")
data object Net3Map : Route("net3map")
data class Net3Lesson(val id: Int) : Route("net3unit$id")
data class Net3Ex(val id: Int) : Route("net3ex$id")
data object Net4Map : Route("net4map")
data class Net4Lesson(val id: Int) : Route("net4unit$id")
data class Net4Ex(val id: Int) : Route("net4ex$id")
data object HwYears : Route("hwyears")
data object HwMap : Route("hwmap")
data class HwLesson(val id: Int) : Route("hwunit$id")
data class HwEx(val id: Int) : Route("hwex$id")
data object Hw2Map : Route("hw2map")
data class Hw2Lesson(val id: Int) : Route("hw2unit$id")
data class Hw2Ex(val id: Int) : Route("hw2ex$id")
data object Hw3Map : Route("hw3map")
data class Hw3Lesson(val id: Int) : Route("hw3unit$id")
data class Hw3Ex(val id: Int) : Route("hw3ex$id")
data object Hw4Map : Route("hw4map")
data class Hw4Lesson(val id: Int) : Route("hw4unit$id")
data class Hw4Ex(val id: Int) : Route("hw4ex$id")
data object OnYears : Route("onyears")
data object OnMap : Route("onmap")
data class OnLesson(val id: Int) : Route("onunit$id")
@ -102,6 +120,8 @@ sealed class Route(val page: String) {
val isExercise: Boolean
get() = this is GermanEx || this is Vocab || this is NetEx || this is HwEx ||
this is OnEx || this is On2Ex || this is On3Ex || this is On4Ex || this is EnLesson || this is EnEx || this is DeLesson || this is DeEx || this is MluvEx || this is BookQuiz || this is BookPlot ||
this is Net2Lesson || this is Net2Ex || this is Net3Lesson || this is Net3Ex || this is Net4Lesson || this is Net4Ex ||
this is Hw2Lesson || this is Hw2Ex || this is Hw3Lesson || this is Hw3Ex || this is Hw4Lesson || this is Hw4Ex ||
this is NetLesson || this is HwLesson || this is OnLesson || this is On2Lesson || this is On3Lesson || this is On4Lesson ||
this is LitLesson || this is LitEx || this is Lit2Lesson || this is Lit2Ex ||
this is Lit3Lesson || this is Lit3Ex || this is Lit4Lesson || this is Lit4Ex ||
@ -129,10 +149,28 @@ sealed class Route(val page: String) {
NetMap -> listOf(Subjects, NetYears, NetMap)
is NetLesson -> listOf(Subjects, NetYears, NetMap, this)
is NetEx -> listOf(Subjects, NetYears, NetMap, NetLesson(id), this)
Net2Map -> listOf(Subjects, NetYears, Net2Map)
is Net2Lesson -> listOf(Subjects, NetYears, Net2Map, this)
is Net2Ex -> listOf(Subjects, NetYears, Net2Map, Net2Lesson(id), this)
Net3Map -> listOf(Subjects, NetYears, Net3Map)
is Net3Lesson -> listOf(Subjects, NetYears, Net3Map, this)
is Net3Ex -> listOf(Subjects, NetYears, Net3Map, Net3Lesson(id), this)
Net4Map -> listOf(Subjects, NetYears, Net4Map)
is Net4Lesson -> listOf(Subjects, NetYears, Net4Map, this)
is Net4Ex -> listOf(Subjects, NetYears, Net4Map, Net4Lesson(id), this)
HwYears -> listOf(Subjects, HwYears)
HwMap -> listOf(Subjects, HwYears, HwMap)
is HwLesson -> listOf(Subjects, HwYears, HwMap, this)
is HwEx -> listOf(Subjects, HwYears, HwMap, HwLesson(id), this)
Hw2Map -> listOf(Subjects, HwYears, Hw2Map)
is Hw2Lesson -> listOf(Subjects, HwYears, Hw2Map, this)
is Hw2Ex -> listOf(Subjects, HwYears, Hw2Map, Hw2Lesson(id), this)
Hw3Map -> listOf(Subjects, HwYears, Hw3Map)
is Hw3Lesson -> listOf(Subjects, HwYears, Hw3Map, this)
is Hw3Ex -> listOf(Subjects, HwYears, Hw3Map, Hw3Lesson(id), this)
Hw4Map -> listOf(Subjects, HwYears, Hw4Map)
is Hw4Lesson -> listOf(Subjects, HwYears, Hw4Map, this)
is Hw4Ex -> listOf(Subjects, HwYears, Hw4Map, Hw4Lesson(id), this)
OnYears -> listOf(Subjects, OnYears)
OnMap -> listOf(Subjects, OnYears, OnMap)
is OnLesson -> listOf(Subjects, OnYears, OnMap, this)
@ -218,6 +256,12 @@ fun routeFromPage(page: String): Route? = when (page) {
"dehome" -> Route.DeHome
"netyears" -> Route.NetYears
"netmap" -> Route.NetMap
"net2map" -> Route.Net2Map
"net3map" -> Route.Net3Map
"net4map" -> Route.Net4Map
"hw2map" -> Route.Hw2Map
"hw3map" -> Route.Hw3Map
"hw4map" -> Route.Hw4Map
"hwyears" -> Route.HwYears
"hwmap" -> Route.HwMap
"onyears" -> Route.OnYears
@ -375,6 +419,42 @@ fun routeFromPage(page: String): Route? = when (page) {
Regex("""de(\d)ex(\d+)""").matchEntire(page)?.let {
return Route.DeEx(it.groupValues[1].toInt(), it.groupValues[2].toInt())
}
Regex("""net([234])unit(\d+)""").matchEntire(page)?.let {
val year = it.groupValues[1].toInt()
val id = it.groupValues[2].toInt()
return when (year) {
3 -> Route.Net3Lesson(id)
4 -> Route.Net4Lesson(id)
else -> Route.Net2Lesson(id)
}
}
Regex("""net([234])ex(\d+)""").matchEntire(page)?.let {
val year = it.groupValues[1].toInt()
val id = it.groupValues[2].toInt()
return when (year) {
3 -> Route.Net3Ex(id)
4 -> Route.Net4Ex(id)
else -> Route.Net2Ex(id)
}
}
Regex("""hw([234])unit(\d+)""").matchEntire(page)?.let {
val year = it.groupValues[1].toInt()
val id = it.groupValues[2].toInt()
return when (year) {
3 -> Route.Hw3Lesson(id)
4 -> Route.Hw4Lesson(id)
else -> Route.Hw2Lesson(id)
}
}
Regex("""hw([234])ex(\d+)""").matchEntire(page)?.let {
val year = it.groupValues[1].toInt()
val id = it.groupValues[2].toInt()
return when (year) {
3 -> Route.Hw3Ex(id)
4 -> Route.Hw4Ex(id)
else -> Route.Hw2Ex(id)
}
}
Regex("""en(\d)map""").matchEntire(page)?.let {
return Route.EnMap(it.groupValues[1].toInt())
}

View file

@ -29,6 +29,12 @@ class Content(root: JSONObject) {
}
}
val hw = raw.arr("hw")
val net2 = raw.arr("net2")
val net3 = raw.arr("net3")
val net4 = raw.arr("net4")
val hw2 = raw.arr("hw2")
val hw3 = raw.arr("hw3")
val hw4 = raw.arr("hw4")
val on = raw.arr("on")
val on2 = raw.arr("on2")
val on3 = raw.arr("on3")
@ -63,6 +69,16 @@ class Content(root: JSONObject) {
fun germanUnit(id: Int) = german.firstOrNull { it.int("id") == id }
fun netLesson(id: Int) = netLessons.firstOrNull { it.int("id") == id }
fun hwLesson(id: Int) = hw.firstOrNull { it.int("id") == id }
fun itLessons(course: String) = when (course) {
"net2" -> net2
"net3" -> net3
"net4" -> net4
"hw2" -> hw2
"hw3" -> hw3
"hw4" -> hw4
else -> emptyList()
}
fun itLesson(course: String, id: Int) = itLessons(course).firstOrNull { it.int("id") == id }
fun onLesson(id: Int) = on.firstOrNull { it.int("id") == id }
fun on2Lesson(id: Int) = on2.firstOrNull { it.int("id") == id }
fun on3Lesson(id: Int) = on3.firstOrNull { it.int("id") == id }

View file

@ -48,6 +48,8 @@ class ProgressStore(context: Context) {
fun markEn(year: Int, id: Int) { prefs.edit().putBoolean("en.$year.$id", true).apply() }
fun deDone(year: Int, id: Int) = prefs.getBoolean("de.$year.$id", false)
fun markDe(year: Int, id: Int) { prefs.edit().putBoolean("de.$year.$id", true).apply() }
fun courseDone(course: String, id: Int) = prefs.getBoolean("$course.$id", false)
fun markCourse(course: String, id: Int) { prefs.edit().putBoolean("$course.$id", true).apply() }
fun litDone(id: Int) = prefs.getBoolean("lit.$id", false)
fun markLit(id: Int) { prefs.edit().putBoolean("lit.$id", true).apply() }
@ -119,6 +121,16 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum {
sum.doneUnits += 1
}
}
listOf("net2", "net3", "net4", "hw2", "hw3", "hw4").forEach { course ->
content.itLessons(course).forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.courseDone(course, l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
}
content.on.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1

View file

@ -1077,6 +1077,18 @@ private fun MathBoard(
}
}
@Composable
fun ChoiceLessonScreen(vm: AppViewModel, lesson: J?, onDone: () -> Unit) {
if (lesson == null) return
val spec = J(org.json.JSONObject().apply {
put("type", "choice")
put("title", vm.tr(lesson.str("exTitleKey")))
put("sub", lesson.str("quizHeadKey"))
put("questions", lesson.o.optJSONArray("quiz"))
})
DispatchExercise(vm, spec, vm.tr(lesson.str("exTitleKey")), onDone)
}
@Composable
fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) {
val lesson = when (year) {

View file

@ -306,12 +306,16 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum {
}
}
"netyears" -> {
sum.totalEx = vm.content.netLessons.size
sum.doneEx = vm.content.netLessons.count { vm.progress.netDone(it.int("id")) }
val extra = listOf("net2", "net3", "net4")
sum.totalEx = vm.content.netLessons.size + extra.sumOf { vm.content.itLessons(it).size }
sum.doneEx = vm.content.netLessons.count { vm.progress.netDone(it.int("id")) } +
extra.sumOf { course -> vm.content.itLessons(course).count { vm.progress.courseDone(course, it.int("id")) } }
}
"hwyears", "hwmap" -> {
sum.totalEx = vm.content.hw.size
sum.doneEx = vm.content.hw.count { vm.progress.hwDone(it.int("id")) }
val extra = listOf("hw2", "hw3", "hw4")
sum.totalEx = vm.content.hw.size + extra.sumOf { vm.content.itLessons(it).size }
sum.doneEx = vm.content.hw.count { vm.progress.hwDone(it.int("id")) } +
extra.sumOf { course -> vm.content.itLessons(course).count { vm.progress.courseDone(course, it.int("id")) } }
}
"enyears", "en1map", "en2map", "en3map", "en4map" -> {
sum.totalEx = (1..4).sumOf { vm.content.enYear(it).size }
@ -530,10 +534,19 @@ private fun buildSearch(vm: AppViewModel): List<Hit> {
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "netunit${l.int("id")}", extra = "site")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "netex${l.int("id")}")
}
listOf("net2", "net3", "net4").forEach { course ->
add(vm.tr("net_year${course.drop(3)}"), vm.tr("search_lesson"), "${course}map", extra = "rocnik site")
vm.content.itLessons(course).forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "${course}unit${l.int("id")}", extra = "site")
}
}
add(vm.tr("hw_year1"), vm.tr("search_lesson"), "hwmap", extra = "rocnik")
add(vm.tr("hw_year2"), vm.tr("search_lesson"), "hwyears", locked = true, extra = "rocnik")
add(vm.tr("hw_year3"), vm.tr("search_lesson"), "hwyears", locked = true, extra = "rocnik")
add(vm.tr("hw_year4"), vm.tr("search_lesson"), "hwyears", locked = true, extra = "rocnik")
listOf("hw2", "hw3", "hw4").forEach { course ->
add(vm.tr("hw_year${course.drop(2)}"), vm.tr("search_lesson"), "${course}map", extra = "rocnik hardware")
vm.content.itLessons(course).forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "${course}unit${l.int("id")}", extra = "hardware")
}
}
vm.content.hw.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "hwunit${l.int("id")}", extra = "hardware")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "hwex${l.int("id")}")
@ -718,15 +731,26 @@ fun UnitMapScreen(vm: AppViewModel, unitId: Int) {
}
}
@Composable
fun ItYearScreen(vm: AppViewModel, course: String, titleKey: String, subKey: String, lesson: (Int) -> Route) {
LessonListScreen(
vm, vm.tr(titleKey), vm.tr(subKey),
vm.content.itLessons(course).map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.courseDone(course, id), lesson(id))
},
)
}
@Composable
fun NetYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("net_years_title"), vm.tr("net_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item {
NavRow(title = vm.tr("net_year1"), subtitle = vm.tr("net_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.NetMap) }
}
items(listOf("net_year2", "net_year3", "net_year4")) { key ->
NavRow(title = vm.tr(key), subtitle = vm.tr("net_year_locked_sub"), locked = true, onClick = null)
NavRow(title = vm.tr("net_year2"), subtitle = vm.tr("net2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net2Map) }
NavRow(title = vm.tr("net_year3"), subtitle = vm.tr("net3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net3Map) }
NavRow(title = vm.tr("net_year4"), subtitle = vm.tr("net4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Net4Map) }
}
}
}
@ -738,9 +762,9 @@ fun HwYearsScreen(vm: AppViewModel) {
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item {
NavRow(title = vm.tr("hw_year1"), subtitle = vm.tr("hw_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.HwMap) }
}
items(listOf("hw_year2", "hw_year3", "hw_year4")) { key ->
NavRow(title = vm.tr(key), subtitle = vm.tr("hw_year_locked_sub"), locked = true, onClick = null)
NavRow(title = vm.tr("hw_year2"), subtitle = vm.tr("hw2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw2Map) }
NavRow(title = vm.tr("hw_year3"), subtitle = vm.tr("hw3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw3Map) }
NavRow(title = vm.tr("hw_year4"), subtitle = vm.tr("hw4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Hw4Map) }
}
}
}