2

I hope to get an object of data calss with default value which from string resource file.

The code A will not be compiled because I have not to pass a Context paramter for data class MVoice(), I don't think it's a good way.

I there a simple way to get an object of data calss with default value which from string resource file in Kotlin?

Added Content:

If I use Code B, is it a good way ?

Code A

@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
    @PrimaryKey @ColumnInfo(name = "id") var id: Long = 0,
    var name: String = getString(R.String.Name)
)


<string name="Name">Untitled</string>

Code B

@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
    @PrimaryKey @ColumnInfo(name = "id") var id: Long = 0,
    var name: String
)
{
    companion object {
        fun getDefaultMVoice(mContext: Context): MVoice {
            return MVoice(name = mContext.getString(R.string.name))
        }
    }
}
Sergio
  • 27,326
  • 8
  • 128
  • 149
HelloCW
  • 843
  • 22
  • 125
  • 310
  • Have you tried this method: https://stackoverflow.com/a/4391811/4813946 ? it's not that pretty but it should do the trick. – Zakaria Boukaddouss Aug 01 '20 at 10:08
  • 1
    Just a quick architectural note. The problem you are having here often arises from lack of separation of concerns. After all you are trying to control UI (you need to use the `Context`) in you database or model code. What is usually done in these cases, is that you let the database give you a empty signal value (e.g. `null`) and later in the UI code you can decide to replace this value with e.g. `getString(R.string.Name)`. Think of it this way: If in the next iteration the requirements change and instead of "name" there should be a "?"-icon would you really place a drawable into your model? – ymindstorm Aug 03 '20 at 14:40

5 Answers5

1

I assume you want the default value of the name variable to be displayed somewhere in your app's UI. I would create an extension function on MVoice object:

fun MVoice.nameOrDefault(ctx: Context) =
    if (name == null || name.trim().isEmpty()) {
        MVoice.defaultName(ctx)
    } else {
        name
    }

data class MVoice(...) {

    // ...

    companion object {
        @JvmStatic
        fun defaultName(ctx: Context) = ctx.getString(R.string.name)
    }
}

We can use that function to set text, for example, to TextView:

val voice: MVoice = ...
val textView: TextView = ...
textView.text = voice.nameOrDefault(context)
Sergio
  • 27,326
  • 8
  • 128
  • 149
1

You should not add logic or Android specific code to your data classes which makes unit testing impossible.

You should instead use a business logic layer(usecase/inteactor) or ViewModel that has interface and that interface should have concrete implementation that takes context that returns a default string. It's like injecting context to db if you are familiar with dagger.

You can also use AndroidViewModel class either for accessing context.

Thracian
  • 43,021
  • 16
  • 133
  • 222
0

You cannot get string resources without a context, however you can use the application context and make it static. You have to declare in manifest:

<application android:name="com.xyz.App">

</application>

And then make the application context static:

class App: Application() {
    companion object {
        lateinit var appContext: WeakReference<Context>
    }

    override fun onCreate() {
        super.onCreate()
        appContext = WeakReference(applicationContext)
    }
}

Then you can use the context everywhere you need it by calling: App.appContext.get()!!.getString(R.string.example)

Daniel
  • 2,320
  • 1
  • 14
  • 27
0

Yes, we can access resources without using Context To get a value from string resource file for data class in Kotlin:

Resources.getSystem().getString(R.string.app_name)

you can use them everywhere in your application, even in static constants declarations!

eg:

@Entity(tableName = "Support")
data class Support(
        @PrimaryKey @ColumnInfo(name = "support_id") val support_id: String,
        @ColumnInfo(name = "support_count")       val support_count: Int,
        @ColumnInfo(name = "support_visit")       val support_visit: String= Resources.getSystem().getString(R.string.app_name)
)

I just gave a solution for your answer, but I do recommended you to use direct string value.

Bolt UIX
  • 5,988
  • 6
  • 31
  • 58
  • 1
    This is incorrect as this returns a global Resources reference which does not contain any application resources, which `R.string.app_name` would be part of. See [here](https://developer.android.com/reference/android/content/res/Resources#getSystem()). – ymindstorm Aug 05 '20 at 10:09
0

I think what you're trying to do is not a good idea on it's own, so even if you can, the question is if you should. It basically means that your data class needs to know about Android resources and UI (R.string, Context), which is questionable design

I'd go with

@Entity(tableName = "voice_table", indices = [Index("createdDate")])
data class MVoice(
    @PrimaryKey @ColumnInfo(name = "id") var id: Long = 0,
    var name: String
)

as it doesn't have the default value you always have to provide it, then you just call

MVoice(requireContext().getString(R.string.mvoicedefault))
Stachu
  • 1,614
  • 1
  • 5
  • 17