Pass data from base activity to activity that extends it - android

I have an Activity that extend base Activity like so:
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getInfo("Pojo")
}
}
My base activity does some API access and returns the answer like so:
abstract class BaseActivity : AppCompatActivity() {
companion object {
#JvmStatic var compositeDisposable: CompositeDisposable?=null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
compositeDisposable = CompositeDisposable()
}
override fun onDestroy() {
super.onDestroy()
compositeDisposable?.clear()
}
fun getTvShows(query: String) {
compositeDisposable?.add(
ApiClient.getClient.getTV(Params.getParamsSearch(1, query))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::handleResponseTvShows)
)
}
private fun handleResponseTvShows(result: ObjectsSearchTVShows) {
//Need to send result back to MainActivity or any activity that extends BaseActivity
}
}
I need to send the result back to MainActivity or any activity that extends BaseActivity, how I can achive this?

Make handleResponseTvShows abstract and protected:
protected abstract fun handleResponseTvShows(result: ObjectsSearchTVShows)
and implement it in MainActivity:
override protected fun handleResponseTvShows(result: ObjectsSearchTVShows) {
// process the result here
}

Related

I cant solve this error "lateinit property dataBinding has not been initialized"

I have base activity< T : ViewDataBinding , VM : ViewModel > extends AppCompatActivity()
and i initialize view binding and view model but when run the app i get this error "lateinit property dataBinding has not been initialized"
I don't know what I miss or what the wrong
Below is Base Activity Code
open abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
lateinit var dataBinding : T
lateinit var viewModel : VM
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
dataBinding = getViewBinding()
setContentView(dataBinding.root)
viewModel = generateViewModel()
}
abstract fun getViewBinding(): T
abstract fun generateViewModel(): VM
and this My HomeActivity
class HomeActivity : BaseActivity<ActivityHomeBinding, HomeViewModel>() {
override fun getViewBinding(): ActivityHomeBinding = ActivityHomeBinding.inflate(layoutInflater)
override fun generateViewModel(): HomeViewModel {
return ViewModelProvider(this).get(HomeViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding.vm = viewModel
}
}
this is the error message
Because in your base class the onCreate() with 2 parameters is not going to get called during the activity's lifecycles. And in your subclass you override the onCreate() with a parameter.
Just simple change your base class to override the onCreate() with a parameter to fix the problem. And the other thing is you implement these class the java's way.
You can just make it better this way:
BaseClass
abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
protected abstract val dataBinding : T
protected abstract val viewModel : VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(dataBinding.root)
}
}
Subclass:
class HomeActivity : BaseActivity<ActivityHomeBinding, HomeViewModel>() {
override val viewModel get() = ViewModelProvider(this).get(HomeViewModel::class.java)
override val dataBinding get() = ActivityHomeBinding.inflate(layoutInflater)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding.vm = viewModel
}
}
When you are calling super.onCreate(savedInstanceState) in your HomeActivity, the onCreate from android's Activity is called, but not the onCreate from BaseActivity - because it expected second param persistentState.
So you can do this options to fix the issue:
call super method with 2 params in your HomeActivity
class HomeActivity ... {
...
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
dataBinding.vm = viewModel
}
OR
use onCreate with one param in your BaseActivity
open abstract class BaseActivity<T : ViewDataBinding , VM : ViewModel> : AppCompatActivity() {
lateinit var dataBinding : T
lateinit var viewModel : VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...

How to use interface in Android

I know the concept of "Interfaces" but I've hard time to understand how to use them in android development.
Let's say I created an interface to decide if to show progress bar or not -
interface ProgressBarInterface {
fun showProgressBar()
fun hideProgressBar()
}
And I implement this inside BaseActivity/MainActivity in single Activity app:
class BaseActivity : AppCompatActivity() , ProgressBarInterface {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun showProgressBar() {
}
override fun hideProgressBar() {
}
}
And inside my other activity I've a button, that when I click on it, I want to trigger showProgressBar in the base activity:
button.setOnClickListener {
//Show progress bar
}
How can I interact with the interface to trigger the function inside base activity?
Since you already are implementing the interface in your BaseActivity, you can then just add what you need to do inside the interface methods, and then call them up in any point in your activity, if what you are looking for is to extend this BaseActiviy into more activities you will need to make this BaseActivity abstract then you can extend in each activity this BaseClass and just use the interface methods
abstract class BaseActivity : AppCompatActivity() , ProgressBarInterface {
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun showProgressBar() {
progressBar.visibility = View.VISIBLE
}
override fun hideProgressBar() {
progressBar.visibility = View.GONE
}
}
and then in your Activities you can extend from BaseActivity() and just use your interface methods as you have defined in that BaseActivity() to prevent coding them again, you can do
class FirstActivity : BaseActivity() {
...
button.setOnClickListener {
showProgressBar()
}
An easier way to show and hide the views? Use extension functions
fun View.show() {
this.visibility = View.VISIBLE
}
fun View.hide() {
this.visibility = View.GONE
}
you can define that extensions in any class, for example ViewUtils.kt and then just call
button.setOnClickListener {
progressBar.show()
}
or
button.setOnClickListener {
progressBar.hide()
}

Testing BaseActivity

I have a BaseActivity, which has Dagger behavior inside:
abstract class BaseActivity : DaggerAppCompatActivity(), HasSupportFragmentInjector {
#Inject
lateinit var fragmentDispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState, persistentState)
/**
* Method that gets called after the [onCreate] method
*/
public override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
applyDebugOverlay(this)
}
override fun supportFragmentInjector() = fragmentDispatchingAndroidInjector
}
Now I want to test, that applyDebugOverlay(this) is calling. For this, I use Robolectric in my AbstractActivityTest
#Test
fun testDebugOverlayIsShown() {
underTest.onPostCreate(Bundle())
val buildTimeStamp = underTest.inc_debugOverlay_tv_date.text
assert(buildTimeStamp.isNotEmpty())
}
But I get "No injector factory bound for Class" error, and it is not possible to bind BaseActivity with #ContributesAndroidInjector in my ActivityBuilder Class (would get another error).

