Read objects in many-to-many relationship in Realm? - android

I am performing many to many relationship in Realm and i am trying to read objects in Realm but i ain't getting Realm Results. This is how i'm storing objects in Realm.
fun addMessage(message : Message?,ref_id : String?){
Realm.getDefaultInstance().use {realm ->
realm.executeTransaction {realm ->
val num = realm.where<Messages>().max("auto_index")
var nextVal : Int ?= null
if (num == null){
nextVal = 1
}else{
nextVal = num.toInt() + 1
}
val msg = Messages()
msg.messageId = message?.message_uid
msg.body = message?.body
msg.isCheck = message?.isCheck
msg.auto_index = nextVal
val messa = realm.where<MessageRef>().equalTo("messageRefId",ref_id).findFirst()
if (messa != null){
messa.messages?.add(msg)
}
else {
val messageRef = MessageRef()
messageRef.messageRefId = ref_id
messageRef.messages = RealmList<Messages>()
messageRef.messages?.add(msg)
realm.copyToRealmOrUpdate(messageRef)
}
realm.copyToRealmOrUpdate(msg)
}
}
}
That's how i'm reading objects from database.
fun getAllMessages (realm : Realm?,messageRefId : String?)=
realm?.where<MessageRef>()?.equalTo("messageRefId",messageRefId)?.findFirst()
?.messages?.sort("auto_index")
}
Below is my MessageRef model.
open class MessageRef : RealmObject() {
#Required
#PrimaryKey
var messageRefId : String ?= null
var messages : RealmList<Messages> ?= null
}
Below is my Messages Model.
open class Messages : RealmObject(){
#Required
#PrimaryKey
var messageId : String ?= null
#Required
var auto_index : Int ?= null
#Required
var body : String ?= null
#Required
var isCheck : String ?= null
}
But I'm not getting any results. I debug the code and found that objects are successfully storing but not able to read.Below is my activity code.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_chat_page)
initialize()
message_ref_id = intent?.getStringExtra("conversation_id")
toolbar_name?.text = name
messageList = realmMessageController?.getAllMessages(realm,message_ref_id)
}
Please tell me anybody why i ain't getting result.

ORIGINAL ANSWER: You are getting the following error
IllegalArgumentException: 'value' is not a valid managed object
And that's because you're trying to add an unmanaged object to a managed RealmObject's RealmList
You can use copyToRealmOrUpdate() to get a managed proxy of the thing you are saving:
messageRef.messages?.add(realm.copyToRealmOrUpdate(Messages().apply {
messageId = message?.message_uid
isCheck = message?.isCheck
auto_index = nextValue
}))
EDIT: to do what you want, you just need to check against the managed RealmObject if it's already in the list.
val managed = realm.copyToRealmOrUpdate(Messages().apply {
messageId = message?.message_uid
isCheck = message?.isCheck
auto_index = nextValue
})
if(messageRef.messages?.contains(managed) == false) {
messageRef.messages?.add(managed)
}
EDIT2: For your query, you need
open class Messages : RealmObject(){
#Required
#PrimaryKey
var messageId : String ?= null
#Required
var auto_index : Int ?= null
#Required
var body : String ?= null
#Required
var isCheck : String ?= null
}
#field:LinkingObjects("messages")
val messageRefs : RealmResults<MessageRef>? = null
Then
realm.where<Messages>()
.equalTo("messageRefs.messageRefId", messageRefId)
.sort("auto_index")
.findAll()

Related

Remove scientific notation formatting for Float values in kotlin android

