MVVM + Jetpack Compose Navigation
FOCS_Book
│
└── app
├── manifests
│ └── [Link]
│
├── kotlin+java
│ └── [Link].focs_book
│ ├── data
│ │ └── ChapterRepository
│ │
│ ├── model
│ │ └── Chapter
│ │
│ ├── navigation
│ │ └── NavRoutes
│ │
│ └── ui
│ ├── screens
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ ├── [Link]
│ │ └── [Link]
│ │
│ └── theme
│
│ └── viewmodel
│ ├── Chapter
│ └── [Link]
│
├── [Link].focs_book (androidTest)
├── [Link].focs_book (test)
│
├── assets
│ ├── book_content.json (androidTest)
│ └── [Link] (androidTest)
│
├── res
└── res (generated)
│
└── Gradle Scripts
├── [Link] (Project: FOCS_Book)
├── [Link] (Module: app)
├── [Link]
├── [Link]
├── [Link]
├── [Link]
├── [Link]
└── [Link]
FOCS_Book
│
└── app
├── manifests
│ └── [Link]
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="[Link]
xmlns:tools="[Link]
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.FOCS_Book">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.FOCS_Book">
<intent-filter>
<action android:name="[Link]" />
<category android:name="[Link]"
/>
</intent-filter>
</activity>
</application>
</manifest>
│
├── kotlin+java
│ └── [Link].focs_book
│ ├── data
│ │ └── ChapterRepository
package [Link].focs_book.data
import [Link]
import [Link].focs_book.[Link]
import [Link]
import [Link]
class ChapterRepository(private val context: Context) {
/**
* Loads chapters from assets. Default filename = "[Link]".
* Returns emptyList() on error so the UI won't crash.
*/
fun loadChapters(filename: String = "[Link]"): List<Chapter> {
val jsonString = try {
[Link](filename).bufferedReader().use
{ [Link]() }
} catch (e: Exception) {
[Link]()
return emptyList()
}
return try {
// specify the target type explicitly
Json { ignoreUnknownKeys = true
}.decodeFromString<List<Chapter>>(jsonString)
} catch (e: Exception) {
[Link]()
emptyList()
}
}
}
│ │
│ ├── model
│ │ └── Chapter
package [Link].focs_book.model
import [Link]
@Serializable
data class Chapter(
val id: Int,
val title: String,
val content: String? = null
)
│ │
│ ├── navigation
│ │ └── NavRoutes
package [Link].focs_book.navigation
sealed class NavRoutes(val route: String) {
object Splash : NavRoutes("splash")
object Home : NavRoutes("home")
object Toc : NavRoutes("toc") // Table of Contents
object ChapterList : NavRoutes("chapter_list") // List of chapters
object BookContent : NavRoutes("book_content") // Reader screen
object Search : NavRoutes("search") // Search screen
object Bookmarks : NavRoutes("bookmarks") // Bookmarks & Notes
object Settings : NavRoutes("settings") // Settings screen
object About : NavRoutes("about") // About screen
object Quiz : NavRoutes("quiz") // Quiz screen
│ │
│ └── ui
│ ├── screens
│ │ ├── [Link]
package [Link].focs_book.[Link]
import [Link].*
import [Link].material3.*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
@OptIn(ExperimentalMaterial3Api::class) // ✅ allows CenterAlignedTopAppBar
@Composable
fun AboutScreen(navController: NavController) {
Scaffold(
topBar = {
CenterAlignedTopAppBar(
title = { Text("About") },
navigationIcon = {
IconButton(onClick = { [Link]() })
{
Icon(
imageVector = [Link],
contentDescription = "Back"
)
}
}
)
}
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
.padding([Link]),
contentAlignment = [Link]
) {
Column(verticalArrangement = [Link]([Link])) {
Text(
text = "FOCS Book",
style = [Link]
)
Text(
text = "A structured cybersecurity learning app for
students and professionals.",
style = [Link]
)
Text(
text = "Version 1.0.0",
style = [Link]
)
Text(
text = "Developed by Shaikh Junaid Ahmad (2025)",
style = [Link]
)
}
}
}
}
│ │ ├── [Link]
package [Link].focs_book.[Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
@Composable
fun BookmarksScreen(navController: NavController) {
val bookmarks = listOf("Bookmark 1", "Bookmark 2", "Bookmark 3")
LazyColumn(
verticalArrangement = [Link]([Link]),
modifier = Modifier
.fillMaxSize()
.padding([Link])
) {
items(bookmarks) { bookmark ->
Card(modifier = [Link]()) {
Box(modifier = [Link]([Link])) {
Text(text = bookmark, style =
[Link])
}
}
}
}
}
│ │ ├── [Link]
package [Link].focs_book.[Link]
import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
@Composable
fun HomeScreen(navController: NavController) {
Box(
modifier = Modifier
.fillMaxSize()
.padding([Link]),
contentAlignment = [Link]
) {
Column(
verticalArrangement = [Link]([Link]),
horizontalAlignment = [Link],
modifier = [Link]()
) {
HomeMenuCard("📑 Table of Contents") {
[Link]([Link])
}
HomeMenuCard("🔍 Search") {
[Link]([Link])
}
HomeMenuCard("📌 Bookmarks & Notes") {
[Link]([Link])
}
HomeMenuCard("⚙️Settings") {
[Link]([Link])
}
HomeMenuCard("ℹ️About") {
[Link]([Link])
}
}
}
}
@Composable
fun HomeMenuCard(title: String, onClick: () -> Unit) {
Card(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() },
elevation = [Link]([Link])
) {
Box(
modifier = Modifier
.padding([Link])
.fillMaxWidth(),
contentAlignment = [Link]
) {
Text(
text = title,
style = [Link]
)
}
}
}
│ │ ├── [Link]
package [Link].focs_book.[Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
@Composable
fun ReaderScreen(navController: NavController, chapterId: Int? = null) {
val context = [Link]
val repo = ChapterRepository(context)
val chapters = [Link]()
val chapter = [Link] { [Link] == chapterId } ?:
[Link]()
Column(
modifier = Modifier
.fillMaxSize()
.padding([Link])
) {
Text(text = chapter?.title ?: "Chapter", style =
[Link])
Spacer(modifier = [Link]([Link]))
Text(
text = chapter?.content ?: "No content available.",
style = [Link]
)
}
}
│ │ ├── [Link]
package [Link].focs_book.[Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]
@Composable
fun SearchScreen(navController: NavController) {
var query by remember { mutableStateOf("") }
Column(modifier = Modifier
.fillMaxSize()
.padding([Link])) {
OutlinedTextField(
value = query,
onValueChange = { query = it },
label = { Text("Search") },
modifier = [Link]()
)
Spacer(modifier = [Link]([Link]))
Text(
text = "Search results for \"$query\"",
style = [Link]
)
}
}
│ │ ├── [Link]
package [Link].focs_book.[Link]
import [Link].*
import [Link].material3.*
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
@Composable
fun SettingsScreen(
navController: NavController,
viewModel: SettingsViewModel
) {
// Collect state from ViewModel
val isDarkMode by [Link]()
val fontSize by [Link]()
Column(
modifier = Modifier
.fillMaxSize()
.padding([Link]),
verticalArrangement = [Link]([Link])
) {
Text("Settings", style = [Link])
// Dark mode toggle
Row(
modifier = [Link](),
horizontalArrangement = [Link]
) {
Text("Dark Mode", style = [Link])
Switch(
checked = isDarkMode,
onCheckedChange = { [Link](it) }
)
}
// Font size slider
Column {
Text("Font Size: ${[Link]()} sp", style =
[Link])
Slider(
value = fontSize,
onValueChange = { [Link](it) },
valueRange = 12f..24f,
steps = 6
)
}
// Reset button
Button(
onClick = { [Link]() },
modifier = [Link]()
) {
Text("Reset to Default")
}
}
}
│ │ ├── [Link]
package [Link].focs_book.[Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
import [Link]
@Composable
fun SplashScreen(navController: NavController) {
// Navigate to Home after 2 seconds
LaunchedEffect(true) {
delay(2000)
[Link]([Link]) {
popUpTo([Link]) { inclusive = true } // clear
backstack
}
}
// Centered splash content
Box(
modifier = Modifier
.fillMaxSize()
.padding([Link]),
contentAlignment = [Link]
) {
Column(horizontalAlignment = [Link]) {
// Placeholder app title (replace with Image if you add a logo)
Text(
text = "FOCS Book",
style = [Link],
fontWeight = [Link],
fontSize = [Link]
)
Spacer(modifier = [Link]([Link]))
Text(
text = "Foundation of Computer Science",
style = [Link]
)
}
}
}
│ │ └── [Link]
package [Link].focs_book.[Link]
import [Link]
import [Link].*
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
import [Link].focs_book.[Link]
import [Link].focs_book.[Link]
@Composable
fun TocScreen(navController: NavController) {
val context = [Link]
val repository = ChapterRepository(context)
val chapters: List<Chapter> = [Link]() // loads
[Link]
LazyColumn(
verticalArrangement = [Link]([Link]),
modifier = Modifier
.fillMaxSize()
.padding([Link])
) {
items(chapters) { chapter ->
Card(
modifier = Modifier
.fillMaxWidth()
.clickable {
// Navigate to ReaderScreen with chapter ID
[Link]("$
{[Link]}/${[Link]}")
},
elevation = [Link]([Link])
) {
Box(modifier = [Link]([Link])) {
Text(text = [Link], style =
[Link])
}
}
}
}
}
│ │
│ └── theme
│
│ └── viewmodel
│ ├── [Link]
[Link]
package [Link].focs_book.viewmodel
class ChapterViewModel {
}
│ ├──[Link]
package [Link].focs_book.viewmodel
import [Link]
import [Link]
import [Link]
class SettingsViewModel : ViewModel() {
private val _isDarkMode = MutableStateFlow(false)
val isDarkMode = _isDarkMode.asStateFlow()
private val _fontSize = MutableStateFlow(16f)
val fontSize = _fontSize.asStateFlow()
fun setDarkMode(enabled: Boolean) {
_isDarkMode.value = enabled
// Save to DataStore/SharedPreferences if needed
}
fun setFontSize(size: Float) {
_fontSize.value = size
}
fun resetSettings() {
_isDarkMode.value = false
_fontSize.value = 16f
}
}
│ └── [Link]
package [Link].focs_book
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link]
import [Link].focs_book.[Link]
import [Link].focs_book.[Link].*
import [Link].focs_book.[Link].FOCS_BookTheme
import [Link].focs_book.[Link]
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
[Link](savedInstanceState)
setContent {
FOCS_BookTheme {
BookApp()
}
}
}
}
@Composable
fun BookApp() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = [Link]
) {
composable([Link]) { SplashScreen(navController) }
composable([Link]) { HomeScreen(navController) }
composable([Link]) { TocScreen(navController) }
composable(route = "${[Link]}/{chapterId}") {
backStackEntry ->
val chapterId =
[Link]?.getString("chapterId")?.toIntOrNull()
ReaderScreen(navController = navController, chapterId =
chapterId)
}
composable([Link]) {
ReaderScreen(navController = navController) }
composable([Link]) { SearchScreen(navController) }
composable([Link]) {
BookmarksScreen(navController) }
composable([Link]) {
val settingsViewModel: SettingsViewModel = viewModel()
SettingsScreen(navController = navController, viewModel =
settingsViewModel)
}
composable([Link]) { AboutScreen(navController) }
composable([Link]) { QuizScreen(navController) }
}
}
@Composable
fun QuizScreen(navController: [Link]) {
Text(text = "Quiz Screen")
}
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
FOCS_BookTheme {
BookApp()
}
}
│
├── [Link].focs_book (androidTest)
├── [Link].focs_book (test)
│
├── assets
│ ├── book_content.json (androidTest)
{
"section": "I Introduction to Cyber Security",
"chapters": [
{
"title": "Defining Cyberspace",
"page": 4,
"content": "Cyberspace refers to..."
},
{
"title": "Architecture of Cyberspace",
"page": 8,
"content": "The architecture of cyberspace consists of..."
}
]
}
│ └── [Link] (androidTest)
[
{ "id": 1, "title": "Unit 1: Introduction to FOCS" },
{ "id": 2, "title": "Unit 2: Algorithms and Complexity" },
{ "id": 3, "title": "Unit 3: Automata Theory" },
{ "id": 4, "title": "Unit 4: Computability" }
]
│
├── res
└── res (generated)
│
└── Gradle Scripts
├── [Link] (Project: FOCS_Book)
├── [Link] (Module: app)
plugins {
alias([Link])
alias([Link])
alias([Link])
id("[Link]") version "1.9.23" // use
your Kotlin version
android {
namespace = "[Link].focs_book"
compileSdk = 36
defaultConfig {
applicationId = "[Link].focs_book"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner =
"[Link]"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("[Link]"),
"[Link]"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
compose = true
}
}
dependencies {
implementation([Link])
implementation([Link])
implementation([Link])
implementation(platform([Link]))
implementation([Link])
implementation([Link])
implementation([Link])
implementation([Link].material3)
implementation("[Link]:kotlinx-serialization-
json:1.9.0")
// ✅ Add navigation
implementation([Link])
testImplementation([Link])
androidTestImplementation([Link])
androidTestImplementation([Link])
androidTestImplementation(platform([Link]))
androidTestImplementation([Link].junit4)
debugImplementation([Link])
debugImplementation([Link])
}
├── [Link]
├── [Link]
├── [Link]
├── [Link]
├── [Link]
└── [Link]
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
[Link](RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
[Link] = "FOCS_Book"
include(":app")
RoadMap for this application
🔹 1. Define App Purpose & Audience • Goal: Provide students, learners, and professionals
easy access to structured cybersecurity content. • Audience: Beginners to intermediate
learners. • Core Features: o Table of contents navigation 📑 o Search within topics 🔍 o
Bookmarks & notes 📌 o Dark mode 🌙 o Offline access 📴
________________________________________
🔹 2. Choose Tech Stack • Language: Kotlin (modern, Google recommended) • UI
Framework: Jetpack Compose (or XML if you prefer classic layouts) • Architecture: MVVM
(Model-View-ViewModel) for scalability • Storage: o Use Room Database or JSON assets to
store book chapters
o SharedPreferences for bookmarks & settings
________________________________________
🔹 3. Design App Structure a) Screens 1. Splash Screen – app logo + book title 2. Home
Screen – shows main sections (I, II, III, IV) 3. Table of Contents Screen – expandable list of
chapters 4. Chapter Reader Screen – display text content with scroll, zoom, and search 5.
Search Screen – search terms across chapters 6. Bookmarks & Notes Screen 7. Settings
Screen – dark mode, font size, reset data ________________________________________ b)
Navigation • Use Navigation Component for easy navigation between screens. • TOC →
Subsections → Chapter → Reader Screen. ________________________________________
🔹 4. Content Management • Store your book content in: o assets JSON/XML files
(lightweight, easy to load offline) o Example: • { • "section": "I Introduction to Cyber
Security", • "chapters": [ • { • "title": "Defining Cyberspace", • "page": 4, • "content":
"Cyberspace refers to..." • }, • { • "title": "Architecture of Cyberspace", • "page": 4, •
"content": "The architecture of cyberspace consists of..." • } • ] • } • This allows dynamic
rendering without hardcoding. ________________________________________
🔹 5. UI/UX Features • Expandable TOC list (Accordion style) • Reader-friendly UI:
adjustable font, themes • Highlighting & bookmarking (save last-read position) • Case Study
Section: formatted nicely with examples • Search bar: full-text search inside chapters
________________________________________ 🔹 6. Security Features (since it’s a
Cybersecurity book ⚡) • Protect content from easy copy-paste (optional). • Provide
Quiz/MCQ module later for learning engagement. • Offline mode (no data leak via internet).
________________________________________
🔹 7. Development Phases Phase 1 (MVP – 3 weeks) • Setup project, splash screen, navigation
• Implement Table of Contents + Reader screen • Load chapters from JSON Phase 2
(Enhancements – 3 weeks) • Add bookmarks, notes, and search • Add dark mode + font size
customization • Optimize for offline access Phase 3 (Advanced – optional) • Add quizzes /
practice tests • Integrate voice reading (Text-to-Speech) • Cloud sync (Firebase) for multi-
device ________________________________________
🔹 8. Deployment • Test thoroughly on emulators + your real phone (Infinix). • Generate
Signed APK or App Bundle. • Publish on Google Play Store (if public) or distribute APK
privately. ________________________________________
🔹 9. Tools Needed • Android Studio 2025.1.3.7 (already installed 🎉) • Gradle (comes with
Studio) • JSON editor (for managing book content) • GitHub (optional for version control)
________________________________________ ✅ This roadmap will give you a
professional book reader app, but tailored for cybersecurity education. 👉 Next step: I can
design a sample folder structure + UI wireframe for your project.