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) }
}
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
}