Instantiate instance with a callback composed of events - android

I am currently implementing an app that uses a printer from Hoin. They provided an SDK that I can integrate in Android Studio (done) and tell me to instantiate the instance like this:
val mHoinPrinter = HoinPrinter.getInstance(context, mode, callback)
For context and mode, no biggies. For the callback I am having troubles.
In their documentation they specify that the callback is of interface PrinterCallback which is composed like so:
Public interface PrinterCallback {
     Public void onState(int state); //state callback, parameter is status code, refer to status code definition
     Public void onError(int errorCode); //Error callback, parameter is error code, refer to error code definition
     Public void onEvent(PrinterEvent event); //Event callback, parameter is PrinterEvent event, refer to PrinterEvent definition
}
Which is great because it gives me 4 events that should be triggered at some point.
My issue is that I have no clue how to actually instantiate it. I know that I need to do it like so val mHoinPrinter = HoinPrinter.getInstance(this.context, 2, callback) but where I have troubles is in defining the callback. Should I write a function? should I setup something extra? How can I define logics for this callback?
It's very confusing when reading the docs online.

You have to create an implementation of the PrinterCallback interface and then pass that implementation as a parameter to getInstance method. The implementation can be created as so
val callback = object: PrinterCallback{
override fun onState(state: Int) {
// DO SOMETHING
}
override fun onError(errorCode: Int) {
// DO SOMETHING
}
override fun onEvent(event: Int) {
// DO SOMETHING
}
}
If you do everything else as specified in the documentation then these three functions will be invoked on their respective events

Related

Is it possible to call a non-final function in constructor?

I'm developing a huge section of my Android app in Jetpack Compose with the MVVM pattern.
I have a ViewModel father that is extended by all the other ViewModels. There, I have defined an open function which contains the initialization logic of each ViewModel that I need to call every time I enter in a new screen and to call again when something went wrong and the user clicks on the "try again" button.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
fun onTryAgainClick() {
myInitMethod()
}
}
class MyScreen1ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
class MyScreen2ViewModel(): MyFatherViewModel() {
init {
myInitMethod()
}
override fun myInitMethod() {
super.myInitMethod()
// Do something
}
}
Is there a way I can call this method in the init function of MyFatherViewModel instead of doing it in all the children ViewModels? If I try to do that, it gives me the "Calling non-final function in constructor" warning and, of course, it doesn't work.
abstract class MyFatherViewModel(): ViewModel() {
open fun myInitMethod() {}
init {
myInitMethod()
}
fun onTryAgainClick() {
myInitMethod()
}
}
Is it possible to call a non-final function in constructor?
Technically yes, but you shouldn't. Kotlin is trying to protect you from problems here. If you call an open function from a constructor, it means you are running code from the child class before the parent class is completely initialized, and before the child class even started initializing. If the child implementation of the open function tries to access properties from the child class, unexpected things may happen. For instance, non-nullable properties could yield null (because not initialized yet), or primitive values could yield their type's default instead of the default value from their initializer:
fun main() {
Child()
}
open class Parent {
init {
initialize()
}
val id = 42
open fun initialize() = println("parent init")
}
class Child : Parent() {
val name = "Bob"
override fun initialize() = println("initializing $name, parent id=$id")
}
This prints the following output:
initializing null, parent id=0
I guess you can see why this is dangerous.
Maybe you should reconsider what you're trying to do with this try-again feature. Maybe a new view model should be instantiated instead (if try-again is to handle crashes, the state of the current view model may actually be bad enough to want to re-create it from scratch anyway).

Create generic higher order function in kotlin

