Home > Blockchain >  Kotlin: passing function as argument got Type mismatch
Kotlin: passing function as argument got Type mismatch

Time:06-15

While migrating my java Android app to Kotlin I've come across with the next issue:

I have a generic class used to run background tasks as follows:

class TaskRunner {
    private val executor: Executor =
        Executors.newSingleThreadExecutor() 
    private val handler = Handler(Looper.getMainLooper())

    interface Callback<R> {
        fun onComplete(result: R)
    }

    fun <R> executeAsync(callable: Callable<R>, callback: Callback<R>) {
        executor.execute {
            val result: R
            try {
                result = callable.call()
                handler.post { callback.onComplete(result) }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

And then I'm doing a TaskRunner call as follows (have to say it was working fine in java):

override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
    val appSettings = instance!!
    val outputPath = appSettings.outputPathCache
    //
    val taskRunner = TaskRunner()
    taskRunner.executeAsync(
        ScreenshotSaver(
            arrayBitmap,
            outputPath!!,
            ""
        )
    ) { ipr: ImageProcessingResult -> this::onImageProcessingFinished(ipr) }
}

This is the interface:

interface IImageListeners {
    fun onImageProcessingFinished(ipr: ImageProcessingResult)
}

And the method:

@Override
private fun onImageProcessingFinished(ipr: ImageProcessingResult) {
    val uris = ArrayList<Uri?>()
    for (file in ipr.screenShotFiles) {
        val uri = FileProvider.getUriForFile(this, "$packageName.GenericFileProvider", file)
        if (uri!=null) uris.add(uri)
    }
    AWDrawerMenu.listener = this
    AWDrawerMenu.activity = WeakReference(this)
    AWDrawerMenu.shareFile(uris, "image/jpg")
}

Previously, in my first attempt, I was having a "Type Mismatch" compilation error, and now, after some modifications (like ::) the error is: Overload resolution ambiguity. All these functions match.

public abstract fun onImageProcessingFinished(ipr: com.xxx.xxx.activities.main.ImageProcessingResult): Unit defined in com.xxx.xxx.activities.shared.BaseActivity

private final fun onImageProcessingFinished(ipr: com.xxx.xxx.activities.thought.result.ImageProcessingResult): Unit defined in com.xxx.xxx.activities.shared.BaseActivity

I'm stuck, and I don't know hot to pass a function as argument to a method in Kotlin.

Edit 1:

The only abstract classic my solution:

abstract class BaseActivity : AppCompatActivity(), ICommonActivityMethods, IActionListeners,
    IImageListeners, IWaitingDialog {
    var appSettings: AppSettings? = null
    var dialog: AlertDialog? = null
    var sharedPreferences: SharedPreferences? = null
    var screen: AWScreen? = null
    private var noInternetDialog: AWDialog? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE)
        setContext(this)
        setLocale()
        setDrawer()
        if (className() == "DisplayThoughtActivity") {
            checkInternetConnection()
        } else {
            startActivity()
        }
    }

    private fun className(): String {
        return this.javaClass.simpleName
    }

    private fun setDrawer() {
        setDrawer(this)
        setupDrawer(this)
    }

    private fun checkInternetConnection() {
        if (!AWHttp.isOnline()) {
            showNoInternetDialog()
        } else {
            startActivity()
        }
    }

    private fun showNoInternetDialog() {
        val title = AWLocale.getStringResourceByName("requestpermission_title")
        val message = AWLocale.getStringResourceByName("activity_displaythought_notonline")
        val positiveButtonText = AWLocale.getStringResourceByName("customalert_accept")
        val negativeButtonText = AWLocale.getStringResourceByName("customalert_cancel")
        if (noInternetDialog == null) {
            noInternetDialog = AWDialog(this)
            val rAccept = Runnable { checkInternetConnection() }
            val rCancel = Runnable {
                noInternetDialog!!.dismiss()
                startActivity()
            }
            noInternetDialog!!.title = title
            noInternetDialog!!.message = message
            noInternetDialog!!.closeOnAccept = true
            noInternetDialog!!.textColor = Color.parseColor("#ffffff")
            noInternetDialog!!.positiveButtonText = positiveButtonText
            noInternetDialog!!.positiveRunnable = rAccept
            noInternetDialog!!.negativeButtonText = negativeButtonText
            noInternetDialog!!.negativeRunnable = rCancel
            noInternetDialog!!.dismiss = false
            noInternetDialog!!.createYesNoDialog()
        }
    }

    override fun onBackPressed() {
        super.onBackPressed()
        AWAppearance.doTransitionOut(this)
    }

    override fun onResume() {
        super.onResume()
        setContext(this)
    }

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(updateBaseContextLocale(base))
    }

    override fun setLocale() {
        val currLang = AWLocale.locale
        AWLocale.locale = currLang
    }

    override fun startActivity() {
        if (dialog != null) dialog!!.dismiss()
        if (noInternetDialog != null) noInternetDialog!!.dismiss()
        AWDrawerMenu.listener = this
        setActivityLayout()
    }

    override fun setActivityLayout() {
        setContentView()
        setClassVariables()
        setActivityBackground()
        createActionBar()
        resizeActionBarBackButton(this)
    }

    override fun setClassVariables() {
        setContext(this)
        appSettings = instance
        dialog = AWDialog.createSpotsWaitDialog(this, R.style.CustomSpotsDialogLight)
        appSettings!!.dialog = dialog
        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
        screen = AWScreen(this)
    }

