Reuse methods in Kotlin, Android - android

I need to open one activity from several different points in the app. Let's say from Settings fragment, Main Activity and Navigation drawer (fragment). I don't want to copy/paste the same method and the method is very specific, it should be exactly the same (because it registeres Firebase events). How to structure the code in effective way? Where to put this method? One idea is to have a global ActivityUtils.kt file with just methods and it would be used to store these methods. I'm interested in the alternatives and what are pros and cons of each.

I would create a companion object in the Activity you need to open:
class YourActivity : AppCompatActivity() {
companion object {
fun start(ctx: Context) {
// put your logic here (registering of Firebase events)
val i = Intent(ctx, YourActivity::class.java)
ctx.startActivity(i)
}
}
}
And call it from another activity:
YourActivity.start(this)
or from another fragment:
YourActivity.start(context)

Use an extension method:
fun Activity.doMyStuff() {}
That can be called from any class extending Activity:
doMyStuff()
Extension functions like this shouldn't go inside a class, but rather inside a file. So if you were to make an ActivityUtils.kt file, don't have any sort of class ActivityUtils {} stuff in it. The function(s) should just go directly into the file.

Why not to use MVP?
Like,
interface IView {
val context: Context
}
interface IPresenter {
fun launchActivity(view: IView)
}
class MyActivityModel
{
var key = "key"
/*some other data*/
fun getParcelableObject(): Parcelable
{
return /*some parcelable from model data*/
}
}
class MyActivity : AppCompatActivity(), IView
{
override val context: Context
get() = this
}
class MyActivityPresenter() : IPresenter
{
private var model: MyActivityModel = MyActivityModel()
override fun launchActivity(view: IView)
{
val intent = Intent(view.context, MyActivity::class.java)
intent.putExtra(model.key, model.getParcelableObject())
view.context.startActivity(intent)
}
fun setSomeDataToModel(someData: Any) {
}
}
/*Everyone who wants to use presenter, must be a Context and implement IView*/
fun use()//in some fragment, or activity implementing IView
{
MyActivityPresenter().launchActivity(this)
//or
val presenter = MyActivityPresenter()
presenter.setSomeDataToModel("some data")
presenter.launchActivity(this)
}

Related

How to bind external library class with callback in Hilt?

I'm a beginner in Hilt. I have a library which takes in an interface. The library does some operation and invokes the interface callback. I have an activity which invokes this library by passing the interface implementation. I'd like to know how to inject this using Hilt.
Interface in library
interface InterfaceInLibrary() {
fun callback1()
fun callback2(/*params */)
}
Activity
class MyActivity: InterfaceInLibrary() {
override fun onCreate(savedInstanceState: Bundle?) {
//library initialization
val myLibraryClass = MyLibraryClass.getInstance(this) //passing the InterfaceInLibrary implementation
}
override fun callback1() {
Toast.makeText(this, "callback1", Toast.LENGTH_LONG).show()
}
override fun callback2() {
Toast.makeText(this, "callback2", Toast.LENGTH_LONG).show()
}
}
I would like to know how to inject MyLibraryClass in MyActivity using Hilt.
The only possible way I know (or at least how I am handling this use-case in my projects) is to field inject the concrete class that invokes the interface and then let the activity implement the concrete class and inherit from the callback. Since your interface and your concrete class look kinda weird, I will provide a full implementation here. Let's assume we have the following interface:
Interface
interface IMyCallbackInterface {
fun callbackWithoutParameters()
fun callbackWithParameters(value: String)
}
Then, you need some class to invoke this callback. In my case, this was always a recylerview.adapter, but we will use somethin easier:
Invoking class
class MyInvokingClass #Inject constructor() {
// this interface will be initialized by our activity
private lateinit var callbackListener: IMyCallbackInterface
// This function invokes the first callback
fun someFunctionThatInvokesCallbackWithoutParameters() {
// do some stuff
callbackListener.callbackWithoutParameters()
}
// This function invokes the second callback
fun someFunctionThatInvokesCallbackWithParameters() {
// do some stuff
callbackListener.callbackWithParameters(value = "Hello")
}
// This will be called from our activity to initialize the callback
fun initializeCallback(callbackOwner: IMyCallbackInterface) {
this.callbackListener = callbackOwner
}
}
Then, you need to field inject the class and inherit from the callback inside your activity
Activity or Fragment
class MyActivity : IMyCallbackInterface {
#Inject lateinit var invokingClass: MyInvokingClass
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate()
setContentView(...)
// Since MyActivity implements the interface
// it is an instance of it. So you can simply
// say, that the "owner" of the callback is the activity
invokingClass.initializeCallback(this#MyActivity)
}
override fun callbackWithoutParameters() {
// do some stuff
}
override fun callbackWithParameters(value: String) {
// do some stuff with string
}
}
Because our Activity inherits from the callback and we said in onCreate() that the interfaceOwner of MyInvokingClass is the activity, every time the callback gets invoked, the interface functions inside the activity will be invoked as well.