I have an interface GalleryImagesDataCallback which I use to pass data from background thread to UI thread, to avoid calling runOnUiThread() from each overriden method of GalleryImagesDataCallback, I have used kotlin higher order function.
interface GalleryImagesDataCallback {
fun fetchedList(list: ArrayList<ImageGalleryItemModel>)
#JvmDefault
fun callMethodOnUIThreadForFetch(mContext: Context, list:ArrayList<ImageGalleryItemModel>,func: (ArrayList<ImageGalleryItemModel>) -> Unit) {
(mContext as BaseActivity).runOnUiThread {
Logger.error("TEST_ABC","callMethodOnUIThreadForFetch Enter")
func(list)
}
}
fun deleteList()
#JvmDefault
fun callMethodOnUIThreadForDelete(mContext: Context, func: () -> Unit) {
(mContext as BaseActivity).runOnUiThread {
Logger.error("TEST_ABC","callMethodOnUIThreadForDelete Enter")
func()
}
}
}
Calling from background thread:
callback.callMethodOnUIThreadForFetch(mContext,list) {list:ArrayList<ImageGalleryItemModel> -> callback.fetchedList(list)} // callback is reference of GalleryImagesDataCallback
callback.callMethodOnUIThreadForDelete(mContext) {callback.deleteList()}
Problem :
Right now I'm having 2 separate methods callMethodOnUIThreadForDelete() and callMethodOnUIThreadForFetch(). Is there any way in kotlin to create one generic method (say callMethodOnUIThread()) which I can
use to call deleteList() and fetchedList() both with no change in function definition?
First to answer your literal question, your callMethodOnUIThreadForFetch function has unnecessary redirection of the list argument. Why make the list an argument of the higher-order function only to pass it right back to the function argument? You could use your callMethodOnUIThreadForDelete function for either purpose, but suppose we rename it and remove the unsafe cast to Activity by using a Handler:
// In interface:
fun callMethodOnUIThread(context: Context, func: () -> Unit) {
Handler(context.mainLooper).post(func)
}
// From background thread:
callback.callMethodOnUIThread(mContext) { callback.fetchedList(list) }
callback.callMethodOnUIThread(mContext) { callback.deleteList() }
Assuming that you want to simplify the work of implementing this interface, then I don't think this actually helps. You've pushed the work of calling code on the UI thread from the interface implementation into the user of the interface. You may as well create a global helper function, instead of cluttering your interface, which is a weird place for it. Usage becomes more straightforward:
// Global utility function, not in a class
fun Context.onUiThread(func: () -> Unit) {
Handler(mainLooper).post(func)
}
// Usage:
mContext.onUiThread { callback.fetchedList(list) }
mContext.onUiThread { callback.deleteList() }
If you really want to fully encapsulate the thread switching, you would have to change your interface into an abstract class like this:
abstract class GalleryImagesDataCallback {
protected abstract fun fetchedListImpl(list: List<String>)
protected abstract fun deleteListImpl()
fun fetchedList(context: Context, list: List<String>) {
Handler(context.mainLooper).post { fetchListImpl(list) }
}
fun deleteList(context: Context) {
Handler(context.mainLooper).post { deleteListImpl() }
}
}
So instead of implementing the interface, you would create subclasses of this and implement the two abstract functions. Neither the subclass or the user of the callback has to be concerned with ensuring code is called on the UI thread.

Remove "this" callback in kotlin