    private fun updateBaseContextLocale(context: Context): Context {
        val language = AWLocale.locale
        val locale = Locale(language)
        Locale.setDefault(locale)
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            updateResourcesLocale(context, locale)
        } else updateResourcesLocaleLegacy(context, locale)
    }

    fun hideKeyboard(editText: EditText) {
        val imm = this.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
        Objects.requireNonNull(imm).hideSoftInputFromWindow(
            editText.windowToken,
            InputMethodManager.RESULT_UNCHANGED_SHOWN
        )
    }

    @TargetApi(Build.VERSION_CODES.N)
    private fun updateResourcesLocale(context: Context, locale: Locale): Context {
        val configuration = context.resources.configuration
        configuration.setLocale(locale)
        return context.createConfigurationContext(configuration)
    }

    private fun updateResourcesLocaleLegacy(context: Context, locale: Locale): Context {
        val resources = context.resources
        val configuration = resources.configuration
        configuration.locale = locale
        resources.updateConfiguration(configuration, resources.displayMetrics)
        return context
    }

    override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
        val appSettings = instance!!
        val outputPath = appSettings.outputPathCache
        //
        val taskRunner = TaskRunner()
        taskRunner.executeAsync(
            ScreenshotSaver(
                arrayBitmap,
                outputPath!!,
                ""
            )
        ) { ipr: ImageProcessingResult -> this::onImageProcessingFinished(ipr) }
    }

    @Override
    private fun onImageProcessingFinished(ipr: ImageProcessingResult) {
        val uris = ArrayList<Uri?>()
        for (file in ipr.screenShotFiles) {
            val uri = FileProvider.getUriForFile(this, "$packageName.GenericFileProvider", file)
            if (uri!=null) uris.add(uri)
        }
        AWDrawerMenu.listener = this
        AWDrawerMenu.activity = WeakReference(this)
        AWDrawerMenu.shareFile(uris, "image/jpg")
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
    }

    override fun showWorkingDialog(message: String?) {
        WaitingDialog.context = WeakReference(this)
        WaitingDialog.setInstance(message)
    }

    override fun clearWorkingDialog() {
        if (WaitingDialog.instance != null) {
            WaitingDialog.instance!!.dismiss()
        }
    }

    companion object {
        @JvmStatic
        fun resizeActionBarBackButton(activity: Activity) {
            val tvTitle = activity.findViewById<AutoResizeTextView>(R.id.tvActionBarTitle)
            val treeObserver = tvTitle.viewTreeObserver
            treeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    tvTitle.viewTreeObserver
                        .removeOnGlobalLayoutListener(this)
                    val newImgBackHeight = tvTitle.height
                    val imgBack = activity.findViewById<ImageView>(R.id.imgBack)
                    val treeObserverImgBack = imgBack.viewTreeObserver
                    treeObserverImgBack.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
                        override fun onGlobalLayout() {
                            imgBack.viewTreeObserver
                                .removeOnGlobalLayoutListener(this)
                            val reduction = AWScreen.dp2px(10)
                            imgBack.layoutParams.height = newImgBackHeight - reduction
                            imgBack.requestLayout()
                        }
                    })
                }
            })
        }
    }
}

Edit 2:

I changed the problematic fun to:

override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
    val appSettings = instance!!
    val outputPath = appSettings.outputPathCache
    //
    val taskRunner = TaskRunner()
    taskRunner.executeAsync(
        ScreenshotSaver(
            arrayBitmap,
            outputPath!!,
            ""
        )
    ) { ipr: String -> (this::onImageProcessingFinished)(ipr) }
}

enter image description here

CodePudding user response:

Apparently I've found the issue, that's why I'll answer my own question. First of all below there is the correct syntax (at least no more compilation problems).

I suppose ScreenshotSaver was returning a different object type than the expected by onImageProcessingFinished.

I had two different ImageProcessingResult result classes in different packages, and now set only one for all.

Still need to test it, but I guess this was the key.

override fun onShareThisApp(arrayBitmap: ArrayList<Bitmap?>?) {
    val appSettings = instance!!
    val outputPath = appSettings.outputPathCache
    val taskRunner = TaskRunner()
    assert(outputPath != null)
    taskRunner.executeAsync(
        ScreenshotSaver(arrayBitmap, outputPath!!, null)
    ) { ipr -> onImageProcessingFinished(ipr) }
}

override fun onImageProcessingFinished(ipr: ImageProcessingResult) {
    val uris = ArrayList<Uri?>()
    for (file in ipr.screenShotFiles) {
        val uri = FileProvider.getUriForFile(this, "$packageName.GenericFileProvider", file)
        if (uri!=null) uris.add(uri)
    }
    AWDrawerMenu.listener = this
    AWDrawerMenu.activity = WeakReference(this)
    AWDrawerMenu.shareFile(uris, "image/jpg")
}

CodePudding user response:

It's described in error message. Based on your code base, you should pass a callback as the second attribute to executeAsync method because you are using callback pattern. Otherwise you can use inline function instead of it if you change your executeAsync method signature and pass a lambda function instead of callback. So please follow one of the below approaches:

1.

fun <R> executeAsync(callable:  
 Callable<R>, callback: Callback<R>) {
   //Do what you want
   callback.onComplete(result)
}

And while you want to call it from outside of the class

taskRunner.executeAsync(
    ScreenshotSaver(
        arrayBitmap,
        outputPath!!,
        ""
    ),
    object: Callback<R> {
      override fun onComplete(result: 
       R) {
        //Do what you want with result
      }
    }
)
fun <R> executeAsync(callable: Callable<R>, callback: (String)->Unit) {
   //Do what you want
   callback.invoke(result)
}

And while you want to call it from outside of the class

taskRunner.executeAsync(
    ScreenshotSaver(
        arrayBitmap,
        outputPath!!,
        ""
    ) ) {result -> 
      //Do what you want with result
    }
  • Related