Add physics lessons across four secondary-school years.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
pepaskopec-blip 2026-10-06 10:55:27 +02:00
commit 524269edd7
32 changed files with 4152 additions and 13 deletions

View file

@ -176,6 +176,26 @@ class AppViewModel(app: Application) : AndroidViewModel(app) {
refresh()
}
fun markFyz(id: Int) {
progress.markFyz(id)
refresh()
}
fun markFyz2(id: Int) {
progress.markFyz2(id)
refresh()
}
fun markFyz3(id: Int) {
progress.markFyz3(id)
refresh()
}
fun markFyz4(id: Int) {
progress.markFyz4(id)
refresh()
}
fun markMluv(n: Int) {
progress.markMluv(n)
refresh()

View file

@ -32,6 +32,8 @@ import org.maturita.maturita.ui.HomeScreen
import org.maturita.maturita.ui.HwQuizScreen
import org.maturita.maturita.ui.HwYearsScreen
import org.maturita.maturita.ui.LessonListScreen
import org.maturita.maturita.ui.FyzQuizScreen
import org.maturita.maturita.ui.FyzYearsScreen
import org.maturita.maturita.ui.LitYearsScreen
import org.maturita.maturita.ui.LessonRow
import org.maturita.maturita.ui.LitQuizScreen
@ -348,6 +350,63 @@ private fun MaturitaAppBody(vm: AppViewModel) {
}
}
is Route.Lit4Ex -> LitQuizScreen(vm, 4, r.id)
Route.FyzYears -> FyzYearsScreen(vm)
Route.FyzMap -> LessonListScreen(
vm, vm.tr("fyz_year1"), vm.tr("fyz_sub"),
vm.content.fyz.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.fyzDone(id), Route.FyzLesson(id))
},
)
is Route.FyzLesson -> {
val l = vm.content.fyzLesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.FyzEx(r.id))
}
}
is Route.FyzEx -> FyzQuizScreen(vm, 1, r.id)
Route.Fyz2Map -> LessonListScreen(
vm, vm.tr("fyz_year2"), vm.tr("fyz2_sub"),
vm.content.fyz2.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.fyz2Done(id), Route.Fyz2Lesson(id))
},
)
is Route.Fyz2Lesson -> {
val l = vm.content.fyz2Lesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Fyz2Ex(r.id))
}
}
is Route.Fyz2Ex -> FyzQuizScreen(vm, 2, r.id)
Route.Fyz3Map -> LessonListScreen(
vm, vm.tr("fyz_year3"), vm.tr("fyz3_sub"),
vm.content.fyz3.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.fyz3Done(id), Route.Fyz3Lesson(id))
},
)
is Route.Fyz3Lesson -> {
val l = vm.content.fyz3Lesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Fyz3Ex(r.id))
}
}
is Route.Fyz3Ex -> FyzQuizScreen(vm, 3, r.id)
Route.Fyz4Map -> LessonListScreen(
vm, vm.tr("fyz_year4"), vm.tr("fyz4_sub"),
vm.content.fyz4.map { l ->
val id = l.int("id")
LessonRow(vm.tr(l.str("titleKey")), vm.progress.fyz4Done(id), Route.Fyz4Lesson(id))
},
)
is Route.Fyz4Lesson -> {
val l = vm.content.fyz4Lesson(r.id)
if (l != null) SlidesScreen(vm, vm.tr(l.str("titleKey")), vm.tr(l.str("subKey")), l.arr("slides")) {
vm.replace(Route.Fyz4Ex(r.id))
}
}
is Route.Fyz4Ex -> FyzQuizScreen(vm, 4, r.id)
Route.SciMap -> SciMapScreen(vm)
Route.ChemMap -> LessonListScreen(
vm, vm.tr("Chemie"), vm.tr("chem_sub"),

View file

@ -50,6 +50,19 @@ sealed class Route(val page: String) {
data object BioMap : Route("biomap")
data class BioLesson(val id: Int) : Route("biounit$id")
data class BioEx(val id: Int) : Route("bioex$id")
data object FyzYears : Route("fyzyears")
data object FyzMap : Route("fyzmap")
data class FyzLesson(val id: Int) : Route("fyzunit$id")
data class FyzEx(val id: Int) : Route("fyzex$id")
data object Fyz2Map : Route("fyz2map")
data class Fyz2Lesson(val id: Int) : Route("fyz2unit$id")
data class Fyz2Ex(val id: Int) : Route("fyz2ex$id")
data object Fyz3Map : Route("fyz3map")
data class Fyz3Lesson(val id: Int) : Route("fyz3unit$id")
data class Fyz3Ex(val id: Int) : Route("fyz3ex$id")
data object Fyz4Map : Route("fyz4map")
data class Fyz4Lesson(val id: Int) : Route("fyz4unit$id")
data class Fyz4Ex(val id: Int) : Route("fyz4ex$id")
data object CzechMap : Route("czechmap")
data object Mluvnice : Route("mluvnice")
data class MluvEx(val n: Int) : Route("mluve$n")
@ -68,7 +81,9 @@ sealed class Route(val page: String) {
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 ||
this is ChemLesson || this is ChemEx || this is BioLesson || this is BioEx
this is ChemLesson || this is ChemEx || this is BioLesson || this is BioEx ||
this is FyzLesson || this is FyzEx || this is Fyz2Lesson || this is Fyz2Ex ||
this is Fyz3Lesson || this is Fyz3Ex || this is Fyz4Lesson || this is Fyz4Ex
/** Full stack from the practice root to this screen, so back always goes up one level. */
fun stack(): List<Route> = when (this) {
@ -120,6 +135,19 @@ sealed class Route(val page: String) {
BioMap -> listOf(Subjects, SciMap, BioMap)
is BioLesson -> listOf(Subjects, SciMap, BioMap, this)
is BioEx -> listOf(Subjects, SciMap, BioMap, BioLesson(id), this)
FyzYears -> listOf(Subjects, FyzYears)
FyzMap -> listOf(Subjects, FyzYears, FyzMap)
is FyzLesson -> listOf(Subjects, FyzYears, FyzMap, this)
is FyzEx -> listOf(Subjects, FyzYears, FyzMap, FyzLesson(id), this)
Fyz2Map -> listOf(Subjects, FyzYears, Fyz2Map)
is Fyz2Lesson -> listOf(Subjects, FyzYears, Fyz2Map, this)
is Fyz2Ex -> listOf(Subjects, FyzYears, Fyz2Map, Fyz2Lesson(id), this)
Fyz3Map -> listOf(Subjects, FyzYears, Fyz3Map)
is Fyz3Lesson -> listOf(Subjects, FyzYears, Fyz3Map, this)
is Fyz3Ex -> listOf(Subjects, FyzYears, Fyz3Map, Fyz3Lesson(id), this)
Fyz4Map -> listOf(Subjects, FyzYears, Fyz4Map)
is Fyz4Lesson -> listOf(Subjects, FyzYears, Fyz4Map, this)
is Fyz4Ex -> listOf(Subjects, FyzYears, Fyz4Map, Fyz4Lesson(id), this)
CzechMap -> listOf(Subjects, CzechMap)
Mluvnice -> listOf(Subjects, CzechMap, Mluvnice)
is MluvEx -> listOf(Subjects, CzechMap, Mluvnice, this)
@ -153,6 +181,11 @@ fun routeFromPage(page: String): Route? = when (page) {
"scimap" -> Route.SciMap
"chemmap" -> Route.ChemMap
"biomap" -> Route.BioMap
"fyzyears" -> Route.FyzYears
"fyzmap" -> Route.FyzMap
"fyz2map" -> Route.Fyz2Map
"fyz3map" -> Route.Fyz3Map
"fyz4map" -> Route.Fyz4Map
"czechmap" -> Route.CzechMap
"mluvnice" -> Route.Mluvnice
"readinglist" -> Route.ReadingList
@ -196,6 +229,30 @@ fun routeFromPage(page: String): Route? = when (page) {
Regex("""bioex(\d+)""").matchEntire(page)?.let {
return Route.BioEx(it.groupValues[1].toInt())
}
Regex("""fyz4unit(\d+)""").matchEntire(page)?.let {
return Route.Fyz4Lesson(it.groupValues[1].toInt())
}
Regex("""fyz4ex(\d+)""").matchEntire(page)?.let {
return Route.Fyz4Ex(it.groupValues[1].toInt())
}
Regex("""fyz3unit(\d+)""").matchEntire(page)?.let {
return Route.Fyz3Lesson(it.groupValues[1].toInt())
}
Regex("""fyz3ex(\d+)""").matchEntire(page)?.let {
return Route.Fyz3Ex(it.groupValues[1].toInt())
}
Regex("""fyz2unit(\d+)""").matchEntire(page)?.let {
return Route.Fyz2Lesson(it.groupValues[1].toInt())
}
Regex("""fyz2ex(\d+)""").matchEntire(page)?.let {
return Route.Fyz2Ex(it.groupValues[1].toInt())
}
Regex("""fyzunit(\d+)""").matchEntire(page)?.let {
return Route.FyzLesson(it.groupValues[1].toInt())
}
Regex("""fyzex(\d+)""").matchEntire(page)?.let {
return Route.FyzEx(it.groupValues[1].toInt())
}
Regex("""lit4unit(\d+)""").matchEntire(page)?.let {
return Route.Lit4Lesson(it.groupValues[1].toInt())
}

View file

@ -39,6 +39,10 @@ class Content(root: JSONObject) {
val lit4 = raw.arr("lit4")
val chem = raw.arr("chem")
val bio = raw.arr("bio")
val fyz = raw.arr("fyz")
val fyz2 = raw.arr("fyz2")
val fyz3 = raw.arr("fyz3")
val fyz4 = raw.arr("fyz4")
val mluvnice = raw.arr("mluvnice")
val books = raw.arr("books")
val changelog = raw.arr("changelog")
@ -56,6 +60,10 @@ class Content(root: JSONObject) {
fun lit4Lesson(id: Int) = lit4.firstOrNull { it.int("id") == id }
fun chemLesson(id: Int) = chem.firstOrNull { it.int("id") == id }
fun bioLesson(id: Int) = bio.firstOrNull { it.int("id") == id }
fun fyzLesson(id: Int) = fyz.firstOrNull { it.int("id") == id }
fun fyz2Lesson(id: Int) = fyz2.firstOrNull { it.int("id") == id }
fun fyz3Lesson(id: Int) = fyz3.firstOrNull { it.int("id") == id }
fun fyz4Lesson(id: Int) = fyz4.firstOrNull { it.int("id") == id }
fun mluv(n: Int) = mluvnice.firstOrNull { it.int("id") == n }
fun book(id: String) = books.firstOrNull { it.str("id") == id }

View file

@ -57,6 +57,14 @@ class ProgressStore(context: Context) {
fun markChem(id: Int) { prefs.edit().putBoolean("chem.$id", true).apply() }
fun bioDone(id: Int) = prefs.getBoolean("bio.$id", false)
fun markBio(id: Int) { prefs.edit().putBoolean("bio.$id", true).apply() }
fun fyzDone(id: Int) = prefs.getBoolean("fyz.$id", false)
fun markFyz(id: Int) { prefs.edit().putBoolean("fyz.$id", true).apply() }
fun fyz2Done(id: Int) = prefs.getBoolean("fyz2.$id", false)
fun markFyz2(id: Int) { prefs.edit().putBoolean("fyz2.$id", true).apply() }
fun fyz3Done(id: Int) = prefs.getBoolean("fyz3.$id", false)
fun markFyz3(id: Int) { prefs.edit().putBoolean("fyz3.$id", true).apply() }
fun fyz4Done(id: Int) = prefs.getBoolean("fyz4.$id", false)
fun markFyz4(id: Int) { prefs.edit().putBoolean("fyz4.$id", true).apply() }
fun mluvDone(n: Int) = prefs.getBoolean("mluv.$n", false)
fun markMluv(n: Int) { prefs.edit().putBoolean("mluv.$n", true).apply() }
@ -176,6 +184,38 @@ fun summarize(content: Content, p: ProgressStore): ProgressSum {
sum.doneUnits += 1
}
}
content.fyz.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.fyzDone(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
content.fyz2.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.fyz2Done(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
content.fyz3.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.fyz3Done(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
content.fyz4.forEach { l ->
sum.totalEx += 1
sum.openUnits += 1
if (p.fyz4Done(l.int("id"))) {
sum.doneEx += 1
sum.doneUnits += 1
}
}
sum.totalEx += content.mluvnice.size
sum.openUnits += 1
val md = content.mluvnice.count { p.mluvDone(it.int("id")) }

View file

@ -863,6 +863,30 @@ fun SciQuizScreen(vm: AppViewModel, area: String, id: Int) {
}
}
@Composable
fun FyzQuizScreen(vm: AppViewModel, year: Int, id: Int) {
val lesson = when (year) {
2 -> vm.content.fyz2Lesson(id)
3 -> vm.content.fyz3Lesson(id)
4 -> vm.content.fyz4Lesson(id)
else -> vm.content.fyzLesson(id)
} ?: 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"))) {
when (year) {
2 -> vm.markFyz2(id)
3 -> vm.markFyz3(id)
4 -> vm.markFyz4(id)
else -> vm.markFyz(id)
}
}
}
@Composable
fun LitQuizScreen(vm: AppViewModel, year: Int, id: Int) {
val lesson = when (year) {

View file

@ -25,6 +25,7 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.FormatListNumbered
@ -222,6 +223,7 @@ private fun SubjectIcon(icon: String, open: Boolean) {
"people" -> Icon(Icons.Filled.Groups, null, tint = tint, modifier = Modifier.size(26.dp))
"cz" -> CzechFlag(28.dp)
"flask" -> Icon(Icons.Filled.Science, null, tint = tint, modifier = Modifier.size(26.dp))
"bolt" -> Icon(Icons.Filled.Bolt, null, tint = tint, modifier = Modifier.size(26.dp))
else -> Icon(Icons.Filled.Lock, null, tint = tint)
}
}
@ -314,6 +316,13 @@ private fun subjectSum(vm: AppViewModel, s: J): ProgressSum {
sum.doneEx = vm.content.chem.count { vm.progress.chemDone(it.int("id")) } +
vm.content.bio.count { vm.progress.bioDone(it.int("id")) }
}
"fyzyears", "fyzmap", "fyz2map", "fyz3map", "fyz4map" -> {
sum.totalEx = vm.content.fyz.size + vm.content.fyz2.size + vm.content.fyz3.size + vm.content.fyz4.size
sum.doneEx = vm.content.fyz.count { vm.progress.fyzDone(it.int("id")) } +
vm.content.fyz2.count { vm.progress.fyz2Done(it.int("id")) } +
vm.content.fyz3.count { vm.progress.fyz3Done(it.int("id")) } +
vm.content.fyz4.count { vm.progress.fyz4Done(it.int("id")) }
}
"czechmap" -> {
val litDone = vm.content.lit.count { vm.progress.litDone(it.int("id")) } +
vm.content.lit2.count { vm.progress.lit2Done(it.int("id")) } +
@ -547,6 +556,27 @@ private fun buildSearch(vm: AppViewModel): List<Hit> {
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "lit4unit${l.int("id")}", extra = "literatura")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "lit4ex${l.int("id")}")
}
add(vm.tr("Fyzika"), vm.tr("search_lesson"), "fyzyears", extra = "fyzika")
add(vm.tr("fyz_year1"), vm.tr("search_lesson"), "fyzmap", extra = "rocnik")
add(vm.tr("fyz_year2"), vm.tr("search_lesson"), "fyz2map", extra = "rocnik")
add(vm.tr("fyz_year3"), vm.tr("search_lesson"), "fyz3map", extra = "rocnik")
add(vm.tr("fyz_year4"), vm.tr("search_lesson"), "fyz4map", extra = "rocnik")
vm.content.fyz.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyzunit${l.int("id")}", extra = "fyzika")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyzex${l.int("id")}")
}
vm.content.fyz2.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz2unit${l.int("id")}", extra = "fyzika")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz2ex${l.int("id")}")
}
vm.content.fyz3.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz3unit${l.int("id")}", extra = "fyzika")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz3ex${l.int("id")}")
}
vm.content.fyz4.forEach { l ->
add(vm.tr(l.str("titleKey")), vm.tr("search_lesson"), "fyz4unit${l.int("id")}", extra = "fyzika")
add(vm.tr(l.str("titleKey")), vm.tr("search_exercise"), "fyz4ex${l.int("id")}")
}
add(vm.tr("Chemie"), vm.tr("search_lesson"), "chemmap", extra = "chemie")
add(vm.tr("Biologie"), vm.tr("search_lesson"), "biomap", extra = "biologie")
vm.content.chem.forEach { l ->
@ -683,6 +713,20 @@ fun LitYearsScreen(vm: AppViewModel) {
}
}
@Composable
fun FyzYearsScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Fyzika"), vm.tr("fyz_years_sub")) { inner ->
LazyColumn(contentPadding = PaddingValues(top = inner.calculateTopPadding(), bottom = inner.calculateBottomPadding() + 16.dp)) {
item {
NavRow(title = vm.tr("fyz_year1"), subtitle = vm.tr("fyz_sub"), leading = { Text("1.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.FyzMap) }
NavRow(title = vm.tr("fyz_year2"), subtitle = vm.tr("fyz2_sub"), leading = { Text("2.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz2Map) }
NavRow(title = vm.tr("fyz_year3"), subtitle = vm.tr("fyz3_sub"), leading = { Text("3.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz3Map) }
NavRow(title = vm.tr("fyz_year4"), subtitle = vm.tr("fyz4_sub"), leading = { Text("4.", style = MaterialTheme.typography.titleMedium) }) { vm.go(Route.Fyz4Map) }
}
}
}
}
@Composable
fun SciMapScreen(vm: AppViewModel) {
DetailScaffold(vm, vm.tr("Základy Přírodopisných věd"), vm.tr("sci_sub")) { inner ->