Serialize class with abstract parameter to JSON - android

I have 3 classes:
GameResultList which is basically ArrayList with some helper methods in it
GameResult with an abstract value gameMode
GameMode
public class GameResultList extends ArrayList<GameResult> {
...
}
class GameResult(
val gameMode: GameMode,
val score: Int,
timeSpentInSeconds: Int,
val completionDateTime: Date
) {
...
}
GameMode class:
abstract class GameMode(
val suggestionsActivated: Boolean,
val screenOrientation: ScreenOrientation // enum: PORTRAIT, HORIZONTAL
) {
...
}
I need to serialize GameResultList into JSON.
Since the parameter gameMode is abstract, Gson throws an exception. After some research, I decided to give Moshi a try. I have added PolymorphicJsonAdapterFactory and KotlinJsonAdapterFactory, but the result is always empty ({}).
How I set up Moshi:
private val moshi =
Moshi.Builder().add(PolymorphicJsonAdapterFactory.of(GameMode::class.java, "GameMode")
.withSubtype(GameOnTime::class.java, "GameOnTime")
.withSubtype(GameOnCount::class.java, "GameOnCount"))
.add(KotlinJsonAdapterFactory())
.build()
private val jsonAdapter: JsonAdapter<GameResultList> = moshi.adapter(GameResultList::class.java)
This returns empty JSON response:
jsonAdapter.toJson(gameResultList)
So how can I serialize the GameResultList? Is there an easy way? Also, it's not necessary to use Moshi, it can be anything else for the sake of easiness.

After some investigation, I found out that the main problem is that array lists require explicit converters.
class GameResultListToJsonAdapter {
#ToJson
fun arrayListToJson(list: GameResultList): List<GameResult> = list
#FromJson
fun arrayListFromJson(list: List<GameResult>): GameResultList = GameResultList(list)
}
Also, there is a problem with handling the Date type, I have replaced it with Long to not make another explicit converter.

Related

How to resolve "Entities and POJOs must have a usable public constructor"

I have seen this question several times on SO. however the solution doesn't seem to apply to my problem.
I have a Kotlin data-class that is used as an Entity in Room
#Entity(tableName = "training_session")
data class SessionEntity(
#PrimaryKey(autoGenerate = false) val id: Long,
#ColumnInfo(name = "current_state_marker") val currentState: Short,
#Embedded val states: List<Int>
)
It is producing
> Task :training-infrastructure:kaptDebugKotlin FAILED
error: Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type). - java.util.List
error: Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type). - java.util.List
In the same project I have a very similar entity which also has a list and that doesn't produce any errors.
Tried out the answer provided by MikeT, for me it required a small change in the way the converters were defined
data class SessionStateList (val stateList : List<Int>)
class SessionStateListConverter {
#TypeConverter
fun fromArraySessionStateList(sh: List<Int>?): String? {
return Gson().toJson(sh)
}
#TypeConverter
fun toArraySessionStateList(sh: String?): List<Int>? {
val listType: Type = object : TypeToken<ArrayList<Int?>?>() {}.type
return Gson().fromJson(sh,listType)
}
}
A quick follow-up. I had mentioned that I have another Entity that has an Embedded val something: List<Int> and I had not noticed any compiler errors.
The reason, I had not noticed any compiler errors was because the entity was not included in the #Database annotation.
You cannot have a List/Array etc as a column type. So your issue is centred on #Embedded val states: List<Int>
You could have a POJO e.g. StatesHolder :-
data class StatesHolder(
val stateList: List<Int>
)
and then have
#Entity(tableName = "training_session")
data class SessionEntity(
#PrimaryKey(autoGenerate = false) val id: Long,
#ColumnInfo(name = "current_state_marker") val currentState: Short,
val states: StatesHolder
)
Note that you cannot Embed StatesHolder as then that just inserts List. If you want to Embed then you have to Embed a wrapper that uses a StatesHolder.
You will then need TypeConverters to convert to and from a StatesHolder object to a type that can be stored. Probably a String and Probably a JSON respresentation of the StatesHold object e.g.
class Converters {
#TypeConverter
fun fromStatesHolder(sh: StatesHolder): String {
return Gson().toJson(sh)
}
#TypeConverter
fun toStatesHolder(sh: String): StatesHolder {
return Gson().fromJson(sh,StatesHolder::class.java)
}
}
You additionally need to use #TypeConverters annotation that defines the Converts::class. If coded at the #Database level the converters have full scope.
So after #Database(.....) you could have :-
#TypeConverters(Converters::class)