I am trying to set the float value to an edit text. For this I am using a binding adapter like this below.
#BindingAdapter("android:text")
#JvmStatic
fun setAmount(editText: EditText, currency: Float?) {
if (currency!= null && currency!=0.0f) {
editText.setText(""+Math.round(currency))
}else{
editText.setText("")
}
}
Model Class
class OpportunityModel : BaseObservable(), Serializable {
var accountName = ""
var accountGuid: String? = null
var amount = 0.0
var potentialAmount:Float = 0.0f
get() = field
set(value) {field=value}
var contactPersonName = ""
var fieldOne = ""
var fieldTwo = ""
var fieldThree = ""
var fieldFour = ""
var fieldFive = ""
var opportunityName = ""
var opportunityGuid: String? = null
var opportunityRating = 0
var opportunityReasonGuid: String? = null
var opportunityIntStatus = 2
var opportunityDispStatus = ""
var opportunityNotAvailable = false
var genericFieldUI: GenericFieldDto? = null
#SerializedName("expDateOfClosure")
var dateForServer: String? = null
var expDate = ""
var contactPersonNameGuid: String? = null
var listOfAccountContact = ArrayList<AccountContactPersonModel>()
var listOfReasonMaster = ArrayList<ReasonMasterDto>()}
This shows the value properly in the edit text but when this value is added to the model class via data binding, it gets converted to scientific notation and is showing values like 1E+07. How can I stop this conversion to scientific notation ?
You can use String.format, like
#BindingAdapter("android:text")
#JvmStatic
fun setAmount(editText: EditText, currency: Float?) {
if (currency!= null && currency!=0.0f) {
editText.setText(String.format("%.8f", currency))
}else{
editText.setText("")
}
}

Create an AdapterFactory for gson for dynamic Types

I have a JSON string that I need to converted to data class object in Kotlin, the problem is that there is a field (details) that can have a different structure depending of the value of another field like this
val jsonString1 = "{'name': 'Juan', 'phase': 'step1', 'details': { 'name': 'product 1' }}"
val jsonString2 = "{'name': 'Juan', 'phase': 'step2', 'details': { 'position': 10 }}"
now I have something like
data class Customer(
var name: String? = null
var phase: String? = null
var details: Details? = null
)
data class Details(
var name: String? = null
)
data class Details2(
var position: Int? = null
)
now with gson I know I can
Gson().fromJson(jsonString1, Customer::class.java)
I want to be able to automatically use the right data class depending on the value of the phase field, I know I can create an adapterFactory, but I can't figure out how, an in kotlin is worse
I was reading this post
http://anakinfoxe.com/blog/2016/02/01/gson-typeadapter-and-typeadapterfactory/
and I'm pretty sure is the way to go, but I can't quite get it
Yep, it's pretty easy to write such adapter. I've slightly changed your example:
data class Customer(
var name: String? = null,
var phase: String? = null,
var details: Details? = null
)
sealed class Details {
data class Details1(var name: String? = null) : Details()
data class Details2(var position: Int? = null) : Details()
}
class CustomerDeserializer : JsonDeserializer<Customer> {
override fun deserialize(json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext?): Customer {
val customerObject = json.asJsonObject
val detailsObject = customerObject.getAsJsonObject("details")
val details = if (detailsObject.has("name")) {
Details.Details1(detailsObject.get("name").asString)
} else {
Details.Details2(detailsObject.get("position").asInt)
}
return Customer(
name = customerObject.get("name").asString,
phase = customerObject.get("phase").asString,
details = details
)
}
}
fun main() {
val gson = GsonBuilder()
.registerTypeAdapter(Customer::class.java, CustomerDeserializer())
.create()
println(gson.fromJson(jsonString1, Customer::class.java))
println(gson.fromJson(jsonString2, Customer::class.java))
}
data class Customer(
var name: String? = null
var phase: String? = null
var details: Details? = null
)
data class Details(
var name: String? = null
var position: Int? = null
)
Define Details class in this way
Gson().fromJson(jsonString1, Customer::class.java)
return a Customer either name is null or position is null

Android Kotlin save data class in Firebase Database

