Migration is required due to Property id has been made required - realm

it works fine if i change lateinit var id: String in the Payment.kt and CartPayment.kt to var id: String? = "", but the problem is i want the id to be required, how can i achieve that ?
The Error:
java.lang.RuntimeException: Unable to create application: io.realm.exceptions.RealmMigrationNeededException: Migration is required due to the following errors:
- Property 'CartPayment.id' has been made required.
- Property 'Payment.id' has been made required.
Model :
open class Payment() : RealmObject() {
#PrimaryKey
lateinit var id: String
var typeValue: Int = 0
var statusValue: Int = 0
var value: Double = 0.0
var referenceNumber: String? = null
Note: Payment and CartPayment models are identical except for the class name
Migration.kt
class Migration : RealmMigration {
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
var oldVersion = oldVersion
val schema = realm.schema
if (oldVersion == 0L) {
schema.create("Payment")
.addField("id", String::class.java, FieldAttribute.PRIMARY_KEY)
.addField("typeValue", Int::class.java)
.addField("statusValue", Int::class.java)
.addField("value", Double::class.java)
.addField("referenceNumber", String::class.java)
schema.get("Order")!!
.addRealmListField("payments", schema.get("Payment")!!)
oldVersion++
}
if (oldVersion == 1L) {
schema.create("CartPayment")
.addField("id", String::class.java, FieldAttribute.PRIMARY_KEY)
.addField("typeValue", Int::class.java)
.addField("statusValue", Int::class.java)
.addField("value", Double::class.java)
.addField("referenceNumber", String::class.java)
schema.get("Order")!!
.addField("cashPaymentAmount", Float::class.java)
.addField("change", Float::class.java)
oldVersion++
}
}
}
App.kt
class App: Application() {
override fun onCreate() {
super.onCreate()
Realm.init(this)
val realmConfig = RealmConfiguration.Builder()
.schemaVersion(2)
.migration(Migration())
.build()
Realm.getInstance(realmConfig)
Realm.setDefaultConfiguration(realmConfig)
Fresco.initialize(this)
}
}

.addField("id", String::class.java, FieldAttribute.PRIMARY_KEY, FieldAttribute.REQUIRED)
did the trick.
if you declare the variable to be lateinit, make sure to add FieldAttribute.REQUIRED.

Basically you are adding a new field "id" which is primary key (hence required key).
If you do not specify any value while initialisation (lateinit), how will realm migrate all the earlier records, which doesn't have an id, but is required after migration ? Hence the error.
Below solutions might work
Either pre-populate the id's (without using lateinit)
Transform your earlier records to have id's if they don't have
Check the official examples here

For me, it happened after I have done the Migration.
I have made a non-nullable object in Kotlin and on migration I was creating a nullable wrapper type like Double, Int, etc.
Just use
Double::class.java
instead of
Double::class.javaObjectType

Related

Convert Firebase DocumentSnapshot data to a custom Scala (or Java) class

I'm using Firebase for a private Scala project and I'm struggling to understand how I can manage Firebase responses if I want to avoid using HashMap.
This is the information that I'm trying to manage:
These are the two Scala classes that I wrote with the idea to use them along with the toObject method:
class Doc() {
#BeanProperty val map: util.HashMap[String, Value] = new util.HashMap[String, Value]()
}
class Value() {
#BeanProperty val displayName: String = ""
#BeanProperty val email: String = ""
// Extra fields that I need to initialize in the Scala code
#BeanProperty val totalLogins: Int = 0
#BeanProperty val todoMap: util.HashMap[String, String] = new util.HashMap[String, String]()
#BeanProperty val todoList: util.ArrayList[String] = new util.ArrayList[String]()
#BeanProperty val totalChanges: Int = 0
#BeanProperty val totalErrors: Int = 0
}
And this is snapshot listener implementation that I wrote:
docFirebase.addSnapshotListener(new EventListener[DocumentSnapshot]() {
override def onEvent(snapshot: DocumentSnapshot, e: FirestoreException): Unit = {
if (e != null) {
println("[OnSnapshot] Listen failed: " + e)
return
}
if (snapshot != null && snapshot.exists) {
val doc = snapshot.toObject(classOf[Doc])
// Here below I'll write the complex logic I need ...
} else {
println("[OnSnapshot] Current data: null")
}
}
})
Using that code I'm always getting an empty HashMap into the doc variable. Can someone helps me understand what I misunderstood about reading data from Firebase ? Thanks in advance.
All of your properties in the document are nested under an object called "abc". That fact is not reflected in your code - you need to call out abc by name to get all the nested fields from it. You probably don't want that nesting at all. Just put all the fields (displayName, email, etc) as top-level fields in the document.