Does Kotlin have a way to delegate the implementation of an interface to another class?

I am using one activity and three fragments for my application
Each fragment has an interface that is used to communicate with the logic
the activity implements this interface and calls the logic object (a persistent fragment) with the same arguments, so it looks something like this:
class Child : Fragment() {
private fun userInteraction() {
(activity as? ChildInteraction)?.askStuff()
}
interface ChildInteraction {
fun askStuff():Unit
}
}
class ParentActivity : AppCompatActivity(), ChildInteraction {
override fun askStuff() {
(supportFragmentManager.findFragmentByTag("LOGIC") as? ChildInteraction).askStuff()
}
}
class LogicFragment : Fragment(), ChildInteraction {
override fun askStuff() {
//do some work here
}
}
the thing is, that each interaction has 5-10 methods in it, and all ParentActivity does is pass on the message, is there a way to simplify the passing along?
I know you can't do this in Java but I was hoping there is a way for Kotlin to do it
Take a look on setTargetFragment and getTargetFragment. Here is an example of Fragment-to-Fragment communication:
interface ChildInteraction {
companion object
fun askStuff()
}
fun ChildInteraction.Companion.wrap(f: Fragment) = (f.targetFragment as? ChildInteraction)
class Child1 : Fragment() {
private fun userInteraction() {
ChildInteraction.wrap(this)?.askStuff()
}
}
class Child2 : Fragment() {
private fun userInteraction() {
ChildInteraction.wrap(this)?.askStuff()
}
}
class LogicFragment : Fragment(), ChildInteraction {
override fun askStuff() {
//do some work here
}
}
class ParentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val logic ...
val child1 ...
child1.setTargetFragment(logic, 0)
val child2 ...
child2.setTargetFragment(logic, 0)
}
}
Now I know that this is probably what works for me, so I'll be still accepting the other answer, and because it taught me something I didn't know.
But after some work the way that I handled it was this:
The fragment that attaches looks like this:
class Child : Fragment() {
private var parent: ChildInteraction? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
//this should throw an exception if it is not implemented correctly
parent = (context as LogicProvider).logic!! as Child.ChildInteraction
}
private fun userInteraction() {
parent!!.askStuff()
}
interface ChildInteraction {
fun askStuff():Unit
}
}
Then I have the LogicProvider interface like this:
interface LogicProvider {
val logic: Any?
}
and then the parent will implement logic providers that will pass on the arguments
class ParentActivity : AppCompatActivity(), LogicProvider {
override var logic: Logic? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var frag: Fragment? = supportFragmentManager.findFragmentByTag("parent_logic")
if (frag == null) {
frag = Logic()
supportFragmentManager.beginTransaction().add(frag, "parent_logic").commitNow()
}
logic = frag as Logic
}
override fun onPause() {
super.onPause()
if (isFinishing)
supportFragmentManager.beginTransaction().remove(supportFragmentManager.findFragmentByTag("parent_logic")).commitNow()
}
}
that way the logic fragment is the only one that has to implement interfaces
class Logic : Fragment(), Child.ChildInteraction, Child2.ChildInteraction2 {
override fun askStuff() {
//do stuff here
}
override fun askStuff2() {
//do other stuff here
}
}

kodein, inject data to simple class

i'm new on kotlin and kodein development.
I want to inject data to a simple class which extends nothing.
I have my MainActivity which extends KodeinAppCompatActivity(),
my fragment which extends KodeinSupportFragment() calls a function from my simple class CallType. But this function must to change a boolean from an other simple class ConnectivitySate. I don't want to use static value.
Below, my code :
class App : Application(), KodeinAware {
override val kodein by Kodein.lazy {
import(autoAndroidModule(this#App))
bind<CallType>() with instance(CallType())
bind<ConnectivityState>() with instance(ConnectivityState())
bind<ContactData>() with instance(ContactData())
}
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(androidActivityScope.lifecycleManager)
}
MainActivity :
class MainActivity : KodeinAppCompatActivity() {
My Fragment :
class JournalFragment : KodeinSupportFragment(){
private val callType: CallType by instance()
#SuppressLint("MissingSuperCall")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initializeInjector()
}
override fun onCreateView(inflater: LayoutInflater?, container:
ViewGroup?,savedInstanceState: Bundle?): View? {
// !! CALL MY FUNCTION !!
callType.call(callType.callNumber)
}
....
#SuppressLint("MissingSuperCall")
override fun onDestroy() {
super.onDestroy()
destroyInjector()
}
My simple class :
class CallType {
fun call(number: String) {
// !! I want to change gsmState value from ConnectivityState class
connectivityState.gsmState = true
}
My ConnectivityState class :
class ConnectivityState {
var gsmState = false
}
It is an example among many others, because in lots of situations, i'm blocked like that. I have try lots of things but i always have like error : value not injected
Thank you very much for your reply..
When you call super.onCreate(), it calls onCreateView, so the line callType.call(callType.callNumber) is called before initializeInjector().
Note that you should always call initializeInjector() before calling super.onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
initializeInjector()
super.onCreate(savedInstanceState)
}

Categories

Resources