Skip to content

Commit 9d4f50f

Browse files
authored
tutorial UI changes (#617)
* - tutorial UI changes * - formatting fixes * - Test fixes * - test fixes 1
1 parent d06436e commit 9d4f50f

12 files changed

Lines changed: 132 additions & 102 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ android {
167167
}
168168

169169
namespace = "be.scri"
170+
ndkVersion = "27.1.12297006"
170171

171172
applicationVariants.all {
172173
val variantName = this.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
@@ -333,6 +334,7 @@ dependencies {
333334
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
334335
implementation("com.charleskorn.kaml:kaml:0.57.0")
335336
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7")
337+
336338
}
337339

338340
tasks.register<Copy>("moveFromi18n") {

app/src/androidTestKeyboards/kotlin/be/scri/ui/screens/about/AboutUtilInstrumentedTest.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ class AboutUtilInstrumentedTest {
167167
fun testGetFeedbackAndSupportList() {
168168
println("Testing getFeedbackAndSupportList...")
169169

170+
var tutorialClicked = false
170171
var rateClicked = false
171172
var mailClicked = false
172173
var resetHintsClicked = false
@@ -177,6 +178,7 @@ class AboutUtilInstrumentedTest {
177178
CompositionLocalProvider(LocalContext provides context) {
178179
val feedbackList =
179180
AboutUtil.getFeedbackAndSupportList(
181+
onTutorialClick = { tutorialClicked = true },
180182
onRateScribeClick = { rateClicked = true },
181183
onMailClick = { mailClicked = true },
182184
onResetHintsClick = { resetHintsClicked = true },
@@ -191,7 +193,7 @@ class AboutUtilInstrumentedTest {
191193

192194
// Test list is not empty.
193195
assertThat(extractedItems).isNotEmpty()
194-
assertThat(extractedItems).hasSize(5)
196+
assertThat(extractedItems).hasSize(6) // Fixed: size is now 6
195197

196198
// Test each item has required fields.
197199
extractedItems.forEach { item ->
@@ -204,25 +206,30 @@ class AboutUtilInstrumentedTest {
204206
assertThat(linkItem.onClick).isNotNull()
205207
}
206208

207-
// Test specific items.
208-
val rateItem = extractedItems[0] as ScribeItem.ExternalLinkItem
209+
// Test specific items
210+
val tutorialItem = extractedItems[0] as ScribeItem.ExternalLinkItem
211+
assertThat(tutorialItem.title).isEqualTo(R.string.i18n_app_installation_button_quick_tutorial)
212+
213+
val rateItem = extractedItems[1] as ScribeItem.ExternalLinkItem
209214
assertThat(rateItem.leadingIcon).isEqualTo(R.drawable.star)
210215
assertThat(rateItem.title).isEqualTo(R.string.i18n_app_about_feedback_rate_scribe)
211216

212-
val mailItem = extractedItems[2] as ScribeItem.ExternalLinkItem
217+
val mailItem = extractedItems[3] as ScribeItem.ExternalLinkItem
213218
assertThat(mailItem.leadingIcon).isEqualTo(R.drawable.mail_icon)
214219
assertThat(mailItem.title).isEqualTo(R.string.i18n_app_about_feedback_send_email)
215220

216-
val hintsItem = extractedItems[4] as ScribeItem.ExternalLinkItem
221+
val hintsItem = extractedItems[5] as ScribeItem.ExternalLinkItem
217222
assertThat(hintsItem.leadingIcon).isEqualTo(R.drawable.light_bulb_icon)
218223
assertThat(hintsItem.title).isEqualTo(R.string.i18n_app_about_feedback_reset_app_hints)
219224

220225
// Test onClick callbacks OUTSIDE of setContent
226+
tutorialItem.onClick()
221227
rateItem.onClick()
222228
mailItem.onClick()
223229
hintsItem.onClick()
224230

225231
// Verify callbacks were triggered.
232+
assertThat(tutorialClicked).isTrue()
226233
assertThat(rateClicked).isTrue()
227234
assertThat(mailClicked).isTrue()
228235
assertThat(resetHintsClicked).isTrue()

app/src/main/java/be/scri/App.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import be.scri.ui.screens.download.DataDownloadViewModel
4949
import be.scri.ui.screens.download.DownloadActions
5050
import be.scri.ui.screens.download.DownloadDataScreen
5151
import be.scri.ui.screens.settings.SettingsScreen
52+
import be.scri.ui.screens.tutorial.TutorialNavigator
5253
import be.scri.ui.theme.ScribeTheme
5354
import kotlinx.coroutines.CoroutineScope
5455
import kotlinx.coroutines.launch
@@ -173,6 +174,17 @@ fun ScribeApp(
173174
onNavigateToDownloadData = {
174175
navController.navigate("download_data")
175176
},
177+
onTutorialClick = {
178+
navController.navigate("tutorial")
179+
180+
coroutineScope.launch {
181+
kotlinx.coroutines.delay(400)
182+
val aboutIndex = screens.indexOfFirst { it is BottomBarScreen.About }
183+
if (aboutIndex != -1) {
184+
pagerState.scrollToPage(aboutIndex)
185+
}
186+
}
187+
},
176188
)
177189
HintDialog(
178190
pagerState = pagerState,
@@ -245,6 +257,9 @@ fun ScribeApp(
245257
onWikiClick = {
246258
navController.navigate(Screen.WikimediaScribe.route)
247259
},
260+
onTutorialClick = {
261+
navController.navigate("tutorial")
262+
},
248263
resetHints = { resetHints() },
249264
context = context,
250265
)
@@ -264,6 +279,15 @@ fun ScribeApp(
264279
}
265280
}
266281

282+
composable("tutorial") {
283+
TutorialNavigator(
284+
onTutorialExit = {
285+
navController.popBackStack()
286+
},
287+
modifier = Modifier.padding(innerPadding),
288+
)
289+
}
290+
267291
composable("download_data") {
268292
DownloadDataScreen(
269293
onBackNavigation = {

app/src/main/java/be/scri/ui/screens/InstallationScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ fun InstallationScreen(
6464
isDark: Boolean,
6565
context: Context,
6666
onNavigateToDownloadData: () -> Unit,
67+
onTutorialClick: () -> Unit,
6768
modifier: Modifier = Modifier,
6869
) {
6970
var showTutorial by remember { mutableStateOf(false) }
@@ -305,7 +306,7 @@ fun InstallationScreen(
305306

306307
OutlinedButton(
307308
onClick = {
308-
showTutorial = true
309+
onTutorialClick()
309310
},
310311
modifier =
311312
Modifier

app/src/main/java/be/scri/ui/screens/about/AboutScreen.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ fun AboutScreen(
3434
onPrivacyPolicyClick: () -> Unit,
3535
onThirdPartyLicensesClick: () -> Unit,
3636
onWikiClick: () -> Unit,
37+
onTutorialClick: () -> Unit,
3738
resetHints: () -> Unit,
3839
context: Context,
3940
modifier: Modifier = Modifier,
@@ -60,6 +61,7 @@ fun AboutScreen(
6061
resetHints()
6162
},
6263
context = context,
64+
onTutorialClick = onTutorialClick,
6365
isConjugateApp = isConjugateApp,
6466
)
6567

app/src/main/java/be/scri/ui/screens/about/AboutUtil.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,17 @@ fun feedbackAndSupportList(
118118
onRateScribeClick: () -> Unit,
119119
onMailClick: () -> Unit,
120120
onResetHintsClick: () -> Unit,
121+
onTutorialClick: () -> Unit,
121122
isConjugateApp: Boolean = false,
122123
): List<ScribeItem.ExternalLinkItem> =
123124
listOf(
125+
ScribeItem.ExternalLinkItem(
126+
leadingIcon = R.drawable.tutorial,
127+
title = R.string.i18n_app_installation_button_quick_tutorial,
128+
trailingIcon = R.drawable.right_arrow,
129+
url = null,
130+
onClick = { onTutorialClick() },
131+
),
124132
ScribeItem.ExternalLinkItem(
125133
leadingIcon = R.drawable.star,
126134
title =
@@ -276,6 +284,7 @@ object AboutUtil {
276284
onRateScribeClick: () -> Unit,
277285
onMailClick: () -> Unit,
278286
onResetHintsClick: () -> Unit,
287+
onTutorialClick: () -> Unit,
279288
context: Context,
280289
isConjugateApp: Boolean = false,
281290
): ScribeItemList =
@@ -287,6 +296,7 @@ object AboutUtil {
287296
onRateScribeClick,
288297
onMailClick,
289298
onResetHintsClick,
299+
onTutorialClick,
290300
isConjugateApp,
291301
),
292302
)

app/src/main/java/be/scri/ui/screens/tutorial/TutorialHomeScreen.kt

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ fun TutorialHomeScreen(
5757
val textColor = MaterialTheme.colorScheme.onSurface
5858
val secondaryTextColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
5959
val dividerColor = MaterialTheme.colorScheme.outlineVariant
60-
60+
val headerColor = MaterialTheme.colorScheme.onBackground
6161
val chapters =
6262
listOf(
6363
TutorialChapter("Noun annotation", 0),
@@ -81,41 +81,24 @@ fun TutorialHomeScreen(
8181
Icon(
8282
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowLeft,
8383
contentDescription = "Back",
84-
tint = MaterialTheme.colorScheme.primary,
84+
tint = MaterialTheme.colorScheme.onBackground,
8585
modifier = Modifier.size(24.dp),
8686
)
8787
Text(
88-
text = "Home",
89-
color = MaterialTheme.colorScheme.primary,
88+
text = "About",
89+
color = MaterialTheme.colorScheme.onBackground,
9090
fontSize = 16.sp,
9191
)
9292
}
93-
9493
Spacer(modifier = Modifier.height(24.dp))
9594

96-
// Info banner
97-
Card(
98-
shape = RoundedCornerShape(12.dp),
99-
colors = CardDefaults.cardColors(containerColor = cardBackground),
100-
modifier = Modifier.fillMaxWidth(),
101-
) {
102-
Row(
103-
modifier = Modifier.padding(16.dp),
104-
verticalAlignment = Alignment.CenterVertically,
105-
) {
106-
Text(
107-
text = "\uD83D\uDCA1",
108-
fontSize = 20.sp,
109-
modifier = Modifier.padding(end = 12.dp),
110-
)
111-
Text(
112-
text = "Make sure you select the desired Scribe keyboard by pressing \uD83C\uDF10 when typing.",
113-
color = textColor,
114-
fontSize = 14.sp,
115-
modifier = Modifier.weight(1f),
116-
)
117-
}
118-
}
95+
Text(
96+
text = "Quick tutorial",
97+
color = headerColor,
98+
fontSize = 28.sp,
99+
fontWeight = FontWeight.Bold,
100+
modifier = Modifier.padding(horizontal = 8.dp),
101+
)
119102

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

@@ -126,7 +109,9 @@ fun TutorialHomeScreen(
126109
modifier = Modifier.fillMaxWidth(),
127110
) {
128111
Text(
129-
text = "This quick tutorial will show you how to use Scribe to support writing in your second language.",
112+
text =
113+
"This quick tutorial will show you how to use Scribe to support writing in your second language. " +
114+
"\nMake sure you select the desired Scribe keyboard by pressing \uD83C\uDF10 when typing.",
130115
color = textColor,
131116
fontSize = 14.sp,
132117
modifier = Modifier.padding(16.dp),

app/src/main/java/be/scri/ui/screens/tutorial/TutorialNavigator.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import androidx.compose.runtime.mutableIntStateOf
99
import androidx.compose.runtime.mutableStateOf
1010
import androidx.compose.runtime.remember
1111
import androidx.compose.runtime.setValue
12+
import androidx.compose.ui.Modifier
1213

1314
/**
1415
* The main tutorial navigation controller.
@@ -18,7 +19,10 @@ import androidx.compose.runtime.setValue
1819
* @param onTutorialExit Callback when the user exits the tutorial (back to About tab).
1920
*/
2021
@Composable
21-
fun TutorialNavigator(onTutorialExit: () -> Unit) {
22+
fun TutorialNavigator(
23+
onTutorialExit: () -> Unit,
24+
modifier: Modifier = Modifier,
25+
) {
2226
var currentScreen by remember { mutableStateOf("home") }
2327
var currentChapterIndex by remember { mutableIntStateOf(0) }
2428
var currentStepIndex by remember { mutableIntStateOf(0) }
@@ -49,6 +53,7 @@ fun TutorialNavigator(onTutorialExit: () -> Unit) {
4953
when (currentScreen) {
5054
"home" -> {
5155
TutorialHomeScreen(
56+
modifier = modifier,
5257
onBackPress = onTutorialExit,
5358
onChapterSelect = { chapterIndex ->
5459
currentChapterIndex = chapterIndex
@@ -73,10 +78,10 @@ fun TutorialNavigator(onTutorialExit: () -> Unit) {
7378
val isLastStep = isLastStepInChapter && (isLastChapter || !isFullTutorial)
7479

7580
TutorialStepScreen(
81+
modifier = modifier,
7682
chapterTitle = chapterTitle,
7783
step = step,
7884
isLastStep = isLastStep,
79-
showQuickTutorialHeader = !isFullTutorial && currentStepIndex == 0,
8085
onBackPress = {
8186
when {
8287
currentStepIndex > 0 -> {
@@ -92,9 +97,6 @@ fun TutorialNavigator(onTutorialExit: () -> Unit) {
9297
}
9398
}
9499
},
95-
onClosePress = {
96-
currentScreen = "home"
97-
},
98100
onNextPress = {
99101
when {
100102
!isLastStepInChapter -> {

0 commit comments

Comments
 (0)