3

My goal is to add a default value to constructor of the Room @Entity. The default value must depend on the language settings of the user. The way suggested by android framework is to use resource strings.

Here's the code I have:

@Entity
data class ArmyEntity(
    @PrimaryKey(autoGenerate = true)
    val armyId: Long,
    val userOwnerId: Long,
    val name: String = R.string.untitled, // wrong type
    val description: String = R.string.no_description_yet, // wrong type
    val iconUri: String = "",
    val lastEdit: Timestamp = Timestamp(System.currentTimeMillis())
)

The two lines which interest me are labelled with the "wrong type" comments. Calling R.string.resource_string_name returns resource id, rather than the content of resource (returns Int, not String).

Android documentation suggests this way to get the string:

val string: String = getString(R.string.hello)

But the issue is that the getString() function is a member of the Context class and can be used in Activity. But Room Entity annotated doesn't know about context. (Context page for reference)

I tried passing context as a constructor parameter, but unfortunately, every constructor parameter in the data class has to be val or var. As long as I know, Room creates a column for every field in the class. What should I do to provide a language-dependent default value? Thank you!

Andrey Kachow
  • 936
  • 7
  • 22

2 Answers2

2

FOR LOCAL RESOURCES

Define context in your Application class

class MyApplication : Application() {

    companion object {
        lateinit var context: Context
            private set
    }

    override fun onCreate() {
        super.onCreate()
        context = this
    }
}

and then just use MyApplication.context where you need

import com.example.myapp.R
import com.example.myapp.MyApplication

@Entity
data class ArmyEntity(
    @PrimaryKey(autoGenerate = true)
    val armyId: Long,
    val userOwnerId: Long,
    val name: String = MyApplication.context.getString(R.string.untitled),
    val description: String = MyApplication.context.getString(R.string.no_description_yet)
    val iconUri: String = "",
    val lastEdit: Timestamp = Timestamp(System.currentTimeMillis())
)

NOTE : Android studio will warn you about a memory leak. Check this question for more informations

FOR SYSTEM RESOURCES

You can import Resources to access an application's resources with Resources.getSystem(), and the R class.

import android.content.res.Resources
import com.example.yourapp.R

@Entity
data class ArmyEntity(
    @PrimaryKey(autoGenerate = true)
    val armyId: Long,
    val userOwnerId: Long,
    val name: String = Resources.getSystem().getString(R.string.untitled),
    val description: String = Resources.getSystem().getString(R.string.no_description_yet)
    val iconUri: String = "",
    val lastEdit: Timestamp = Timestamp(System.currentTimeMillis())
)
Stefano Sansone
  • 2,377
  • 7
  • 20
  • 39
  • This will only provide access to system resources, please see the docs for `getSystem()` – mightyWOZ Jul 23 '21 at 07:35
  • Thanks for help. Unfortunately I get `android.content.res.Resources$NotFoundException` when constructor is called – Andrey Kachow Jul 23 '21 at 07:50
  • @AndruhaVUho I just updated my answer with a solution for local resources, let me know if it works in your case – Stefano Sansone Jul 23 '21 at 08:41
  • 1
    @StefanoSansone, that worked, but Android Studio complains that setting `Context` fields in the compaction objects is a memory leak. I adapted your idea to use `companion object` to just store strings. I accept your answer, but could you edit it and let others know about the possibility of a memory leak. Thanks! – Andrey Kachow Jul 23 '21 at 09:06
  • This solution can cause issues, please see [this](https://stackoverflow.com/questions/4208886/using-the-android-application-class-to-persist-data) question and second highest voted answer, following is a comment from that – mightyWOZ Jul 23 '21 at 13:36
  • 1
    This was an unplesant surprise for us in production. Believe me Android kills processes, it just depends on RAM state and other factors described in the documentation. It was a nightmare for us so I just share my real experience. Well we did not have this on emulators, but in real world some devices are 'overloaded' with apps, so killing a background process is a normal situation. Yes, if user then decides to get the app up into foreground - the OS restores its stack including the Application instance, however there will not be your static data you count on unless you persisted it. – mightyWOZ Jul 23 '21 at 13:38
1

In my opinion you should use the id of that String. Change your DB texture a bit to follow this.

  @Entity
data class ArmyEntity(
    @PrimaryKey(autoGenerate = true)
    val armyId: Long,
    val userOwnerId: Long,
    val descriptionRes: Int = R.string.abc, // this is true for multi-languages
    val iconUri: String = "",
)

fun getDescription(context: Context,armyEntity : ArmyEntity,textView: TextView){
    textView.text = context.getString(armyEntity.descriptionRes)
}
Đốc.tc
  • 530
  • 4
  • 12
  • Thanks for the help! I really appreciate it. But in my application the user will override the text of the description after he creates "empty" army. Is it possible to keep db storing the text, not an int? – Andrey Kachow Jul 23 '21 at 07:15