Merging Activities in kotlin - kotlin

I have three different activities which extends three different things and i want to implement multi level inheritance so that i can add it in android:name of androidmanifest.
Here are all activities. I don't know native development at all. Any help would be appreciated.
1st activity
package com.package
import hrx.plugin.monet.MonetApplication
class CustomApplication : MonetApplication()
2nd activity
package com.package
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
3rd activity
package com.package;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugins.androidalarmmanager.AlarmService;
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate();
AlarmService.setPluginRegistrant(this);
}
override fun registerWith(registry: PluginRegistry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}

I have three different activities
Actually, you seem to have two different Application objects and one Activity. If you don't know the difference you may want to go back and take some Android 101 courses.
i want to implement multi level inheritance so that i can add it in android:name of androidmanifest.
OK. Well you have
class CustomApplication : MonetApplication()
and
class Application : FlutterApplication(), PluginRegistrantCallback
Looking at the definition of MonetApplication we see that it's defined as
open class MonetApplication : FlutterApplication()
So MonetApplication is a FlutterApplication so if you extend MonetApplication you are effectively also extending FlutterApplication, so you can basically merge your two existing classes into one:
class HighlanderApplication : MonetApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate();
AlarmService.setPluginRegistrant(this);
}
override fun registerWith(registry: PluginRegistry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
Now you can use HighlanderApplication in your manifest and get all the logic that was in both.

Related

Implement a regular interface in a spring data repository interface

I'm using a Repository that extends a spring data JpaRepository and would like to let it extend another interface.
Previously, my db repository looked like this:
interface PublicTransportPricingZoneRepository : JpaRepository<PublicTransportPricingZone, Long> {
}
I have now created another interface TransitTicketRepo as defined below
interface TransitTicketRepo {
fun findPossibleTickets(geometry: Geometry): Collection<TransitTicket>
}
and now would like to implement the interface with a default method in PublicTransportPricingZoneRepository. I've tried to solve this by changing the code of my PublicTransportPricingZoneRepository to
interface PublicTransportPricingZoneRepository : JpaRepository<PublicTransportPricingZone, Long>, TransitTicketRepo {
fun findPossibleTickets(geometry: Geometry): Collection<TransitTicket> {
// do something
return emptyList()
}
}
but get the following error message when starting the application.
org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract java.util.Collection PublicTransportPricingZoneRepository.findPossibleTickets(Geometry); Reason: Failed to create query for method public abstract java.util.Collection...
I'm assuming the solution is to somehow tell spring data to stop auto-generating a query for findPossibleTickets but have been unable to find out how.
You can do it this way
In short.:
#Repository
// Your bean that will be glued together by spring
interface PublicTransportPricingZoneRepository extends JpaRepository<...>, PublicTransportPricingZoneCustomRepository {
// Other methods, but everything must be annotated with #Query or possible for spring to guess what it needs to do from its name (google derived methods)
}
interface PublicTransportPricingZoneCustomRepository {
// define custom methods
List<String> nameIsNotImportant();
}
#Service
// naming here is important - it must be names as interface + Impl, otherwise spring won't pick it up
// apart from that, it's a regular bean - you can Autowire, etc
class PublicTransportPricingZoneCustomRepositoryImpl implementats PublicTransportPricingZoneCustomRepository {
// Implement custom methods
#Overridden
public List<String> nameIsNotImportant() {
// impl
}
}

Dagger 2 component keeping old reference

Background
I have multiple dagger scopes in the app
UserScope - scope representing user session
ActivityScope - scope per activity
UserComponent - sub component of CoreComponent
UserManager - singleton that creates UserComponent
HomeComponent - Component that is dependent on UserComponent
Issue
I am trying to update user object under UserScope but somehow the object update doesn't get reflected on it's dependent components. So, once update screen updates the user object and the activity in backstack receives the event for the update & I want to reflect that update in my dagger dependencies.
I tried to null the existing component inside UserManager but it looks like the dependent components still holds it's reference.
Summary
User logs in and opens Home screen. At this moment my user manager has created user component with the user object obtained from API call.
Home screen opens update screen which updates the user by re-creating new userComponent inside UserManager. This also fires an event which tells home screen to fetch the updated user object.
Home screen receives the event. However, HomeViewModel(Screen in backstack) is still referring to old User object injected by dagger.
Questions
Is this because HomeComponent is an independant component? So, it keeps an old reference?
Is there any way to achieve this update?
Preferably without using SubComponent. Because I am using a multi-module(dynamic-feature) setup that helps me if I treat this as a separate component.
CoreComponent
#Singleton
#Component
interface CoreComponent {
val userBuilder: UserComponent.Builder
val userManager: UserManager
fun providerContext(): Context
fun inject(activity: UserActivity)
#Component.Factory
interface Factory {
fun create(
#BindsInstance applicationContext: Context
): CoreComponent
}
}
UserComponent
#UserScope
#Subcomponent
interface UserComponent {
val userManager: UserManager
#UserScope
val user: User
#Subcomponent.Builder
interface Builder {
fun bindUser(#BindsInstance user: User): Builder
fun build(): UserComponent
}
}
UserManager
#Singleton
class UserManager #Inject constructor(private val userBuilder: UserComponent.Builder) {
var userComponent: UserComponent? = null
private set
init {
// creating as a dummy reference
createUser(User("1", "1"))
}
fun createUser(user: User) {
userComponent = null
userComponent = userBuilder.bindUser(user).build()
}
fun logout() {
userComponent = null
}
}
HomeComponent
#Component(
modules = [HomeModule::class],
dependencies = [UserComponent::class]
)
#ActivityScope
interface HomeComponent {
fun inject(activity: HomeActivity)
#Component.Factory
interface Factory {
fun create(
#BindsInstance applicationContext: HomeActivity,
coreComponent: UserComponent,
): HomeComponent
}
}
HomeModule
#Module
class HomeModule {
#ActivityScope
#Provides
fun provideVM(activity: HomeActivity, user: User): HomeViewModel {
val vm by activity.scopedComponent {
HomeViewModel(user)
}
return vm
}
}
If you have components A -> B -> C and decide to swap B for B* then you also need to get rid of any object that was created by/using B or anything that depends on B, so any subcomponents, component dependencies, objects, etc.
In this example you'd recreate C as well (using B* this time) to get a C* (using the new dependencies). Now you have new components A -> B* -> C*, but your code (Activity/Fragment/ViewModel) that was created using B/C needs to be "updated" as well. Since it's not feasible to "update" dependencies later, the easiest way to do this would be to recreate all of them as well, this time using B*/C* for injection.
e.g.
In one app where I allow switching between users, I have my main Activity "Singleton -> Activity" scoped, referencing the user manager and displaying the switch UI. The actual app content (per user) is wrapped in a UserFragment which I'll replace as a whole whenever the user changes since this fragment (and all the UI within it) is Singleton -> Activity -> User -> Fragment scoped.
Since Singleton including the user manager won't change, I don't have to recreate the Activity, so all I need to do is recreate the parts affected (in my case the Fragment).