I am working on an Android application in Kotlin which integrate Firebase.
Now I want to store my data (Kotlin data class) into Firebase Database.
Data Classes:
#Parcelize
data class Trip(
val fromAddress: String,
val toAddress: String,
val fromLocation: String,
val toLocation: String,
val orderUid: String
) : Parcelable
#Parcelize
data class Order(val trip: Trip, val date: Date, var status: OrderStatus, val userUid: String) : Parcelable {
var pickUpDate: Date? = null
var dropOffDate: Date? = null
var price: Double? = null
}
Fireabase Database write operation:
fun createNewOrder(
fromAddress: String,
toAddress: String,
fromLocation: Location,
toLocation: Location
) {
val fromGeoLocation = fromLocation.convertToGeoLocation()
val toGeoLocation = toLocation.convertToGeoLocation()
val userUid = sharedPreferences[CURRENT_USER_UID_KEY, ""]!!
val orderKey = databaseReference.child(DB_ORDERS_KEY).push().key
val tripKey = databaseReference.child(DB_TRIPS_KEY).push().key
val trip = orderKey?.let { createNewTrip(fromAddress, toAddress, it) }
val order = trip?.let { Order(it, Date(), OrderStatus.PENDING, userUid) }
if (trip != null && order != null && !userUid.isNullOrEmpty()) {
ordersGeoFire.setLocation(trip.fromGeoLocation, fromGeoLocation)
ordersGeoFire.setLocation(trip.toGeoLocation, toGeoLocation)
val allData = mutableMapOf<String, Any>()
allData["/$DB_TRIPS_KEY/$tripKey"] = trip?.convertToMap()
allData["/$DB_ORDERS_KEY/$orderKey"] = order?.convertToMap()
allData["/$DB_USERS_KEY/$userUid/$DB_ORDERS_KEY/$orderKey"] = true
databaseReference.updateChildren(allData)
}
}
I received this error:
com.google.firebase.database.DatabaseException: No properties to serialize found on class kotlin.Unit
Any suggestions?
The problem in your code is that the fileds inside your Trip class are not initialized. A recommended way in which you can create your model class would be:
class Trip(
val displayName: String = "",
val email: String = "",
val photoUrl: String = "",
val userId: String = ""
)
This is only what you need. And a way to create a new object of your Trip class, would be:
val trip = Trip(displayName, email, photoUrl, userId)
It was my mistake, because I was forget to add return type in my extensions convertToMap functions. Now they look like this:
fun Trip.convertToMap(): MutableMap<String, Any> {
val map = mutableMapOf<String, Any>()
map["fromAddress"] = fromAddress
map["toAddress"] = toAddress
map["fromGeoLocation"] = fromGeoLocation
map["toGeoLocation"] = toGeoLocation
map["orderUid"] = orderUid
return map
}
And also thanks to #Alex Mamo for his answer, it helps me in my investigation.
Now my code looks like this and works fine:
#Parcelize
data class Trip(
var fromAddress: String = "",
var toAddress: String = "",
var fromGeoLocation: String = "",
var toGeoLocation: String = "",
var orderUid: String = ""
) : Parcelable
#Parcelize
data class Order(
var trip: Trip? = null,
var date: Date? = null,
var status: OrderStatus? = null,
var userUid: String = ""
) : Parcelable {
var pickUpDate: Date? = null
var dropOffDate: Date? = null
var price: Double? = null
}
fun createNewOrder(
fromAddress: String,
toAddress: String,
fromLocation: Location,
toLocation: Location
): LiveData<Order> {
orderLiveData = MutableLiveData()
orderLiveData.value = null
val userUid = sharedPreferences[CURRENT_USER_UID_KEY, ""]!!
val orderKey = databaseReference.child(DB_ORDERS_KEY).push().key
val tripKey = databaseReference.child(DB_TRIPS_KEY).push().key
val trip = orderKey?.let { createNewTrip(fromAddress, toAddress, fromLocation, toLocation, it) }
val order = trip?.let { Order(it, Date(), OrderStatus.PENDING, userUid) }
if (trip != null && order != null && !userUid.isNullOrEmpty()) {
val allData = mutableMapOf<String, Any>()
allData["/$DB_TRIPS_KEY/$tripKey"] = trip.convertToMap()
allData["/$DB_ORDERS_KEY/$orderKey"] = order.convertToMap()
allData["/$DB_USERS_KEY/$userUid/$DB_ORDERS_KEY/$orderKey"] = true
databaseReference.updateChildren(allData) { databaseError, databaseReference ->
if (databaseError == null) orderLiveData.value = order
}
}
return orderLiveData
}
Hope this will be helpful

Android Kotlin: Realm many to one relation

