Kotlin Cancels getParceableExtra call when passing data class using Intent - kotlin

Trying to pass a data class User from one Activity to another using Intent.
My putExtra looks like this using my observe fun:
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("userData",userData)
startActivity(intent)
My get routine looks like this:
userData = intent.getParcelableExtra<User>("userData") as User
or
userData = intent.getParcelableExtra("userData")
My problem is that Android Studio strikes out the function. My User data class is marked #Parcelize. It all ends up getParcelableExtra.
I've add to my gradle build:
id 'kotlin-parcelize'
I've read several posts about Parcelable being more modern than Serialable, so that's the technique I'm using. All the posts are from 2018 or prior and many of them in Java.
How does one send an data class from one Activity to another using Intent?

Since getParcelableExtra (String name) is deprecated from api level 33 you can use getParcelableExtra (String name, Class<T> clazz) from api level 33
In Your case use :
val userData =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra("userData", userData::class.java)
}
else{
intent.getParcelableExtra("userData") as userData?
}
where TIRAMISU is constant value 33
To get more info:Read this:
https://developer.android.com/reference/android/content/Intent#getParcelableExtra(java.lang.String,%20java.lang.Class%3CT%3E)

SOLUTION:
Given the need for compiler version 33 for the most modern solution, I went with a more backward compatible solution. This code translates the data object into a string, then back into the object.
SETUP: (PUT)
private var _userData = MutableLiveData<User>() // does the username and pw validate?
val userData : LiveData<User>
get() = _userData
SETUP: (GET)
private lateinit var userData: User
PUT CODE:
val intent = Intent(this, MainActivity::class.java)
val jsonUserData = Gson().toJson(userData)
intent.putExtra("userData",jsonUserData)
GET CODE:
val jsonUserData = intent.getStringExtra("userData")
userData = Gson().fromJson(jsonUserData, User::class.java)

Related

Kotlin Annotation processor doesn't add import for generated files

I have an Annotation-processor, which should generate a class MyGeneratedClass containing a variable of another class MyEntity.
My code inside the processfunction:
val elementsWithAnnotation = roundEnv.getElementsAnnotatedWith(MyClass::class.java)
if (elementsWithAnnotation.isEmpty()) {
return true
}
val fileName = "MyGeneratedClass"
val packageName = "me.myname.sdk.generated"
val classBuilder = TypeSpec.classBuilder(fileName)
for (element in elementsWithAnnotation) {
val ann = element.getAnnotation(MyClass::class.java)
println("package: "+ ann.javaClass.packageName)
val variableBuilder =
PropertySpec.varBuilder(
name = element.simpleName.toString(),
type = ClassName("", element.asType().asTypeName().asNullable().toString()),
).initializer("null")
classBuilder
.addProperty(variableBuilder.build())
}
val file = FileSpec.builder(packageName, fileName)
.addType(classBuilder.build())
.build()
val generatedDirectory = processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME]
file.writeTo(File(generatedDirectory, "$fileName.kt"))
return true
But the generated code misses the import MyEntity
package me.myname.sdk.generated
class MyGeneratedClass {
var MyEntity: MyEntity? = null
}
When looking inside the generated file, IntelliJ suggests me to import MyEntity, which resolves the error. But how can I achieve, that the import MyEntity statement is being added when generating the file?
looking at the kotlinpoet documentation https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/-class-name/index.html
seems like the first argument in your code, which is a empty string is the package name you are missing in the generated code.
in my experience kotlinpoet is much happier to generate code that in in packages. it sometimes does silly things with types in the root/default package.

How to properly create and connect to database with Kotlin Exposed?

I did check the similar question here, but it didn't work as expected for me, I still need to reconnect to the created database and I'm not sure how or even if I can avoid that.
Here is my code:
hikari.properties:
jdbcUrl=jdbc:mariadb://localhost:3306/
driverClassName=org.mariadb.jdbc.Driver
username=root
dataSource.databaseName=DBNAME //this doesn't seem to do much, I'm getting the same behavior with or without it
fun initDB() {
val config = HikariConfig("/hikari.properties")
val ds = HikariDataSource(config)
transaction(connect(ds)) {
SchemaUtils.createDatabase("DBNAME")
}
config.jdbcUrl = "jdbc:mariadb://localhost:3306/DBNAME"
//ds.jdbcUrl = "jdbc:mariadb://localhost:3306/DBNAME" //THIS WILL NOT WORK
val ds2 = HikariDataSource(config)
transaction(connect(ds2)) {
SchemaUtils.create( Tables... )
}
}
The reason I make a new datasource, is because otherwise I'll get this error:
java.lang.IllegalStateException: The configuration of the pool is sealed once started. Use HikariConfigMXBean for runtime changes. HikariConfigMXBean doesn't seem allow jdbcUrl changes.
There must be a more elegant way to do this, right?
It is not a good practice. Creating and filling db it is two separate processes. DB creates once by the DBA, then you just connect and use it. Your approach has huge security violation. User from the datasource must have create db privilege.
But if you want proceed with your current approach, first you should create a db without using datasource, then create datasource and connect to the db. Something like this
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.transactions.transaction
fun main(args: Array<String>) {
//LOAD proerties
val config = HikariConfig("/hikari.properties")
//Hikari properties content
// jdbcUrl=jdbc:mysql://localhost:3306/hikari
// driverClassName=com.mysql.cj.jdbc.Driver
// username=root
// password=<pwd here>
//Here replace dbname from jdbc url to empty
transaction(Database.connect(url = config.jdbcUrl.replace("hikari", ""),
driver = config.driverClassName,
user = config.username,
password = config.password)) {
SchemaUtils.createDatabase("hikari")
}
val ds = HikariDataSource(config)
transaction(Database.connect(ds)) {
SchemaUtils.create(Users)
}
}
object Users : Table() {
val id = varchar("id", 10) // Column<String>
val name = varchar("name", length = 50) // Column<String>
override val primaryKey = PrimaryKey(id, name = "PK_User_ID")
}

