Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions app/src/main/java/be/scri/ui/screens/InstallationScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
Expand All @@ -49,6 +53,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import be.scri.R
import be.scri.ui.common.ScribeBaseScreen
import be.scri.ui.screens.tutorial.TutorialNavigator

/**
* The installation page of the application with details for installing Scribe keyboards and downloading data.
Expand All @@ -61,6 +66,15 @@ fun InstallationScreen(
onNavigateToDownloadData: () -> Unit,
modifier: Modifier = Modifier,
) {
var showTutorial by remember { mutableStateOf(false) }

if (showTutorial) {
TutorialNavigator(
onTutorialExit = { showTutorial = false },
)
return
}

val layoutDirection = LocalLayoutDirection.current
val localConfiguration = LocalConfiguration.current
val resource: Int =
Expand Down Expand Up @@ -291,6 +305,7 @@ fun InstallationScreen(

OutlinedButton(
onClick = {
showTutorial = true
},
modifier =
Modifier
Expand Down
14 changes: 14 additions & 0 deletions app/src/main/java/be/scri/ui/screens/tutorial/TutorialChapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package be.scri.ui.screens.tutorial

/**
* Represents a single tutorial chapter in the home screen.
*
* @property title The display name of the chapter.
* @property chapterIndex The index used to navigate to this chapter.
*/
data class TutorialChapter(
val title: String,
val chapterIndex: Int,
)
86 changes: 86 additions & 0 deletions app/src/main/java/be/scri/ui/screens/tutorial/TutorialContent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package be.scri.ui.screens.tutorial

/**
* Defines all tutorial chapters and their steps.
* Each chapter contains one or more interactive steps that guide the user
* through a specific Scribe feature.
*/
object TutorialContent {
/**
* Chapter 1: Noun Annotation.
* Teaches users about gender tags that appear when typing nouns.
*/
val nounAnnotationSteps =
listOf(
TutorialStep(
instruction =
"Write the word \"Vater\". Notice the word suggestions " +
"that appear on the keyboard's top bar.\n\n" +
"Then, press space. You will see the word's gender " +
"tag on the keyboard's top bar \u2013 in this case, \"M\" for Maskulin.",
expectedWord = "Vater",
),
TutorialStep(
instruction =
"Now write the word \"Mutter\" and then press space. " +
"The gender tag will be \"F\", for Feminin.",
expectedWord = "Mutter",
),
)

/**
* Chapter 2: Word Translation.
* Teaches users how to use the Translate command via the Scribe key.
*/
val wordTranslationSteps =
listOf(
TutorialStep(
instruction =
"Let's translate! Tap the \u27A1 Scribe key on the top-left " +
"corner of your keyboard, and select \u00DCbersetzen.\n\n" +
"Then write the word you want to translate, press \u25B6, " +
"and the translation will be returned to you.",
hint = "If your second language is not German, change the language in your keyboard.",
requiresValidation = false,
),
)

val verbConjugationSteps =
listOf(
TutorialStep(
instruction =
"On to the verbs. Tap the \u27A1 Scribe key on the top-left " +
"corner of your keyboard, and select Konjugieren.\n\n" +
"Write the verb you want to conjugate, press \u25B6, and " +
"you will see a table with all the verb tenses. Select " +
"the one you need and it will be inserted!",
hint = "If your second language is not German, change the language in your keyboard.",
requiresValidation = false,
),
)

val nounPluralsSteps =
listOf(
TutorialStep(
instruction =
"Finding the plural of a noun with Scribe is easy. Tap " +
"the \u27A1 Scribe key on the top-left corner of your " +
"keyboard, and select Plural.\n\n" +
"Then write the noun you want the plural for, press " +
"\u25B6, and the plural will be returned to you.",
hint = "If your second language is not German, change the language in your keyboard.",
requiresValidation = false,
),
)

/** Returns all chapters as a list of pairs (title, steps). */
fun getAllChapters(): List<Pair<String, List<TutorialStep>>> =
listOf(
"Noun annotation" to nounAnnotationSteps,
"Word translation" to wordTranslationSteps,
"Verb conjugation" to verbConjugationSteps,
"Noun plurals" to nounPluralsSteps,
)
}
211 changes: 211 additions & 0 deletions app/src/main/java/be/scri/ui/screens/tutorial/TutorialHomeScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// SPDX-License-Identifier: GPL-3.0-or-later