Usage of Dagger2 outside Android

I've recently started to learn Dagger. In order to do that, i've decided to write a simple console application to get the feeling of how various dagger features (like modules, component, subcomponents and component dependencies) fit together in an app architecture. As I don't really understeand it and given how hard it is to find an application sample created with dagger2 which is not Android app, i've decided to open a question here.
The first and probably most important question is: is dagger2 even ment to be used outside android?
If yes, then lets consider a simple application architecture: we have the data layer, service layer and ui layer
Data layer might consist of some kind of facade:
(Following code snippets will be written in Kotlin)
class Entity(var id: Int)
interface Repository {
fun findEntityById(id: Int): Entity?
fun deleteEntity(entity: Entity): Boolean
fun saveEntity(entity: Entity): Boolean
fun findAllEntities(): List<Entity>
}
Then i could have a couple of implementations of this facade:
class InMemoryRepository #Inject constructor() : Repository {
private val entities: MutableList<Entity> = LinkedList()
override fun findEntityById(id: Int): Entity? = entities.firstOrNull { it.id == id }
override fun deleteEntity(entity: Entity) = entities.remove(entity)
override fun saveEntity(entity: Entity) = entities.add(entity)
override fun findAllEntities(): List<Entity> = LinkedList(entities)
}
For which i would have modules:
#Module
interface InMemoryPersistenceModule {
#Singleton
#Binds
fun bindRepository(rep: InMemoryRepository): Repository
}
Service layer would be simpler:
#Singleton
class Service #Inject constructor(repository: Repository) {
fun doSomeStuffToEntity(entity: Entity) {}
}
#Singleton
class AnotherService #Inject constructor(repository: Repository) {
fun doSomeStuffToEntity(entity: Entity) {}
}
But it gets a little bit unlcear when it comes to the UI layer. Lets say i have some kind of android-like activity:
interface Activity : Runnable
And some kind of class that manages those activities:
class UserInterfaceManager {
val activityStack: Stack<Activity> = Stack()
val eventQueue: Queue<Runnable> = LinkedList()
fun startActivity(activity: Activity) = postRunnable {
activityStack.push(activity)
activity.run()
}
fun postRunnable(callback: () -> Unit) = eventQueue.add(callback)
fun stopActivity() { TODO() }
//other
}
How does dagger fit into this scenario? The articles i have read about the the dagger with android suggest createing the application component to inject my activites:
#Singleton
#Component(modules = [InMemoryPersistenceModule::class])
interface ApplicationComponent {
fun injectSomeActivity(activity: SomeActivity)
// and more
}
But then, where would the injection go to? It does't really make sense to put it in the UserInterfaceManager as Activities will most likely need an instance of it, which would create a circular dependency.
I also do not like the idea of the component being obtained from some kind of static method/property and injecting the activity from inside of it at the startup, as it creates duplicate lines of code in each activity.
Also, where do components and subcomponents fit in this kind of architecture? Why not create the separate
component for the data layer and expose just the repository and declare it as a dependency of the app component which would further isolate the details from abstraction? Maybe i should declare this component a dependcy of a service component which would enforce the layer architecure, as components can only use the types exposed in component interface? Or maybe i should use compoenent only when i need a custom scope and use the modules everywhere elsewhere?
I just overally think I am missing the bigger picture of the dagger. I will be really greatefull for answers, explanations and links to articles and other resouces that will let me understeand it better.
From the perspective of an Android developer, I fully understand your point. I asked myself this question too. The way how you construct an object in plain Java/Kotlin world is a little bit different. The main reason is due to the fact basic Android components (Activity/Fragment) don't allow constructor injection.
The answer to your question is, though, pretty straightforward. The Dagger Component is responsible for object creation, and you, as a developer, control what objects specific component provides. Let's use it in your scenario and provide some of the objects you might be interested in:
#Singleton
#Component(modules = [InMemoryPersistenceModule::class])
interface ApplicationComponent {
val service: Service
val anotherService: AnotherService
}
ApplicationComponent should be understood as a component for your whole application. It's not related to Android's Application class in any way. Now, you can simply create your component and let Dagger instantiate your objects:
val component = DaggerApplicationComponent.create()
val anotherService: AnotherService = component.anotherService
val service: AnotherService = component.service