How can I don't create a database if the database exists with Room in Kotlin?

Normally I use Code A to create a database Tasks.db with Room when the app run for the first time, I hope that Room doesn't create the database Tasks.db again when I run the app again, how can I do?
Code A
val result = Room.databaseBuilder(
context.applicationContext,
ToDoDatabase::class.java, "Tasks.db"
).build()
This is safe to use as it is. You'll only get a new ToDoDatabase instance that you can access your database file through, but the file on disk won't be erased and recreated if it already exists.
You can also use method onCreate() of RoomDatabase.Callback, which is invoke only the first time you create a data base:
val result = Room.databaseBuilder(context.applicationContext,
ToDoDatabase::class.java, "Tasks.db").addCallback(dbCallback).build()
...
var dbCallback: RoomDatabase.Callback = object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
Executors.newSingleThreadScheduledExecutor().execute {
Log.i(TAG, "create database")
result!!.getDao().insertAll(...) // add default data
...
}
}
}

How to exclude mutations from Query root node with spqr?

I use io.leangen.graphql.spqr library version 0.9.6 and I need to exclude mutations from Query root node into the Doc.
My GraphQLController.kt looks this way:
#RestController
class GraphQLController {
private var graphQL: GraphQL? = null
#Autowired
fun GraphQLController(bookGraph: BookGraph) {
val schema = GraphQLSchemaGenerator()
.withResolverBuilders(
AnnotatedResolverBuilder(),
PublicResolverBuilder("com.example.graphql.spqr"))
.withOperationsFromSingleton(bookGraph)
.withValueMapperFactory(JacksonValueMapperFactory())
.generate()
graphQL = GraphQL.newGraphQL(schema)
.build()
}
#PostMapping(value = ["/graphql"], consumes = [MediaType.APPLICATION_JSON_UTF8_VALUE], produces = [MediaType.APPLICATION_JSON_UTF8_VALUE])
#ResponseBody
fun execute(#RequestBody request: Map<String?, Any?>): ExecutionResult? {
return graphQL!!.execute(ExecutionInput.newExecutionInput()
.query(request["query"] as String?)
.variables(request["variables"] as Map<String, Object>?)
.operationName(request["operationName"] as String?)
.build())
and my BookGraph.kt looks this way:
#Component
class BookGraph {
#Autowired
private lateinit var bookService: BookService
#GraphQLQuery(name = "books")
fun books() : List<Book> {
return bookService.findAll()
}
#GraphQLMutation(name = "createBook")
fun createBook(#GraphQLInputField(name = "name") name: String) : Book {
return bookService.findAll()
}
}
How can I do it?
I searched for possible solutions both in StackOverflow and SPQR issues but cannot find a solution.
Example of Query root node below, I want to exclude createBook:
While I want Mutation root node to remain untouched:
It's bug. You're using a very old version of SPQR (Feb. 2018). This has been fixed a long long time ago. Please try to follow the releases as much as possible, as lots of things are getting fixed and improved.
It is possible to work around the bug by customizing the ResolverBuilders, but I wouldn't recommend going that route.
The Spring Starter (if even relevant to you) is currently lagging behind (not yet on the latest SPQR version) but I'm actively working on the new release. Should be out very soon.
Btw, your setup has a lot of redundancy. Can be simplified to:
val schema = GraphQLSchemaGenerator()
.withOperationsFromSingleton(bookGraph)
//replace with your own root package(s)
.withBasePackages("com.example.graphql.spqr")
.generate()

How do you mock a URL connection in Kotlin?

I've seen a lot of examples on how to mock a connection in Java but haven't seen any explaining how to do it in Kotlin. A bit of code that I want mocked as an example:
val url = URL("https://google.ca")
val conn = url.openConnection() as HttpURLConnection
with(conn) {
//doStuff
}
conn.disconnect()
Similar to a question like this but for Kotlin:
how to mock a URL connection
Kotlin and Java can interop with one another, so you should be able to take your exact example (from the question) provided and convert it to Kotlin (or don't convert it and call the Java directly):
#Throws(Exception::class)
fun function() {
val r = RuleEngineUtil()
val u = PowerMockito.mock(URL::class.java)
val url = "http://www.sdsgle.com"
PowerMockito.whenNew(URL::class.java).withArguments(url).thenReturn(u)
val huc = PowerMockito.mock(HttpURLConnection::class.java)
PowerMockito.`when`(u.openConnection()).thenReturn(huc)
PowerMockito.`when`(huc.getResponseCode()).thenReturn(200)
assertTrue(r.isUrlAccessible(url))
}
It's worth noting that you should probably consider using an actual mocking HTTP server like HttpMocker for handling this as opposed to implement the behavior yourself.