package be.scri.ui.screens.tutorial

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

/**
* The tutorial home screen (Screen 0.0 from Figma).
* Displays a list of tutorial chapters and a button to start the full tutorial.
* This screen is accessible from the About tab.
*
* @param onBackPress Callback when the back button is pressed.
* @param onChapterSelect Callback when a specific chapter is tapped.
* @param onStartFullTutorial Callback when the "Start full tutorial" button is pressed.
* @param modifier Modifier for this composable.
*/
@Composable
fun TutorialHomeScreen(
onBackPress: () -> Unit,
onChapterSelect: (Int) -> Unit,
onStartFullTutorial: () -> Unit,
modifier: Modifier = Modifier,
) {
val isDarkTheme = isSystemInDarkTheme()
val backgroundColor = MaterialTheme.colorScheme.background
val cardBackground = MaterialTheme.colorScheme.surface
val textColor = MaterialTheme.colorScheme.onSurface
val secondaryTextColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
val dividerColor = MaterialTheme.colorScheme.outlineVariant

val chapters =
listOf(
TutorialChapter("Noun annotation", 0),
TutorialChapter("Word translation", 1),
TutorialChapter("Verb conjugation", 2),
TutorialChapter("Noun plurals", 3),
)

Column(
modifier =
modifier
.fillMaxSize()
.background(backgroundColor)
.padding(16.dp),
) {
// Back button
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.clickable { onBackPress() },
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft,
contentDescription = "Back",
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(24.dp),
)
Text(
text = "Home",
color = MaterialTheme.colorScheme.primary,
fontSize = 16.sp,
)
}

Spacer(modifier = Modifier.height(24.dp))

// Info banner
Card(
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = cardBackground),
modifier = Modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "\uD83D\uDCA1",
fontSize = 20.sp,
modifier = Modifier.padding(end = 12.dp),
)
Text(
text = "Make sure you select the desired Scribe keyboard by pressing \uD83C\uDF10 when typing.",
color = textColor,
fontSize = 14.sp,
modifier = Modifier.weight(1f),
)
}
}

Spacer(modifier = Modifier.height(16.dp))

// Intro text
Card(
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = cardBackground),
modifier = Modifier.fillMaxWidth(),
) {
Text(
text = "This quick tutorial will show you how to use Scribe to support writing in your second language.",
color = textColor,
fontSize = 14.sp,
modifier = Modifier.padding(16.dp),
)
}

Spacer(modifier = Modifier.height(24.dp))

// Tutorial chapters header
Text(
text = "Tutorial chapters",
color = textColor,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
)

Spacer(modifier = Modifier.height(12.dp))

// Chapter list
Card(
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = cardBackground),
modifier = Modifier.fillMaxWidth(),
) {
Column {
chapters.forEachIndexed { index, chapter ->
Row(
modifier =
Modifier
.fillMaxWidth()
.clickable { onChapterSelect(chapter.chapterIndex) }
.padding(horizontal = 16.dp, vertical = 14.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = chapter.title,
color = textColor,
fontSize = 16.sp,
)
Icon(
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
contentDescription = "Go to ${chapter.title}",
tint = secondaryTextColor,
)
}
if (index < chapters.size - 1) {
HorizontalDivider(
color = dividerColor,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
}
}
}

Spacer(modifier = Modifier.weight(1f))

// Start full tutorial button
Button(
onClick = onStartFullTutorial,
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = if (isDarkTheme) Color.White else Color.Black,
),
shape = RoundedCornerShape(12.dp),
modifier =
Modifier
.fillMaxWidth()
.height(52.dp),
) {
Text(
text = "Start full tutorial",
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
)
}

Spacer(modifier = Modifier.height(16.dp))
}
}
Loading