Navigating to Fragment from callback

Hi All,

I’m new to Kotlin, and android dev in general. I have developed a web app that has a local DB and login credentials. My app can successfully post to that web app to verify login, but when I try to switch fragments after that, I get an error “Method setCurrentState must be called on the main thread”

See my onViewCreated of my FirstFragment below. Note that if I put the findNavController() line directly into the button listener, it works. It just fails within the callback.

Thank you!

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //urlButton listener
        binding.loginButton.setOnClickListener() {

            //on button click
            val url = "https://redacted/mobile/login"
            Log.i("Testing", "Attempting Client Connection")

            val formBody: RequestBody = FormBody.Builder()
                .add("username", binding.usernameInput.text.toString())
                .add("password", binding.passwordInput.text.toString())
                .build()

            val okHttpClient = OkHttpClient()
            val request = Request.Builder()
                .post(formBody)
                .url(url)
                .build()
            Log.i("Testing", url.toString())


            okHttpClient.newCall(request).enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    // Handle this
                    Log.i("Failed", e.toString())
                }

                override fun onResponse(call: Call, response: Response) {
                    // Handle this
                    val body = response.body?.string();
                    if (body != null) {
                        if (body.contains("Login successful", ignoreCase = true)) {
                            binding.apply {
                                findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
                            }
                        }
                    }
                }
            })
        }

I’ve managed to get a little further with await() from GitHub - gildor/kotlin-coroutines-okhttp: Kotlin Coroutines await() extension for OkHttp Call but I have reached a strange roadblock. I don’t seem to see any response body when using awaits client builder.

val result = client.newCall(request).await()
        println(result.request)
        println(result.networkResponse)
        println(result.priorResponse)
        println(result.cacheResponse)
        println(result.headers)
        println(result.body.toString())
        println(result.body?.string())
        println(result)
        println("${result.code}: ${result.message}, ${result.body}")

None of which shows the body that I returned from the call. Only the http code and the request data and OK message. EDIT: When using okhttp3’s base client and running the code, I can print out the body that Im looking for, but I need the await for synchronization.

Thanks.

From that library’s readme:

This library doesn’t provide any facilities for non-blocking read of response body, if you need a way to do that consider to use withContext(Dispatchers.IO) and wrap blocking calls.

You probably want to do something like:

val response = withContext(Dispatchers.IO) {
    client.newCall(request).execute()
}

Finally got it figured out after taking a step back to the basics. This was what I needed:

     override fun onResponse(call: Call, response: Response) {
                // Handle this
                val body = response.body?.string()
                if (body != null) {
                    if (body.contains("Login successful", ignoreCase = true)) {
                        activity?.runOnUiThread( Runnable() {
                            toSecondFragment()
                        })
                    }
                }
            }
        })
        return null
    }

    fun toSecondFragment(): Void? {
        binding.apply {
            findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
        }
        return null
    }

Mainly the

activity?.runOnUiThread( Runnable() {
toSecondFragment()
})