What is equivalent Dagger's #Named in Koin?

I'm in the process to moving app from Dagger2 to Koin and need to convert below dagger's
#Provides
#Singleton
#Named("refresh")
fun provideRefreshRetrofit(#Named("refresh") okHttpClient: OkHttpClient, gson: Gson): Retrofit {/*...not important...*/}
till now:
single<Retrofit> { /*....*/ }
but I need to have similar entry in same module. Is a way to convert/solve/workaround it?
See the docs. You can give a name to a definition
single(name="refresh") { Retrofit.Builder().build() }
and use it
factory { ClassThatDependsOnRefresh(get("refresh")) }
Just using single and factory as example here.

Exception when using spring-data-mongodb with Kotlin

I'm new to Kotlin, and experimenting with spring-data-mongodb. Please see example below (also available here as fully runnable Maven project with in-memory MongoDb: https://github.com/danielsindahl/spring-boot-kotlin-example).
Application.kt
package dsitest
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
#SpringBootApplication
open class Application
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
User.kt
package dsitest
import org.springframework.data.annotation.Id
import org.springframework.data.annotation.PersistenceConstructor
import org.springframework.data.mongodb.core.mapping.Document
#Document(collection = "user")
data class User #PersistenceConstructor constructor(#Id val id: String? = null, val userName: String)
UserRepository.kt
package dsitest
import org.springframework.data.repository.CrudRepository
interface UserRepository : CrudRepository<User, String>
KotlinIntegrationTest.kt
package dsitest
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
#RunWith(SpringRunner::class)
#SpringBootTest
class KotlinIntegrationTest constructor () {
#Autowired
lateinit var userRepository : UserRepository;
#Test
fun persistenceTest() {
val user : User = User(userName = "Mary")
val savedUser = userRepository.save(user)
val loadedUser = userRepository.findOne(savedUser.id) // Failing code
println("loadedUser = ${loadedUser}")
}
}
When running the test KotlinIntegrationTest.persistenceTest, I get the following error message when trying to retrieve a User object from MongoDb:
org.springframework.data.mapping.model.MappingException: No property null found on entity class dsitest.User to bind constructor parameter to!
If I modify the User data class so that userName is nullable, everything works.
data class User #PersistenceConstructor constructor(#Id val id: String? = null,
val userName: String? = null)
I would like to understand why this is the case, since I don't want userName to be nullable. Is there some alternative, better way of defining my User class in Kotlin?
Many thanks,
Daniel
Yes, it is a known problem. You should check how the bytecode for your User class looks like. Java sees the constructor with all the parameters present and tries to call it with a null value for the 2nd one.
What you could do is to try adding #JvmOverloads to your constructor - this will force Kotlin compiler to generate all versions of the constructor and so the Spring Data Mongo could pick the correct one (get rid of the #PersistenceConstructor) then.
You could also define 1 constructor with no defaults - only for Java-based frameworks and 2nd one with some defaults your you. Or...
When I write things like you are now, I create simple 'persistence' data classes with no default values whatsoever that are mapped to/from my regular domain objects (a sort of abstraction over database). It may generate some overhead at the start - but keeping your domain model not coupled so tightly with the storage model is usually a good idea anyway.