Save an edit text from an alert dialog and use it as a string variable (Kotlin)

I'm really new with android studio and kotlin and I'm having troubles doing an alert dialog that saves the edit text on a variable and then uses it in another activity.
To be clear I want to make an activity where there is a button. If you click on it then the Alert Dialog happens and the text should be store in a variable called "name". Code:
class MainActivity2 : AppCompatActivity() {
var name: String? = null
lateinit var textView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
create.setOnClickListener{
val editAlert = AlertDialog.Builder(this).create()
val editView = layoutInflater.inflate(R.layout.alert_dialog_create, null)
editAlert.setView(editView)
editAlert.setButton(AlertDialog.BUTTON_POSITIVE, "Accept") { _, _ ->
val text = editAlert.alert_dialog_create_btn.text
name= text.toString()
Toast.makeText(this, "Created", Toast.LENGTH_SHORT).show()
}
editAlert.show()
}
}
}
Then, I want that name to be use as my sqlite table name. So I have mi SQLite Helper as this:
val DATABASE_NAME = "Test"
val COL_ID =" id"
val COL_NAME = "name"
class AdminSQLiteOpenHelper(var context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, 1) {
var table_name = MainActivity2().name
override fun onCreate(db: SQLiteDatabase) {
val createTable = "CREATE TABLE " + table_name +" (" +
COL_ID +" INTEGER PRIMARY KEY AUTOINCREMENT," +
COL_NAME + " VARCHAR(256))"
db?.execSQL(createTable)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
}
fun insertData(database :Database){
val db = this.writableDatabase
var cv = ContentValues()
cv.put(COL_NAME, database.nombre))
var result = db.insert(table_name, null, cv)
if(result == -1.toLong())
Toast.makeText(context, "Failed", Toast.LENGTH_SHORT).show()
else
Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
}
}
And then, on another Activity, I have the edit text where the user can input the data of the table and at top of it should be an edit text with the table name (that should change if the user input a new name).
Code:
class Main2InsertActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2_insert)
val context = this
setContentView(R.layout.activity_main2_insert)
val tituloView = findViewById<TextView>(R.id.titulo)
tituloView.setText(AdminSQLiteOpenHelper(context).table_name)
insertar.setOnClickListener {
if ((name.text.toString().length > 0)
){
var baseDatos = Database(name.text.toString())
var db = AdminSQLiteOpenHelper(context)
db.insertData(baseDatos)
}else{
Toast.makeText(context, "Rellena todos los campos", Toast.LENGTH_SHORT).show()
}
}
}
}
I think the proble is in MainActivity2 because as much as I put a new name with button create and the alert dialog the value of name never changes, but I don't know how to fix it
Each Activity and Fragment (including dialogs) is a completely separate object with no connection to each other. Using the same name for a variable in two different objects is irrelevant, because they're completely unrelated.
You need to learn how to pass data between Activitys and the like, which is one of the trickier parts of getting your head around how Android apps work (seems to account for a lot of the questions on here too!). It's a pretty big and fundamental subject, so you'll need to learn about it yourself. Here's some links!
Passing Events Back to the Dialog's Host
Using Intents to pass data to Activities
Communicating with fragments
Centralising data with ViewModels
Android's been around a while and there are a lot of ways to do this by now - and I know, there's a lot to learn. At least get familiar with passing data in Intents, and the the idea of calling a method on the parent Activity (from the first link)
var table_name = MainActivity2().name
class MainActivity2 : AppCompatActivity() {
var name: String? = null
I am honestly not sure how to reply. Why do you think that name on the 1st line and name on the 3d line are part of the same object? Why are you trying to instantiate on your own an Activity?
What are you trying to achieve with this project? If it is something that you want to use for yourself and you just want to make it work and you do not want to invest time in learning Android - "this is ok".
Otherwise, if you want to make something that looks like an Android application - everything is just so far from what it should be that StackOverflow can't help. You just need to invest more time.
Why are you not using Room for the Database?

Calendar TypeConverter for Room (Kotlin)

I am trying to persist a timestamp in my room database using the following TypeConverter:
class Converters {
#TypeConverter
fun fromTimestamp(value: Long?): Calendar? {
if(value == null) return null
val cal = GregorianCalendar()
cal.timeInMillis = value
return cal
}
#TypeConverter
fun toTimestamp(timestamp: Calendar?): Long? {
if(timestamp == null) return null
return timestamp.timeInMillis
}
}
Two of my Entities include the following column
:
#ColumnInfo(name = "timestamp")
val timestamp: Calendar?,
But I get a compilation error upon trying to build the project - I had no issues when using the Date TypeConverter example from the developer reference guide.
I am unable to see what the actual error is as I just get a bunch of databinding 'cannot find symbol' errors if there is something wrong with the code related to Room.
Use:
object Converters {
#TypeConverter
#JvmStatic
fun fromTimestamp(value: Long?): Calendar? = value?.let { value ->
GregorianCalendar().also { calendar ->
calendar.timeInMillis = value
}
}
#TypeConverter
#JvmStatic
fun toTimestamp(timestamp: Calendar?): Long? = timestamp?.timeInMillis
}
And
#TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {

AnKo SQLite : populate listview asynchronously from database?

I'm trying to translate my app from Java to Kotlin.
I'm managing database with AnKo SQLite
All is OK except listviews with CursorLoaders : I can't find how to replace CursorLoader while using AnKo SQLite.
(and same problem with expandableListViews)
Can somebody help me please?
OK, here is my solution... I don't know if it is the best :
create a new kotlin class "MyCursorLoader" that extends CursorLoader
set the primary constructor like this :
class MyCursorLoader(
mContext: Context,
val mTableName: String,
var mProjection: Array<String>? = null,
var mSelection: String = "1",
var mSelectionArgs: Array<String> = emptyArray(),
var mGroupBy: String = MySqlHelper.ID,
var mHaving: String = "",
var mSortOrder: String = "${MySqlHelper.ID} ASC",
var mLimit: String = "",
var mDistinct: Boolean = true
): CursorLoader(mContext) {
val mObserver: Loader<Cursor>.ForceLoadContentObserver = Loader<Cursor>(mContext).ForceLoadContentObserver()
var mCancellationSignal: CancellationSignal? = null
override the OnLoadInBackground method with te same code than built-in one, just replacing the val cursor = ContentResolverCompat.query(... line with :
val cursor = MySqlHelper.instance.readableDatabase.query(
mDistinct, mTableName, mProjection, mSelection, mSelectionArgs, mGroupBy, mHaving, mSortOrder, mLimit, mCancellationSignal)
So no need to recreate a dataprovider in manifest, no need to deal with Uri's... I can use MyCursorLoader exactly like built-in CursorLoader, calling it like this :
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
when (id) {
DAY_HEADER_LOADER ->
return MyCursorLoader(mContext, TABLE_EVENTS, arrayOf(ID, DAY), mGroupBy = DAY, mSortOrder = "$DAY DESC")
...
}
}
Let me know if ther is a better solution.
Hope that can help.

Kotlin data class and bean validation with container element constraints

With Bean Validation 2.0 it is possible to also put constraints on container elements.
I cannot get this to work with Kotlin data classes:
data class Some(val someMap: Map<String, #Length(max = 255) String>)
This does not have any effect. Any ideas?
I created a repository with a sample project to reproduce the case: https://github.com/mduesterhoeft/bean-validation-container-constraints
Add this config to your build.gradle (note that ... means whatever is already there) :
Groovy:
compileKotlin {
kotlinOptions {
freeCompilerArgs = [..., "-Xemit-jvm-type-annotations"]
...
}
}
Kotlin DSL:
tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf(..., "-Xemit-jvm-type-annotations")
...
}
}
Starting Kotlin 1.3.70 and 1.4, this should be possible setting a specific compiler option: https://kotlinlang.org/docs/reference/whatsnew14.html#type-annotations-in-the-jvm-bytecode .
On any previous version or any situation where this support is not sufficient, you have to write a custom validator.
Example one for validating that a collection only contains hex strings:
#Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.FIELD,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.VALUE_PARAMETER
)
#Retention(AnnotationRetention.RUNTIME)
#MustBeDocumented
#Constraint(validatedBy = [HexStringElementsValidator::class])
annotation class HexStringElements(
val message: String = "must only contain hex values",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Any>> = []
)
class HexStringElementsValidator : ConstraintValidator<HexStringElements, Collection<Any>> {
companion object {
val pattern = "^[a-fA-F0-9]+\$".toRegex()
}
override fun isValid(value: Collection<Any>?, context: ConstraintValidatorContext?) =
value == null || value.all { it is String && pattern.matches(it) }
}

Resources