I'm a bit kotlin newbie and I'm trying to remove the callback instance inside the callback itself.
What I'm trying to achieve it's something similar to the following code.
private val myCallback = SomeInterfaceType {
if(it.something) {
someObject.removeListener(this#SomeInterfaceType)
}
}
Of course it doesn't compile or else I wouldn't be asking here. So I ask, how to remove the callback from inside the instance of the interface?
edit:
the error is "inferred type is X but Y was expected.
edit 2: I just realized I've asked the wrong question, it's similar to it but not exactly a Interface.
The object I'm using have the following constructor/interface
public open class Watcher<T> public constructor(call: (T) -> kotlin.Unit)
so in reality I'm trying to reference the Watcher from inside the call: (T) -> kotlin.Unit to remove the listener.
Is that possible?
You need to use a full object expression syntax to refer to be able to refer to the instance itself:
private val myCallback = object: SomeInterfaceType() {
override fun onSomeEvent() {
if (it.something) {
someObject.removeListener(this)
}
}
}
There's also a workaround: wrap the reference to myCallback into a lambda passed to a function that calls it (e.g. run { ... }):
private val myCallback: SomeInterfaceType = SomeInterfaceType {
if (it.something) {
someObject.removeListener(run { myCallback })
}
}

How to test a class with anonymous listener that is set inside a function?

I am just getting into unit testing on Android and I ran into some difficulties when I tried to test bindTo() function of this class.
class DataFlow<T> (produce: DataFlowProducer<T>): BaseDataFlow<T>(produce) {
var updateOnAttach: Boolean = true
fun bindTo(viewKontroller: ViewKontroller, updateImmediately: Boolean, updateUi: (data: T) -> Unit) {
this.updateUi = updateUi
if (updateImmediately)
flow()
viewKontroller.addLifecycleListener(object : Controller.LifecycleListener() {
override fun postAttach(controller: Controller, view: View) {
if (updateOnAttach) flow()
}
override fun preDestroyView(controller: Controller, view: View) {
viewKontroller.removeLifecycleListener(this)
this#DataFlow.updateUi = null
}
})
}
}
If I mock my ViewKontroller test still crashes with NPE on line viewKontroller.addLifecycleListener.
So what am I doing wrong?
What you want to check in the test is probably at least this:
LifecycleListener added to ViewKontroller
When onPostAttach is called by ViewKontroller something happens
When preDestroyView is called by ViewKontroller something else happens
So, the test double of ViewKontroller that you pass into constructor needs to "tell" you whether a listener was registered, and also to delegate method calls to that listener.
In such cases, when the test double object needs to have some actual functionality, it is best to implement a fake than using a mock.
In your case, just implement FakeViewKontroller that extends ViewKontroller and pass it to system under test instead of mock.
In that class you can expose additional methods that will allow you to ensure that LifecycleListener was added, and call methods on that listener in test cases.

Kotlin Android Button.onClickListener causing NoSuchMethodError

I think I've found a quirk of using kotlin for android, or there's some gap in my understanding of the syntax.
Trying to set an onClickListener for a button is throwing a NoSuchMethodError
Here's the code at fault
button.setOnClickListener(Button.OnClickListener {
fun onClick(view: View){
val intent : Intent = Intent(this,DetailActivity::class.java)
if(obj is String) {
intent.putExtra("Topic", obj)
}
startActivity(intent)
}
})
And here's the stacktrace outputted
java.lang.NoSuchMethodError: No static method OnClickListener(Lkotlin/jvm/functions/Function1;)Landroid/view/View$OnClickListener; in class Landroid/widget/Button; or its super classes (declaration of 'android.widget.Button' appears in /system/framework/framework.jar:classes2.dex)
Anyone know whats up?
Interestingly, I don't get that error, your code compiles for me. However, it won't work for a different reason: you're passing in a lambda as the listener inside the {}, which means that the contents of it will be executed when the click event happens. There is no code to run inside it though, you're just defining a local function named onClick that will never be called.
button.setOnClickListener(Button.OnClickListener {
fun onClick(view: View){
...
}
Log.d("TAG", "hi") // this is the code that would be executed on click events
})
There are two ways to fix your syntax:
First, you can use an object expression to create the listener, this is pretty close to what you wrote, and is along the lines of the classic Java solution, it explicitly creates an anonymous class (note that the OnClickListener interface is actually under the View class):
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
val intent = ...
}
})
Or you can use the shorter, more Kotlin-like syntax that the IDE will suggest when you try using the previous long form anyway, by making use of SAM conversion:
button.setOnClickListener {
val intent = ...
}
This solution uses a lambda just like your initial code did, it just doesn't name what interface it converts to explicitly, and drops the () which are not required a single lambda parameter.
try
button.setOnClickListener {
// Handler code here
}
You can try :
// case 1
button?.setOnClickListener { view ->
// handler here
}
// case 2
button?.setOnClickListener {
// you can use keyword 'it' for use member view
// handler here
}

Categories

Resources