In this example I will upload pictures directly from builtIn gallery using this example.

Prepare virtual device like I already explained here.

Controller looks same as here.

In AndroidManifest.xml I need only retrofit permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Api service:
import com.google.gson.annotations.SerializedName
import okhttp3.MultipartBody
import retrofit2.Call
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part

interface IWebApiService {
    @Multipart
    @POST("api/UploadPictures/UploadImage")
    fun uploadImage(
        @Part image: MultipartBody.Part?
    ): Call<UploadResponse>
}

data class UploadResponse(
    @SerializedName("message")
    val message: String
)
This time I will create Retrofit using dependency injection:
import android.util.Log
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager

internal class CreateRetrofitBuilder : ICreateRetrofitBuilder {
    override fun createRetrofitBuilder(baseUrl: String): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(trustAllCertificates())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    private fun trustAllCertificates(): OkHttpClient {
        val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
            override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
                Log.i(MainActivity::class.simpleName, "checkClientTrusted")
            }

            override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
                Log.i(MainActivity::class.simpleName, "checkServerTrusted")
            }

            override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
        })
        val sslContext = SSLContext.getInstance("SSL")
        sslContext.init(null, trustAllCerts, java.security.SecureRandom())

// Create an ssl socket factory with our all-trusting manager
        val sslSocketFactory = sslContext.socketFactory

// connect to server
        return OkHttpClient.Builder()
            .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
            .hostnameVerifier { _, _ -> true }.build()
    }
}
Here notice that I am using "GsonConverterFactory" which means that Retrofit expects JSON anwer from the server, which is why I need UploadResponse class, and from server I will respond with:
return Ok(new { message = "Image uploaded successfully." });
Otherwise I would receive error like:

com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $

Where ICreateRetrofitBuilder looks like:

import retrofit2.Retrofit

interface ICreateRetrofitBuilder {
    fun createRetrofitBuilder(baseUrl: String): Retrofit
}
onResponse and onFailure I will also inject and use the like call backs:
import android.app.AlertDialog
import retrofit2.Call
import retrofit2.Response

class UploadImageRetrofitCallBacks(private val alertDialogBuilder: AlertDialog.Builder) :
    IUploadImageRetrofitCallBacks {

    override fun onResponse(call: Call<UploadResponse>, response: Response<UploadResponse>) {
        if (!response.isSuccessful) {
            alertDialogBuilder.setMessage(response.errorBody()!!.charStream().readText())
                .setCancelable(false)
                .setNeutralButton("OK") { dialog, _ ->
                    dialog.dismiss()
                }

            val alert = alertDialogBuilder.create()
            alert.setTitle("Error")
            alert.show()
        } else {
            alertDialogBuilder.setMessage("Response: ${response.body()?.message.toString()}")
                .setCancelable(false)
                .setNeutralButton("OK") { dialog, _ ->
                    dialog.dismiss()
                }

            val alert = alertDialogBuilder.create()
            alert.setTitle("Success")
            alert.show()
        }
    }

    override fun onFailure(call: Call<UploadResponse>, t: Throwable) {
        alertDialogBuilder.setMessage(t.message)
            .setCancelable(false)
            .setNeutralButton("OK") { dialog, _ ->
                dialog.dismiss()
            }

        val alert = alertDialogBuilder.create()
        alert.setTitle("Error")
        alert.show()
    }
}
Where interface looks like:
import retrofit2.Call
import retrofit2.Response

interface IUploadImageRetrofitCallBacks {
    fun onResponse(call: Call<UploadResponse>, response: Response<UploadResponse>)
    fun onFailure(call: Call<UploadResponse>, t: Throwable)
}
Upload image:
import android.content.Context
import android.net.Uri
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.RequestBody.Companion.toRequestBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class UploadImageRetrofit(private val uploadImageRetrofitCallBacks: IUploadImageRetrofitCallBacks, private val webApiService: IWebApiService) {
    fun uploadImage(imgUri: Uri, context: Context): String? {

        val inputStream = context.contentResolver.openInputStream(imgUri)
        val mediaType = context.contentResolver.getType(imgUri)?.toMediaTypeOrNull()
        val requestFile = inputStream?.use {
            it.readBytes().toRequestBody(mediaType)
        }
        val imagePart: MultipartBody.Part? = requestFile?.let {
            MultipartBody.Part.createFormData("image", "image.jpg", it)
        }

        val webApiRequest = webApiService.uploadImage(imagePart)

        webApiRequest.enqueue(object : Callback<UploadResponse> {
            override fun onResponse(call: Call<UploadResponse>, response: Response<UploadResponse>) {
                uploadImageRetrofitCallBacks.onResponse(call, response)
            }

            override fun onFailure(call: Call<UploadResponse>, t: Throwable) {
                uploadImageRetrofitCallBacks.onFailure(call, t)
            }
        })

        return null
    }
}
At the end, MainActivity looks like this:
import android.app.AlertDialog
import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    private lateinit var uploadImage: UploadImageRetrofit

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        uploadImage = UploadImageRetrofit(
            UploadImageRetrofitCallBacks(AlertDialog.Builder(this@MainActivity)),
            CreateRetrofitBuilder().createRetrofitBuilder("https://10.0.2.2:7181/")
                .create(IWebApiService::class.java)
        )
    }

    private val galleryLauncher =
        this.registerForActivityResult(ActivityResultContracts.GetMultipleContents()) { images ->
            images.forEach { imgUri ->
                uploadImage.uploadImage(imgUri, this)
            }
        }

    fun onOpenGalleryAndUploadButtonClick(view: View) {
        galleryLauncher.launch("image/*")
    }
}
Download from here.