Gson unbable to invoke no-args construktor for observer ( LiveData) in my Android & Kotlin project

I already read a lot of stackoverflow articles and other blog posts about it trying out different solutions for the similar (but not same) problems they had there.
I will structure this post as follows:
My problem
My code (the part I think is relevant)
What I tried to fix it
1. My Problem
I'm getting the following error message:
Process: com.myapp, PID: 23553
java.lang.RuntimeException: Unable to invoke no-args constructor for androidx.arch.core.internal.SafeIterableMap$SupportRemove<androidx.lifecycle.Observer<? super java.lang.Integer>, androidx.lifecycle.LiveData$ObserverWrapper>. Registering an InstanceCreator with Gson for this type may fix this problem.
at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:228)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:186)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.Gson.fromJson(Gson.java:932)
at com.google.gson.Gson.fromJson(Gson.java:897)
at com.google.gson.Gson.fromJson(Gson.java:846)
at com.myapp.ui.customGame.CustomGameViewModel.initialize(CustomGameViewModel.kt:46)
Description of what I am trying to achieve:
I'm writing an app as a project for learning programming in Kotlin and making apps in general, I'm relatively new to making apps and Kotlin, bear that in mind if you see me making stupid mistakes, please ;).
In my app, I have an activity that contains a fragment that lets you choose the settings for a game of Volleyball (the CustomGameSetupFragment). The settings include simple things like the final score a team wins at, the names etc. After the settings are chosen and saved, the app creates an object of the Game class with the settings applied, saves them to a Room database. An entity in the table of my database basically contains an ID, some other information, and a JSON string of the game object (created via Google's Gson package). The activity then replaces the fragment with the fragment that lets you count the score of the game and see the names and stuff (the CustomGameFragment). The new fragment creates a ViewModel object which then again reads the games from the database, picks the last saved entities and then tries to recreate the game object from the JSON string saved.
This is done by executing:
val gameType = object: TypeToken<Game>() {}.type
this.game = Gson().fromJson<Game>(
gameRepo.ongoingGameData[0].gameJson,
gameType
//Game::class.java // this was an other method I tried. Also didnt work
)
Before, the Game class contained no LiveData/MutableLiveData but that resulted in the necessity to cast the attributes into LiveData/MutableLiveData in the ViewModel class and that resulted in a lot of bugs. But it worked!. I refactored the Game class so it mostly LiveData/MutableLiveData attributes (the ones I need to be LiveData), since in the CustomGameFragment and its ViewModel that would allow me to simply observe the attributes of the game directly. But after I refactored the class, Gson is not able to load it anymore.
I'm not sure it's simply because I use LiveData, and they somehow need the context or viewLifeCylceOwner that they get implicitly in the ViewModel or something.
2. My Code
a) The Room database (with Repository, Entity, Dao, Database)
Entity:
package com.myapp.data
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
#Entity(tableName = "gameData_table")
data class GameData(
#PrimaryKey(autoGenerate = true) val id: Int?,
#ColumnInfo(name = "gid") val gid: String?,
#ColumnInfo(name = "is_over") val isOver : Boolean?,
#ColumnInfo(name = "team1_name") val team1Name: String?,
#ColumnInfo(name = "team2_name") val team2Name: String?,
#ColumnInfo(name = "team1_sets") val team1Sets: Int?,
#ColumnInfo(name = "team2_sets") val team2Sets: Int?,
#ColumnInfo(name = "total_sets") val totalSets: Int?,
#ColumnInfo(name = "game_json") val gameJson : String?
)
The Dao:
package com.myapp.data
import androidx.room.*
#Dao
interface GameDao {
#Query("SELECT * FROM gameData_table")
suspend fun getAll(): List<GameData>
#Query("SELECT * FROM gameData_table WHERE id = (:id)")
fun loadAllByIds(id: Array<Int>): List<GameData>
#Query("SELECT * FROM gameData_table WHERE is_over = 0")
suspend fun getOngoing() : List<GameData>
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(vararg game: GameData)
#Delete
suspend fun delete(game: GameData)
#Query("DELETE FROM gameData_table WHERE is_over = 0")
suspend fun deleteOngoing()
#Query("UPDATE gameData_table SET game_json = (:json) WHERE gid = (:gameId)")
suspend fun updateJSON(json: String, gameId : String)
}
The Database:
package com.myapp.data
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
#Database(entities = [GameData::class], version = 1, exportSchema = false)
abstract class GameDatabase : RoomDatabase() {
abstract fun gameDao() : GameDao
companion object {
//Singleton pattern to prevent multiple instances of the database
#Volatile
private var INSTANCE: GameDatabase? = null
fun getDatabase(context: Context) : GameDatabase {
return INSTANCE ?: synchronized(this){
val instance = Room.databaseBuilder(
context.applicationContext,
GameDatabase::class.java,
"game_database"
).build()
INSTANCE = instance
return instance
}
}
}
The Repository:
package com.myapp.data
import kotlinx.coroutines.runBlocking
class GameRepository (private val gameDao: GameDao){
val allGameData: List<GameData> = runBlocking { gameDao.getAll()}
val ongoingGameData : List<GameData> = runBlocking { gameDao.getOngoing() }
suspend fun insert(gameData : GameData){
gameDao.insertAll(gameData)
}
suspend fun deleteOngoing() {
gameDao.deleteOngoing()
}
suspend fun updateGame(gameData : GameData){
gameDao.updateJSON(gameData.gameJson!!, gameData.gid!!)
}
}
b) The Game class
And now a very short version of the game, since most of the methods are not really relevant for my problem I think:
package com.myapp.game
import android.app.Application
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.myapp.data.GameData
import com.myapp.values.Values
import com.google.gson.Gson
class Game {
/*
No live data needed or possible?
*/
private var sets : MutableList<Set>
private val pointGap : Int = Values.DEFAULT_POINT_GAP
private val gid : String = this.toString()
/*
Live data needed or possible
*/
// private MutableLiveData
private var _team1Name : MutableLiveData<String> = MutableLiveData(Values.DEFAULT_TEAM1_NAME)
(more strings ...)
private var _setWinScore : MutableLiveData<Int> = MutableLiveData(Values.DEFAULT_WIN_SCORE)
(...)
// public LiveData
val team1Name : LiveData<String>
(more strings ...)
val setWinScore : LiveData<Int>
(...)
init{
team1Name = _team1Name
(more strings ...)
setWinScore = _setWinScore
(...)
}
constructor(gameSettings: GameSettings = GameSettings()){
this._team1Name.value = gameSettings.team1Name
(more strings...)
this._setWinScore.value = gameSettings.setWinScore
(...)
}
}
3. Approaches to fix it#
I tried to use a InstanceCreator. But after I read some stuff about it, I found that this is neccessary if the object you want to recreate has an argument of something the Gson class needs to know to put it in (context for example). I don't have that, I think (?).
I tried it anyway, which of course didn't work.
Also I tried several variations of using TypeToken which I also have shown at the beginning.
Another thing I read often, was to use the newest version of the package Gson, Room and LiveData or to use kapt instad of implement keywords in the grandle.build at project level.
I tried both -> same Exception
So, do you have any ideas?
Or am I doing something stupidly wrong?
Thanks in advance for sacrificing your time!
PS: I'm not an English native speaker, so sorry for bad grammar and spelling.
The following shows how to deserialize LiveData, however maybe in your use case it would be more appropriate to share the Game data as ViewModel? See Android Developers page.
When no custom or built-in type adapter matches, Gson uses a reflection-based one. The problem is that you are asking Gson to deserialize JSON as LiveData. If you look at the source code of LiveData you will see that is has multiple private fields and for the type of one of them Gson cannot create instances.
In general it is discouraged to use Gson's reflection-based serialization or deserialization for any third party classes (here LiveData) because you then rely on their internal implementation details which could change at any point.
This can be solved by creating a custom TypeAdapterFactory.
I am not familiar with Kotlin, but hopefully the following Java code is useful for you as example nonetheless:
class LiveDataTypeAdapterFactory implements TypeAdapterFactory {
public static final LiveDataTypeAdapterFactory INSTANCE = new LiveDataTypeAdapterFactory();
private LiveDataTypeAdapterFactory() { }
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<?> rawType = type.getRawType();
// Only handle LiveData and MutableLiveData
if (rawType != LiveData.class && rawType != MutableLiveData.class) {
return null;
}
// Assumes that LiveData is never used as raw type but is always parameterized
Type valueType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
// Get the adapter for the LiveData value type `T`
// Cast TypeAdapter to simplify adapter code below
#SuppressWarnings("unchecked")
TypeAdapter<Object> valueAdapter = (TypeAdapter<Object>) gson.getAdapter(TypeToken.get(valueType));
// Is safe due to `type` check at start of method
#SuppressWarnings("unchecked")
TypeAdapter<T> adapter = (TypeAdapter<T>) new TypeAdapter<LiveData<?>>() {
#Override
public void write(JsonWriter out, LiveData<?> liveData) throws IOException {
// Directly write out LiveData value
valueAdapter.write(out, liveData.getValue());
}
#Override
public LiveData<?> read(JsonReader in) throws IOException {
Object value = valueAdapter.read(in);
return new MutableLiveData<>(value);
}
};
return adapter;
}
}
(Note that this does not retain the observers of the LiveData)
You can then create the Gson instance using a GsonBuilder and register the factory:
Gson gson = new GsonBuilder()
.registerTypeAdapterFactory(LiveDataTypeAdapterFactory.INSTANCE)
.create();
There is no need to use TypeToken when deserializing Game, using the class directly will work as well. TypeToken is intended for generic types.
Ideally you would also create a TypeAdapterFactory for MutableList to not rely on its internal implementation.

