Android Kaki

Build beautiful, usable products using required Components for Android.

Constructing language studying apps with Compose — Half 5

Victor Brandalise

Welcome to half 5 of the “Constructing language studying apps with Compose” collection. This can be a collection during which I’ll share my strategy of constructing a language studying app.

On this collection, I will share my progress, design selections, and any new insights I gained alongside the way in which. Immediately I’ll cowl the 18–20 days.

In case you have not learn the opposite articles, you will discover them right here:

If you wish to know precisely what code adjustments I made, you’ll be able to click on [Code Diff] hyperlink subsequent to the date title and see all adjustments for that date.

On this article, I’ll speak about a brand new coaching mode I added in addition to in regards to the preliminary migration to make use of Room because the database.

Along with the two decks I have already got, I created 2 new decks so there are various phrases to overview.

After I first wrote CheckPracticeAnswerUseCaseImpl, I am checking if the distinction is lower than 4 characters. That wasn’t very environment friendly so I modified it to twenty% of the proper reply size. In a phrase with 5 characters, only one character could be turned off.

Partly 1, I confirmed you a prototype of the app that features the display under. That is one other method to apply. As a substitute of coming into a solution, the person should select the proper reply.

This can be a helpful operate so I made a decision to implement it. The present code solely helps the case the place the person enters one thing, for the brand new case I modified the code in order that it’s extensible and likewise permits different circumstances sooner or later.

I wasn’t positive how to do that, so I made a decision to make one thing work first after which refactor it. The implementation I will present under is a bit messy however that is okay as a result of on the nineteenth I went via and refactored this code.

That is the way in which PracticeSession it appears, it simply returns an inventory of tags to be thought-about. I needed to change it to incorporate the tag overview as effectively.

knowledge class PracticeSession(
val id: String,
val title: String,
val playing cards: Listing

To do this I created PracticeType the interface, the implementation defines how the tag must be considered and likewise extra info e.g. I would like an inventory of choices to show when a multi-option case is proven to the person. All kinds have a DeckCard as a result of that is what is used to test the reply.

sealed interface PracticeType {
val card: DeckCard
knowledge class TypeAnswer(override val card: DeckCard) : PracticeType
knowledge class MultipleOption(override val card: DeckCard, val choices: Listing) : PracticeType


Then I needed to change PracticeSessionCreator as a result of i’ve modified PracticeSession class.

I put MULTIPLE_OPTION_PROBABILITY to 30, which suggests there is a 30% probability a card shall be thought-about utilizing the a number of choice class. I solely proceed if the cardboard output is only one phrase. i’m inventive validOptions accommodates solely doable solutions that don’t embrace the proper reply. Then I simply created CardPractice.MultipleTextOptions use 3 doable solutions + appropriate reply.

if (Random.nextInt(1..100) <= MULTIPLE_OPTION_PROBABILITY) {
if (card.outputs.none { it.accommodates(" ") }) {
val validOptions = sortedCards
.minus(card) // take away the reply presumably from choices
.filter { it.outputs.none { output -> output.accommodates(" ") } }
if (validOptions.measurement >= MULTIPLE_OPTION_COUNT - 1) {
return CardPractice.MultipleTextOptions(
card = card,
choices = validOptions.shuffled()
.map { it.outputs.random() }
.plus(card.outputs.random()) // add reply to choices

Here is how the multi-option kind ends on the primary day:

On the nineteenth, I did not add any new options. I simply refactored the code that I wrote on the 18th.

i modified my title PracticeType ARRIVE CardPractice. I modified my title too TypeAnswer ARRIVE InputField And MultipleOption ARRIVE MultipleTextOptions. I believe this makes the code simpler to know.

I additionally made a small change to the cardboard to make it look slightly higher.

I need to begin utilizing this app to study languages ​​nevertheless it’s troublesome proper now as a result of it simply shops the whole lot in reminiscence. To have the ability to use the appliance, we have to host the whole lot domestically or create a backend service.

I selected so as to add an area database as a result of I will be utilizing it for caching later anyway. I am utilizing Room as database.

The unique database appears to be like like this:

entities = [
], model = 1
summary class AppDatabase : RoomDatabase() {
summary enjoyable deckDao(): DeckDao
summary enjoyable deckCardDao(): DeckCardDao

Let me clarify the structure that I’m utilizing for this a part of the challenge.

Initially, all of the code associated to Room is within the class that implements the repository. I do not need to leak it into different lessons of the challenge. The good thing about that is that I can migrate my challenge from utilizing an in-memory listing to a database simply by altering the info layer. No want to alter any use circumstances or view fashions due to that. If sooner or later I select to make use of a backend service, I’ll solely have to alter the info layer. That is the great thing about abstracting your lessons.

Contained in the repository deployment package deal there are 3 different packages:

  • knowledge that is the place i outline database entities. Instance I created DeckData And DackCardDatasome customers Entity sufix as a substitute nevertheless it’s the identical factor.
  • mapper that is the place mappers stay. It maps from database entity to area entity and from area entity to database entity. That is achieved in order that the database layers don’t leak into different layers of the appliance.
  • dao that is the place the DAO is applied. it is class Room used to outline a database desk.

Including all these lessons positively will increase the complexity of the challenge however for me it is a worthwhile trade-off.

Please use DeckCard eg. First I create a Dao for it.

interface DeckCardDao {
@Question("SELECT * FROM deck_card WHERE id = :id LIMIT 1")
droop enjoyable getById(id: String): DeckCardData?
@Question("SELECT * FROM deck_card WHERE deck_id = :id")
droop enjoyable getByDeckId(id: String): Listing

droop enjoyable insert(deckCardData: DeckCardData)

I then created its entity. I put a overseas key on its deck in order that it’s deleted in case the deck is deleted.

tableName = "deck_card",
foreignKeys = [
parentColumns = ["id"],
childColumns = ["deck_id"],
onDelete = ForeignKey.CASCADE
knowledge class DeckCardData(
@PrimaryKey val id: String,
@ColumnInfo(title = "deck_id") val deckId: String,
@ColumnInfo(title = "enter") val enter: String?

You could discover that the outputs are lacking and that is as a result of the Room would not permit us to save lots of the listing. I’ll come again to that partially 6.

I ended up making a map for it.

class DeckCardDataMapper @Inject constructor() {
enjoyable toData(card: DeckCard): DeckCardData {
return DeckCardData(
id =,
deckId = card.deckId,
enter = card.enter

enjoyable fromData(card: DeckCardData): DeckCard? {
return DeckCard(
id =,
deckId = card.deckId,
enter = card.enter ?: return null,
outputs = emptyList() // <--- empty listing for now

I did related issues for different entities, you’ll be able to test different code if you wish to see.

John Wick: Chapter 4 (FREE) FULLMOVIE The Super Mario Bros Movie avatar 2 Where To Watch Creed 3 Free At Home Knock at the Cabin (2023) FullMovie Where To Watch Ant-Man 3 and the Wasp: Quantumania Cocaine Bear 2023 (FullMovie) Scream 6 Full Movie
Updated: May 4, 2023 — 6:17 pm

Leave a Reply

Your email address will not be published. Required fields are marked * © 2023 Android kaki