I have trouble with the management with one-to-many relations between Kotlin objects using Realm. I don't understand how to do some insert or update.
Here my two objects with one-to-many relation between exam and career.
open class Career : RealmObject() {
#PrimaryKey
var id: Long = 1
var status: String = CareerStatus.ACTIVE.value
var careerName: String? = null
var exams: RealmList<Exam>? = null
}
open class Exam : RealmObject() {
#PrimaryKey
var id: Long = 1
var teachers: String? = null
var lode: Boolean? = null
var score: String = ""
var exam: String = ""
var examDescription: String? = null
var date: Date = Date()
var cfu: Long = Long.MIN_VALUE
#LinkingObjects("exams")
val career: RealmResults<Career>? = null
}
Here the two class Manager the manage the read/write on Realm
class CareerManager : DatabaseManager() {
fun findActiveCareer(): Career? {
return realm.where(Career::class.java).equalTo("status", CareerStatus.ACTIVE.value).findFirst()
}
fun insertExamInCareer(exam: Exam) {
realm.executeTransaction {
var activeCareer: Career = findActiveCareer()
?: throw Resources.NotFoundException("No active career found")
activeCareer.exams?.add(exam)
realm.copyToRealmOrUpdate(exam)
}
}
fun closeActiveCareer() {
realm.executeTransaction {
val activeCareer: Career = findActiveCareer()
?: throw Resources.NotFoundException("No active career found")
activeCareer.status = CareerStatus.TERMINATED.value
}
}
}
class ExamManager : DatabaseManager() {
fun findAll(): List<Exam> {
val findAll = realm.where(Exam::class.java).findAll()
return realm.copyFromRealm(findAll).toList()
}
fun findAllActive(): List<Exam> {
val findAll = realm.where(Exam::class.java).equalTo("career.status", CareerStatus.ACTIVE.value).findAll()
return realm.copyFromRealm(findAll).toList()
}
fun insert(exam: Exam): Long {
realm.beginTransaction()
var newId: Long = 1
if (realm.where(Exam::class.java).max("id") != null) {
newId = realm.where(Exam::class.java).max("id") as Long + 2
}
val examToSave = realm.createObject(Exam::class.java, newId)
examToSave.exam = exam.exam
examToSave.examDescription = exam.examDescription
examToSave.date = exam.date
examToSave.teachers = exam.teachers
examToSave.score = exam.score
examToSave.lode = exam.lode
examToSave.cfu = exam.cfu
realm.commitTransaction()
CareerManager().updateCareerExam(findAll())
return newId
}
}
In my application I need to insert some exams in my career that is actually in ACTIVE state. For the insert I use CareerManager().insertExamInCareer(exam) this call (before I try to use ExamManager().insert(exam) but doesn't work).
Another use case is when I need to close a career and add the exam to another career. I do this in this manner
CareerManager().closeActiveCareer()
CareerManager().createCareer()
CareerManager().insertExamInCareer(exam)
The exam in the two career are the same, but they had to be different and in Exam there must have been two records, not one

DbFlow Kotlin and List<String> Type Converter

DBFlow Version: 4.0.4
Hi, I'm strugling with List Type converter with dbflow Android ORM and Kotlin. I have a data class defined like this:
#Table(database = StopsDb::class)
data class FavouriteStop(
#PrimaryKey #Column var id: String = "",
#Index #Column var name: String = "",
#Column(typeConverter = StringListConverter::class) var directions: List<String> = listOf(),
#Column(typeConverter = StringListConverter::class) var selectedDirections: List<String> = listOf()
) : BaseRXModel()
and as I don't want to create a separate table only to store Strings I created a List Type converter like this:
class StringListConverter : TypeConverter<String, List<String>>() {
val separator = ","
override fun getDBValue(model: List<String>?): String {
if(model==null || model.isEmpty())
return ""
else
return model.joinToString (separator = separator){ it }
}
override fun getModelValue(data: String?): List<String> {
return data?.split(separator) ?: listOf()
}
}
however following error is thrown during build phase:
Error:error: *==========* :The specified custom TypeConverter's Model Value java.util.List<? extends java.lang.String> from com.kapuscinski.departures.persistence.db.StringListConverter must match the type of the column java.util.List<java.lang.String>.*==========*
Am I missing something here, and how to fix this? Thanks in advance for help!
Change everything from "List" to "MutableList"
#Table(database = StopsDb::class)
data class FavouriteStop(
#PrimaryKey #Column var id: String = "",
#Index #Column var name: String = "",
#Column(typeConverter = StringListConverter::class) var directions: MutableList<String> = mutableListOf(),
#Column(typeConverter = StringListConverter::class) var selectedDirections: MutableList<String> = mutableListOf()
) : BaseRXModel()
class StringListConverter : TypeConverter<String, MutableList<String>>() {
val separator = ","
override fun getDBValue(model: MutableList<String>?): String =
if (model == null || model.isEmpty())
""
else
model.joinToString(separator = separator) { it }
override fun getModelValue(data: String?): MutableList<String> {
return data?.split(separator)?.toMutableList() ?: mutableListOf()
}
}

Categories

Resources