0

I'm having some issues building out a section of my code which edits server sent thumbnails and renders them as a Google GroundOverlay.

The issue seems to stem from Kotlin Coroutines. First, the documentation from Google says that the ground overlays must be created on the Main Thread. Running their creation out of the main thread causes a fatal error. So I have been sure to make these GroundOverlays on the main thread. When trying to create the bitmap on a thread out of Main, however, I seem to get no overlay at all.

class BarreMapFragment : Fragment(),
    GoogleMap.OnCameraIdleListener,
    GoogleMap.OnCameraMoveCanceledListener,
    GoogleMap.OnCameraMoveListener,
    GoogleMap.OnCameraMoveStartedListener,
    OnMapReadyCallback {

        //Main handler for google map/item styling
        googleMapHandler = GoogleMapHandler(gMap!!, activity!!.applicationContext, DefaultTheme, lifecycle.coroutineScope)


.    .    .

open class GoogleMapHandler(val gMap: GoogleMap,
                            val context: Context,
                            val mapThemeInstructions: MapThemeInstructions,
                            val coroutineScope: CoroutineScope
) {

fun updateActiveUserAvatarPosition(position: LatLng) {

        if (mActiveUserAvatar == null) {

            coroutineScope.launch {
                mActiveUserAvatar = mapObjectFactory.factory(
                    MapObject(
                        latitude = position.latitude,
                        longitude = position.longitude,
                        objectId = "SELF_AVATAR",
                        objectType = MapObjectType.USER_AVATAR,
                        timestamp = System.currentTimeMillis(),
                        weight = 20.toFloat()
                    ), getOverlayWidthByZoom(dpScreenWidth, gMap.cameraPosition.target, gMap.cameraPosition.zoom)) as RenderedUserAvatarItem
            }

        }

        mActiveUserAvatar?.updatePosition(position)
    }
    suspend fun factory(mapObject: MapObject, diameter: Float) : RenderedMapItem {

        overlayDiameter = diameter

        val item = RenderedUserAvatarItem(
            mapObject,
            buildOverlay(mapObject)
        )

        return item
    }

    @MainThread
    private suspend fun buildOverlay(mapObject: MapObject) : GroundOverlay {

        Log.d("UserOverlay", "I was called.")
        //Get the bitmap from the resources
        //TODO:  We can do more with this later...  Like custom avatars



        //val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_user_avatar)
        val bitmap =  withContext(Dispatchers.Default) {
            async {
                val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
                val bitmap : Bitmap = drawableToBitmap(d)!!
                bitmap
            }
        }.await()

        //val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
        //val bitmap : Bitmap = drawableToBitmap(d)!!

        Log.d(TAG, "bitmap = " + bitmap.toString())

        //Make bitmap descriptor
        val descriptor = BitmapDescriptorFactory.fromBitmap(bitmap)

        val overlayOptions = GroundOverlayOptions().image(descriptor)

        //Position and size of groundoverlay
        overlayOptions.position(LatLng(mapObject.latitude, mapObject.latitude) , overlayDiameter)

        //Add the overlay to the map, get a handle and save it to public Overlay list
        val mOverlay = gMap.addGroundOverlay(overlayOptions)

        //Store the moment information in the overlay tag
        mOverlay.tag = mapObject.objectId

        return mOverlay
    }

The suspend function is called from the main thread. Now,

        val bitmap =  withContext(Dispatchers.Unconfined) {
            async {
                val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
                val bitmap : Bitmap = drawableToBitmap(d)!!
                bitmap
            }
        }.await()

and the commented out section above (without using async)

        val d: Drawable = context.getDrawable(R.drawable.ic_user_avatar)!!
        val bitmap : Bitmap = drawableToBitmap(d)!!

Will both yield a GroundOverlay with no problems. The problem happens when I change Dispatchers.Unconfined to anything else. Even Dispatchers.Main causes the GroundOverlay to not show up on the map. The GroundOverlays are made, I have checked them with log statements. Their transparency is expected as is their visibility. The issue seems to be with the Bitmap. I suspect I am not understanding the way the await() works. I figured that it would pause the suspend function until the bitmap is returned and ready to go.

This is simplified code to isolate the error. I do need this to be done on Dispatchers.Default because each thumbnail is adjusted in style depending on the location and time of day. This bit of processing would be hard on the UI if done on the main thread.

chrisdottel
  • 1,053
  • 1
  • 12
  • 21

0 Answers0