Pair with generic type in Kotlin

I want to create a Pair which should take a generic type. I mean, I can pass a String to Int. How can I achieve that?
Example with the current behavior:
val liveData = MutableLiveData<Pair<Boolean, Int>>()
Expectation:
val liveData = MutableLiveData<Pair<T, T>>()
try this
class Abc<T, U> {
val liveData = MutableLiveData<Pair<T, U>>()
}
fun <T, U> Abc1(): MutableLiveData<Pair<T, U>> {
return MutableLiveData<Pair<T, U>>()
}
val liveData = Abc<String, Int>()
If you want to pass either String or Int, a sealed class may be the right choice, rather than a generic.
In your case something like:
sealed class StrInt
data class Numeric(val value:Int):StrInt()
data class Alphanum(val value:String):StrInt()
val a:Pair<StrInt, StrInt> = Numeric(10) to Alphanum("qwerty")

Passing information between Moshi custom type adaptors

I am using Moshi to deserialize json from our server but I have come across an issue I’m sure has a solution, I just can’t see it. Over the socket, we are send json that, at the top level, has three fields:
{
"data_type": "<actual_data_type>",
"data_id": "<actual_data_id>",
"data": <data_object>
}
The issue is that the data can actually be several different objects based on what data_type is can I’m not sure how to pass that information into the adaptor for Data. I’ve tried a couple different things, but it just gets closer and closer to me parsing the whole thing myself, which seems to defeat the point. Is there a way to pass information from one adaptor to another?
For anyone who wants to do something similar, I took the basic shape of a generic factory from here: https://github.com/square/moshi/pull/264/files (which also what #eric cochran is recommending in his comment) and made it more specific to fit my exact case.
class EventResponseAdapterFactory : JsonAdapter.Factory {
private val labelKey = "data_type"
private val subtypeToLabel = hashMapOf<String, Class<out BaseData>>(
DataType.CURRENT_POWER.toString() to CurrentPower::class.java,
DataType.DEVICE_STATUS_CHANGED.toString() to DeviceStatus::class.java,
DataType.EPISODE_EVENT.toString() to EpisodeEvent::class.java,
DataType.APPLIANCE_INSTANCE_UPDATED.toString() to ApplianceInstanceUpdated::class.java,
DataType.RECURRING_PATTERNS.toString() to RecurringPatternOccurrence::class.java,
DataType.RECURRING_PATTERN_UPDATED.toString() to RecurringPatternUpdated::class.java
)
override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
if (!annotations.isEmpty() || type != EventResponse::class.java) {
return null
}
val size = subtypeToLabel.size
val labelToDelegate = LinkedHashMap<String, JsonAdapter<EventResponse<BaseData>>>(size)
for (entry in subtypeToLabel.entries) {
val key = entry.key
val value = entry.value
val parameterizedType = Types.newParameterizedType(EventResponse::class.java, value)
val delegate = moshi.adapter<EventResponse<BaseData>>(parameterizedType, annotations)
labelToDelegate.put(key, delegate)
}
return EventResponseAdapter(
labelKey,
labelToDelegate
)
}
private class EventResponseAdapter internal constructor(
private val labelKey: String,
private val labelToDelegate: LinkedHashMap<String, JsonAdapter<EventResponse<BaseData>>>
) : JsonAdapter<EventResponse<BaseData>>() {
override fun fromJson(reader: JsonReader): EventResponse<BaseData>? {
val raw = reader.readJsonValue()
if (raw !is Map<*, *>) {
throw JsonDataException("Value must be a JSON object but had a value of $raw of type ${raw?.javaClass}")
}
val label = raw.get(labelKey) ?: throw JsonDataException("Missing label for $labelKey")
if (label !is String) {
throw JsonDataException("Label for $labelKey must be a string but had a value of $label of type ${label.javaClass}")
}
val delegate = labelToDelegate[label] ?: return null
return delegate.fromJsonValue(raw)
}
// Not used
override fun toJson(writer: JsonWriter, value: EventResponse<BaseData>?) {}
}
}
The only thing to watch out for is that the RuntimeJsonAdapterFactory in the link uses Types.getRawType(type) to get the type with the generics stripped away. We, of course, don't want that because once the specific generic type has been found, we want the normal Moshi adapters to kick in and do the proper parsing for us.

