Set LocationComponentOptions foregroundDrawable dynamically - android

I am using MapBox 8.4.0 and I have the following snippet to load the map on a fragment, pinning the user's current location with a marker. I need to customize the marker by dynamically setting foregroundDrawable with an image loaded from a network URL. But foregroundDrawable only accepts a resource ID as parameter.
val customOptions = LocationComponentOptions.builder(context!!)
.elevation(5f)
.foregroundDrawable(R.drawable.icon_profile) // set image dynamically
.backgroundDrawable(R.drawable.icon_current_location)
.build()
val activationOptions = LocationComponentActivationOptions.builder(context!!, style)
.locationComponentOptions(customOptions)
.build()
mapboxMap.locationComponent.apply {
activateLocationComponent(activationOptions)
isLocationComponentEnabled = true
cameraMode = CameraMode.TRACKING
renderMode = RenderMode.NORMAL
}
It should look like this with the profile icon replaced with the loaded image at run time.
https://i.stack.imgur.com/eoXuG.jpg
Any way I could achieve this?

We can use foregroundName() to set dynamic icon for our marker.
mapboxMap.getStyle { loadedStyle ->
loadedStyle.addImage("marker-icon", bitmapIcon) // create a Bitmap icon; you may use Glide to load image from URL
val locationComponentOptions: LocationComponentOptions =
LocationComponentOptions.builder(context!!)
.foregroundName("marker-icon") // set icon for the marker
.build()
val activationOptions =
LocationComponentActivationOptions.builder(context!!, loadedStyle)
.locationComponentOptions(locationComponentOptions)
.build()
mapboxMap.locationComponent.apply {
activateLocationComponent(activationOptions)
...
}
}

Fly by comment here to say that if you're using Picasso instead of Glide, https://stackoverflow.com/a/20181629/6358488 shows how to use Picasso to set a target to get the Bitmap from the network URL call.

Related

Android Messaging Style notification -Add icon from image URL - Person.Builder

I am using Person Object to build chat app notifications like Gmail. So I have created the person object. But i want to set icon from a image URL coming from server an not from drawable resource. I am using Coil library for loading images . The below code is working fine,
By default the android generates the icon with the first letter passed to the title.
So, How can i show the image coming from server as a URL in icon with best practice of memory and resource usages. Below is my Person object.
Here is the Official link of Person.
And this is what I referred to Notification Messaging style tutorial
val senderPerson: Person = Person.Builder().also {person->
person.setKey(message.getSenderKey(prefs))
person.setName(message.getNotificationTitle())
person.setImportant(true)
//****HERE I WANT TO SET IMAGE FROM URL******
// person.setIcon(IconCompat.createWithResource(this, R.drawable.placeholder_transaparent))
}.build()
You'd load the image URL asynchronously using the Coil Request, and return the fetched icon in a closure.
Coil returns a drawable, and you can get Icon from a Drawable through a Bitmap using IconCompat.createWithBitmap((drawable as BitmapDrawable).bitmap):
private fun asyncLoadIcon(imageUrl: String?, setIcon: (IconCompat?) -> Unit) {
if (imageUrl.isNullOrEmpty())
setIcon(null)
else {
// using COIL to load the image
val request = ImageRequest.Builder(this)
.data(imageUrl)
.target { drawable ->
setIcon(IconCompat.createWithBitmap((drawable as BitmapDrawable).bitmap)) // // Return the fetched icon from the URL
}
.listener(object : ImageRequest.Listener { // Return null icon if the URL is wrong
override fun onError(request: ImageRequest, result: ErrorResult) {
setIcon(null)
}
})
.build()
imageLoader.enqueue(request)
}
}
This code returns a null icon if the URL is wrong or if it's empty/null.
Then build the notification message with that function:
asyncLoadIcon("https://my_icon_url.png") { // set the icon url
val person = Person.Builder().apply {
setName("John Doe")
setIcon(it)
}.build()
// Build the notification with the person
.....
}
For some enhancements, you'd enable caching, and disable hardware bitmaps; but I do recommend other libraries like Glide and Picasso.
.memoryCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(CachePolicy.ENABLED)
.allowHardware(false) // Disable hardware bitmaps

Using Glide with RecyclerView and multi-part HTTP requests