How to call activity type extension function from view model using data binding?

I have created a extension function as below:
fun <A : Activity> Activity.startNewActivity(activity: Class<A>) {
Intent(this, activity).also {
it.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(it)
}
}
I want to call this from my ViewModel class but since ViewModel is not an activity is there any way to achieve this?
In my ViewModel class I want to call this extension function inside the below method which is trigger when a button is clicked:
fun onLoginClicked(view: View) {
// I want to call that startNewActivity function here..
}
What are the possible ways? I could not figure it out.
I think you should handle click actions which depends on its context in Activity itself not in ViewModel.
If you want to do it in ViewModel you need to have access to Activity's reference which you can also get it from View or you can just pass it as method argument.
fun onLoginClicked(view: View) {
(view.context as Activity).startNewActivity(MainActivity::class.java)
}
You may want to use livedata for this kind of purposes, communication between viewmodel and view classes.
In your viewmodel:
val onLoginClicked = MutableLiveData<Boolean>()
...
fun onLoginClicked(view: View) {
onLoginClicked.value = true
}
In your related activity:
vm.onLoginClicked.observe(this, Observer { startNewActivity(...) })

What is the difference between passing data through constructor and arguments of DialogFragment?

I have a simple BottomSheetDialogFragment:
class MyBottomSheetDialog : BottomSheetDialogFragment() {
companion object {
private const val SOME_KEY = "some_key"
fun newInstance(something: Boolean): MyBottomSheetDialog {
return MyBottomSheetDialog().apply {
arguments = bundleOf(SOME_KEY to something)
}
}
}
...
...
}
Which I then display with:
MyBottomSheetDialog.newInstance(false).show(childFragmentManager, "my_dialog")
What is the advantage of this typical approach to just using a constructor parameter and displaying the dialog like this:
class MyBottomSheetDialog(private val something: Boolean) : BottomSheetDialogFragment() {
...
...
}
MyBottomSheetDialog(false).show(childFragmentManager, "my_dialog")
Simple thing is if your class undergoes for a configuration changes then system looks for default constructor to recreate the class i.e without parameters at that you will get Exception.

In Kotlin, how does a constructor with anonymous class work?

Consider a functionality F, which depends on Android lifecycle methods. I have implemented this functionality in an Activity A. Any other activity which wants to implement this functionality can simply extend A. The results are sent back to the child activity via an interface. Example:
// interface
interface ACallbacks {
fun onResult(string: String)
}
// Activity A
open class AActivity
(private val aCallbacks: ACallbacks): AppCompatActivity() {
// functionality F, which depends on Android lifecycle methods
}
// Activity B
class BActivity: AActivity(object: ACallbacks {
override fun onResult(string: String) {
// Q: how to use string in BActivity?
}
}) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_b)
}
}
How to access the result from Activity A (i.e., string) in Activity B. For example, how do I set it in a textView present in Activity B?
If BActivity inherits from AActivity - inside BActivity you can get what you want:
override fun someFun(): String {
val parentResult = super.someFun()

Good practice to implement viewmodel

Does anyone know good practice to implement viewmodel logic? Event based or action based?
class EventBasedVM : ViewModel() {
fun onResume() {
fetchInformation1()
fetchInformation2()
}
}
class ActionBasedVM : ViewModel() {
fun fetchInformation1() {
}
fun fetchInformation2() {
}
}
Although both approaches you mentioned make sense for specific use cases, I would add another one to the list:
class InitialisationBasedVM : ViewModel() {
val informationLiveData = MutableLiveData<String>()
init {
fetchInformation()
}
private fun fetchInformation() {
// call you async code and eventually post the value to the observers
informationLiveData.postValue("whatever")
}
}
The approach of fetching data in the ViewModel constructor make sure data are not fetched again in case of configuration changes. You could also make the fetchInformation() method public and invoke from the View upon certain actions that require reloading the data (i.e pull to refresh).

Categories

Resources