Android Room error: TypeConverter not recognised for List of Enums

The Room library is not recognizing a TypeConverter I created for a List of enums. However, when I change this to an ArrayList of enums it works fine. Anyone has any idea why and what can I do to make this work with List? (Using List in Kotlin is easier and I really don't wanna be converting back and forwards to ArrayList just because of this).
Here is my code:
My model:
#Entity
data class Example(#PrimaryKey val id: String?,
val name: String,
var days: List<DayOfWeek>?)
DayOfWeek is an enum:
enum class DayOfWeek {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
val value: Int
get() = ordinal + 1
companion object {
private val ENUMS = DayOfWeek.values()
fun of(dayOfWeek: Int): DayOfWeek {
if (dayOfWeek < 1 || dayOfWeek > 7) {
throw RuntimeException("Invalid value for DayOfWeek: " + dayOfWeek)
}
return ENUMS[dayOfWeek - 1]
}
}
}
My TypeConverter:
private const val SEPARATOR = ","
class DayOfWeekConverter {
#TypeConverter
fun daysOfWeekToString(daysOfWeek: List<DayOfWeek>?): String? {
return daysOfWeek?.map { it.value }?.joinToString(separator = SEPARATOR)
}
#TypeConverter
fun stringToDaysOfWeek(daysOfWeek: String?): List<DayOfWeek>? {
return daysOfWeek?.split(SEPARATOR)?.map { DayOfWeek.of(it.toInt()) }
}
}
And I set it in my DB class like this:
#Database(entities = arrayOf(Example::class), version = 1)
#TypeConverters(DayOfWeekConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun exampleDao(): ExampleDao
}
My DAO looks like this:
#Dao
interface ExampleDao {
#Query("SELECT * FROM example")
fun getAll(): LiveData<List<Example>>
#Insert(onConflict = REPLACE)
fun save(examples: List<Example>)
}
The error I get with this code is:
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
e:
e: private java.util.List<? extends com.example.DayOfWeek> days;
Like I said above, if I change the days property to ArrayList<DayOfWeek> (and make the changes to ArrayList in DayOfWeekConverter) then everything works fine. If anyone can help me figure this out and tell me how I can use List here it'd be of great help, it is driving me crazy :/.
For some reason, Room does not like Kotlin List, but when I've replaced List with MutableList it started to work:
#Entity
data class Example(#PrimaryKey val id: String,
val name: String,
var days: MutableList<DayOfWeek>?)
class DayOfWeekConverter {
companion object {
#TypeConverter
#JvmStatic
fun daysOfWeekToString(daysOfWeek: MutableList<DayOfWeek>?): String? =
daysOfWeek?.map { it.value }?.joinToString(separator = SEPARATOR)
#TypeConverter
#JvmStatic
fun stringToDaysOfWeek(daysOfWeek: String?): MutableList<DayOfWeek>? =
daysOfWeek?.split(SEPARATOR)?.map { DayOfWeek.of(it.toInt()) }?.toMutableList()
}
}
This is not perfect solution, but hope you can investigate more with that.
Also you need to change #PrimaryKey to be not nullable
The full signature of List in Kotlin is List<out E>(List<? extend E> in Java), it doesn't make any sense to convert such generic type. In other words, Room doesn't know if the input is a DayOfWeek or its subclass.
As for ArrayList and MutableList, their full signature is ArrayList<E> and MutableList<E> correspondingly, the input type is fixed and hence Room knows how to convert it.
We have no way to store and get a List enum without array list. Room does not support it. But if you want to avoid using array list, you can create an object ListDayOfWeek with List is an attribute. I tried it and it's ok. If you need code, please reply here. I will post it.
You should not store it like that into your database. Better build something like that and store it as int:
enum class DaysOfWeek(var bitValue: Int) {
Monday(64),
Tuesday(32),
Wednesday(16),
Thursday(8),
Friday(4),
Saturday(2),
Sunday(1);
}
Finally your utility functions to convert from/to enum.
fun daysToBitValue(days: EnumSet<DaysOfWeek>): Int {
var daysBitValue = 0
for (`val` in days) daysBitValue += `val`.bitValue
return daysBitValue
}
private fun fromBitValues(origBitMask: Int): EnumSet<DaysOfWeek> {
val ret_val = EnumSet.noneOf(DaysOfWeek::class.java)
var bitMask = origBitMask
for (`val` in DaysOfWeek.values()) {
if (`val`.bitValue and bitMask == `val`.bitValue) {
bitMask = bitMask and `val`.bitValue.inv()
ret_val.add(`val`)
}
}
if (bitMask != 0) {
throw IllegalArgumentException(String.format(Locale.getDefault(), "Bit mask value 0x%X(%d) has unsupported bits 0x%X. Extracted values: %s", origBitMask, origBitMask, bitMask, ret_val))
}
return ret_val
}
Now you can either store an int and get the weekdays later:
#Entity
data class Example(#PrimaryKey val id: String?,
val name: String,
var days: Int = 0)
or you use the enum and write a proper typeconverter for that.
#Entity
data class Example(#PrimaryKey val id: String?,
val name: String,
var days: EnumSet<DaysOfWeek>?)
class DayOfWeekConverter {
#TypeConverter
fun daysOfWeekToString(daysOfWeek: EnumSet<DayOfWeek>?) =
daysOfWeek?.let{ YourUtilities.daysToBitValue(it) }
#TypeConverter
fun intToDaysOfWeek(daysOfWeek: Int?) =
daysOfWeek?.let { YourUtilities.fromBitValues(it) }
}
You can loop through the enum and get all days by using
for (aDaysOfWeekEnumSet in daysOfWeekEnumSet)
info{ "day = ${aDaysOfWeekEnumSet.name"}
Code is untested because im not on my Computer, but this should show the concept which works better then storing an enum (which is just an alias to a predefined value!)

Categories

Resources