I'm trying to use Glide to display thumbnails from the Google Photos library in a RecyclerView. In order to fetch images from this library, I must make two HTTP requests: first I must get the MediaItem from the id (I've already obtained a list of ids in a previous step), and second I must request the actual image from thumbnailUrl. This is the recommended process, as baseUrls expire after one hour so you aren't supposed to store them:
val googlePhotosThumbnailUrl =
App.googlePhotosService.getMediaItem(asset.googlePhotosId) // First HTTP request fetches MediaItem
.run {
val baseUrl = this.baseUrl
val thumbnailUrl = "$baseUrl=w400-h400" // Appends the requested dimensions to the Url.
thumbnailUrl // Second HTTP request fetches this URL
}
The problem is that Glide's load() method doesn't appear to support chaining HTTP requests like what's shown above:
GlideApp.with(itemView.context)
.asBitmap()
.load(googlePhotosThumbnailUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(binding.imageViewLargeThumbnail)
The above code executes synchronously, so loading is incredibly slow. I've managed to fix this by using coroutines as shown below. But the problem with this is Glide doesn't cache any of the images, so if I scroll down and back up Glide refetches every image:
override fun bindAsset(asset: GooglePhotosAsset, position: Int) {
this.asset = asset
this.index = position
// We set the loading animation here for Google Photos assets, since for those we need to fetch a mediaItem and then a baseUrl.
// This forces us to perform the process in a coroutine, and Glide can't set the loading animation until the baseUrl is fetched.
binding.imageViewLargeThumbnail.setImageResource(R.drawable.loading_animation)
fragment.lifecycleScope.launch(Dispatchers.Default) {
val googlePhotosThumbnailUrl = App.googlePhotosService.getMediaItem(asset.googlePhotosId) // First HTTP request fetches MediaItem
.run {
val baseUrl = this.baseUrl
val thumbnailUrl = "$baseUrl=w400-h400" // Appends the requested dimensions to the Url.
thumbnailUrl // Second HTTP request fetches this URL
}
withContext(Dispatchers.Main) {
GlideApp.with(itemView.context)
.asBitmap()
.load(googlePhotosThumbnailUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.fitCenter()
.into(binding.imageViewLargeThumbnail)
}
}
}
The only potentially relevant answer I've found is this one, but it seems super complicated and outdated. Are there any better solutions?

Stream video with bitmap as overlay

I'm new to wowza and is working on a project to live stream video captured from an Android device. I need to attach an image(dynamic one) to the video stream so that the users watching the stream can view it. The code I have tried is given below(as from the example source code from wowza):
// Read in a PNG file from the app resources as a bitmap
Bitmap overlayBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.overlay_logo);
// Initialize a bitmap renderer with the bitmap
mWZBitmap = new WZBitmap(overlayBitmap);
// Place the bitmap at top left of the display
mWZBitmap.setPosition(WZBitmap.LEFT, WZBitmap.TOP);
// Scale the bitmap initially to 75% of the display surface width
mWZBitmap.setScale(0.75f, WZBitmap.SURFACE_WIDTH);
// Register the bitmap renderer with the GoCoder camera preview view as a frame listener
mWZCameraView.registerFrameRenderer(mWZBitmap);
This works fine, but I don't want to show the image at the broadcasting end, the image should be visible only at the receiving end. Is there anyway to get this done?
I managed to get this done by registeringFrameRenderer and setting the bitmap inside onWZVideoFrameRendererDraw.
Code snippet is as given below(Kotlin):
private fun attachImageToBroadcast(scoreValue: ScoreUpdate) {
bitmap = getBitMap(scoreValue)
// Initialize a bitmap renderer with the bitmap
mWZBitmap = WZBitmap(bitmap)
// Position the bitmap in the display
mWZBitmap!!.setPosition(WZBitmap.LEFT, WZBitmap.TOP)
// Scale the bitmap initially
mWZBitmap!!.setScale(0.37f, WZBitmap.FRAME_WIDTH)
mWZBitmap!!.isVisible = false // as i dont want to show it initially
mWZCameraView!!.registerFrameRenderer(mWZBitmap)
mWZCameraView!!.registerFrameRenderer(VideoFrameRenderer())
}
private inner class VideoFrameRenderer : WZRenderAPI.VideoFrameRenderer {
override fun onWZVideoFrameRendererRelease(p0: WZGLES.EglEnv?) {
}
override fun onWZVideoFrameRendererDraw(p0: WZGLES.EglEnv?, framSize: WZSize?, p2: Int) {
mWZBitmap!!.setBitmap(bitmap) // note that the bitmap value gets changed once I get the new values
//I have implemented some flags and conditions to check whether a new value has been obtained and only if these values are satisfied, the setBitmap is called. Otherwise, as it is called continuously, flickering can occur in the screen
}
override fun isWZVideoFrameRendererActive(): Boolean {
return true
}
override fun onWZVideoFrameRendererInit(p0: WZGLES.EglEnv?) {
}
}
In iOS, we can implement WZVideoSink protocol to achieve this.
First, we need to update the scoreView with the latest score and then convert the view to an image.
Then we can embed this image to the captured frame using WZVideoSink protocol method.
A sample code is given below.
// MARK: - WZVideoSink Protocol
func videoFrameWasCaptured(_ imageBuffer: CVImageBuffer, framePresentationTime: CMTime, frameDuration: CMTime) {
if self.goCoder != nil && self.goCoder!.isStreaming {
let frameImage = CIImage(cvImageBuffer: imageBuffer)
var addCIImage: CIImage = CIImage()
if let scoreImage = self.getViewAsImage() {
// scoreImage is the image you want to embed.
addCIImage = CIImage(cgImage: scoreImage.cgImage!)
}
let filter = CIFilter(name: "CISourceOverCompositing")
filter?.setDefaults()
filter?.setValue(addCIImage, forKey: kCIInputImageKey)
filter?.setValue(frameImage, forKey: kCIInputBackgroundImageKey)
if let outputImage: CIImage = filter?.value(forKey: kCIOutputImageKey) as? CIImage {
let context = CIContext(options: nil)
context.render(outputImage, to: imageBuffer)
} else {
let context = CIContext(options: nil)
context.render(frameImage, to: imageBuffer)
}
}
}
func getViewAsImage() -> UIImage {
// convert scoreView to image
UIGraphicsBeginImageContextWithOptions(self.scoreView.bounds.size, false, 0.0)
self.scoreView.layer.render(in: UIGraphicsGetCurrentContext()!)
let scoreImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return scoreImage
}

How to get URL from PlacePhotoMetadata

I can't seem to get the url, from the PlacePhotoMetadata object. Debugger shows that there is an URL there but I can't seem to access it.
How do you access the URL in the object?
val placeId = "ChIJa147K9HX3IAR-lwiGIQv9i4"
val photoMetadataResponse = mGeoDataClient.getPlacePhotos(placeId)
photoMetadataResponse.addOnCompleteListener { task ->
// Get the list of photos
val photos = task.result
// Get the PlacePhotoMetadataBuffer (metadata for all of the photos)
val photoMetadataBuffer = photos.photoMetadata
// Get the first photo in the list
for (photo in photoMetadataBuffer) {
// Get the attribution text
val attribution = photo.attributions
}
}
You can't. Take a look at the documentation for PlacePhotoMetadata. There are methods to download a bitmap of the image, but no methods that return the URL.
To get the photo you should do something like this:
// this is your for-loop:
photoMetadataBuffer.forEach { photo ->
photo.getPhoto(client).setResultCallback({ result ->
// do whatever you want here:
showPhotoWithAttribution(photo.attributions, result.getBitmap())
})
}
Note that replacing a for-loop with a forEach call has no real advantage, it just makes your code look cleaner.

ListView reusing old images

I created a plugin using Picasso and it uses the android.widget.ImageView to load the cached image into.
The plugin works fine if using a Repeater but whenever i try using it with a ListView after scrolling past about the 7th item the ListView begins to reuse old images even if the image source is different
The reason why is because list views reuse the entire fragment; so what happens is that your img being reused gets the old image shown unless you clear it.
I actually use Picasso myself; and this is my current picasso library.
So if you look in my code below, when I set the new .url, I clear the existing image. (I made a comment on the specific line) -- This way the image now show blank, and then picasso loads it from either memory, disk or a remote url (in my case a remote url) and it will assign the proper image.
"use strict";
var Img = require('ui/image').Image;
var application = require("application");
var PT = com.squareup.picasso.Target.extend("Target",{
_owner: null,
_url: null,
onBitmapLoaded: function(bitmap, from) {
// Since the actual image / target is cached; it is possible that the
// target will not match so we don't replace the image already seen
if (this._url !== this._owner._url) {
return;
}
this._owner.src = bitmap;
},
onBitmapFailed: function(ed) {
console.log("Failed File", this._url);
},
onPrepareLoad: function(ed) {
}
});
Object.defineProperty(Img.prototype, "url", {
get: function () {
return this._url;
},
set: function(src) {
if (src == null || src === "") {
this._url = "";
this.src = null;
return;
}
var dest = src;
this._url = dest;
this.src = null; // -- THIS IS THE LINE TO CLEAR THE IMAGE
try {
var target = new PT();
target._owner = this;
target._url = dest;
var x = com.squareup.picasso.Picasso.with(application.android.context).load(dest).into(target);
} catch (e) {
console.log("Exception",e);
}
},
enumerable: true,
configurable: true
});
Please note you only need to require this class once, then it attaches itself to the <Image> component and adds the new .url property; this allows me to use this in the Declarative XML in all the rest of the screens and when I need picasso, I just use the .url property to have picasso take over the loading of that image.

Categories

Resources