Callback function if Vaadin Image src changes and update on client is complete - vaadin

I'm using Vaadin 23 and I build an application which shows different images which are changed and loaded on runtime. I need the exact display size of the image to update other information based on the size. The server from which I get the image is quite slow, therefore it can last up to 10 seconds until the new image is downloaded by the client.
Image doesn't have a function like:
myImage.addSrcChangedAndClientUpdatedListener(...);
Is there any other solution to get a callback function in Java when the src Attribut changes and the new image is downloaded from the client?
My workaround is to wait 10 seconds and then get the image size by an async Javascript call.
But sometimes the image is ready after 2 seconds -> my implementation waits 8 seconds unnecessaryly which leads to bad customer experience.
Sometime the image download even takes 20 seconds -> my implementation doesn't work.
I don't see a proper solution. Any ideas? Can a implement a custom Java callback function based on custom Javascript code? How would such a solution look like?

Using Flow's Element API you can create a listener for the load DOM event that an img element fires after it has finished loading the image data. Using the element API you can also add several details to the event data that should be sent to the server. Here is a simple example that allows switching images, and then logs the image's size from the server:
public class ImageView extends Div {
private static final String IMAGE_1 = "https://imgs.search.brave.com/bjAqtSxNjNFNHm384o53EB6Zrv85eGtWpmspBqc98Yk/rs:fit:592:225:1/g:ce/aHR0cHM6Ly90c2Uy/Lm1tLmJpbmcubmV0/L3RoP2lkPU9JUC5X/ZzNXajk1SDl6VTNw/SzNMY2dwT2xRSGFG/NyZwaWQ9QXBp";
private static final String IMAGE_2 = "https://imgs.search.brave.com/v5uhvAoiVtMDM8UPI8vk8XMELrIRKW1fVLkRsiuqnU0/rs:fit:844:225:1/g:ce/aHR0cHM6Ly90c2Ux/Lm1tLmJpbmcubmV0/L3RoP2lkPU9JUC5O/OEV3U1psZlNZNmph/cmR1cm4xckZBSGFF/SyZwaWQ9QXBp";
public ImageView() {
Image image = new Image();
Button setImage1 = new Button("Set image 1", e -> image.setSrc(IMAGE_1));
Button setImage2 = new Button("Set image 2", e -> image.setSrc(IMAGE_2));
Span imageData = new Span();
image.getElement().addEventListener("load", loadEvent -> {
JsonObject eventData = loadEvent.getEventData();
Number width = eventData.getNumber("element.clientWidth");
Number height = eventData.getNumber("element.clientHeight");
imageData.setText(String.format("Width: %s | Height: %s", width, height));
})
.addEventData("element.clientWidth")
.addEventData("element.clientHeight");
add(image);
add(new Div(imageData));
add(new Div(setImage1, setImage2));
}
}
Reference:
Flow Element API - Listening to User Events
Window: load event

Related

Flutter async executing on main thread

I have a CameraPreview that fills the whole screen, with a FloatingActionButton at the bottom to take a picture.
On the onPress method of the button, I'm making a network call for which I do not care (yet) about the result. So I would like everything made inside that method to be done asynchronously, so it does not block my main thread.
That means (if I get it right) that I sould not use the await keyword.
This is the code in my onPressed
// Attempt to take a picture and log where it's been saved
await controller.takePicture(path);
print("Done taking picture");
sendBase64ToAPI(path);
This is my sendBase64ToApi method
Future<String> sendBase64ToAPI(String path) async {
File(path).readAsBytes().then(thenMethod);
return null;
}
void thenMethod(List bytes){
print("Start reading file");
Image image = decodeImage(bytes);
int x = ((screenWidth/2) + (overlayWidth/2)).toInt();
int y = ((screenHeight/2) + (overlayHeight/2)).toInt();
print("Start cropping image");
image = copyCrop(image, x, y, overlayWidth, overlayHeight);
var base64Str = base64.encode(image.getBytes());
print("Done");
print(base64Str.substring(0,30));
print(base64Str.substring(base64Str.length-30,base64Str.length-1));
}
My UI is completely frozen between 'Start reading file' and 'Start cropping image' although, those are async methods, called without await so that shouldn't happen.
Why are those methods not executing asynchronously ?
Okay, this seems to be a known issue from the library I'm using.
The documentation recommends to use the decodeImage function in an Isolate.
Will keep the question open for a few days, if someone spots what is synchronous in my code.

RxJava Observable to smooth out bursts of events

I'm writing a streaming Twitter client that simply throws the stream up onto a tv. I'm observing the stream with RxJava.
When the stream comes in a burst, I want to buffer it and slow it down so that each tweet is displayed for at least 6 seconds. Then during the quiet times, any buffer that's been built up will gradually empty itself out by pulling the head of the queue, one tweet every 6 seconds. If a new tweet comes in and faces an empty queue (but >6s after the last was displayed), I want it to be displayed immediately.
I imagine the stream looking like that described here:
Raw: --oooo--------------ooooo-----oo----------------ooo|
Buffered: --o--o--o--o--------o--o--o--o--o--o--o---------o--o--o|
And I understand that the question posed there has a solution. But I just can't wrap my head around its answer. Here is my solution:
myObservable
.concatMap(new Func1<Long, Observable<Long>>() {
#Override
public Observable<Long> call(Long l) {
return Observable.concat(
Observable.just(l),
Observable.<Long>empty().delay(6, TimeUnit.SECONDS)
);
}
})
.subscribe(...);
So, my question is: Is this too naïve of an approach? Where is the buffering/backpressure happening? Is there a better solution?
Looks like you want to delay a message if it came too soon relative to the previous message. You have to track the last target emission time and schedule a new emission after it:
public class SpanOutV2 {
public static void main(String[] args) {
Observable<Integer> source = Observable.just(0, 5, 13)
.concatMapEager(v -> Observable.just(v).delay(v, TimeUnit.SECONDS));
long minSpan = 6;
TimeUnit unit = TimeUnit.SECONDS;
Scheduler scheduler = Schedulers.computation();
long minSpanMillis = TimeUnit.MILLISECONDS.convert(minSpan, unit);
Observable.defer(() -> {
AtomicLong lastEmission = new AtomicLong();
return source
.concatMapEager(v -> {
long now = scheduler.now();
long emission = lastEmission.get();
if (emission + minSpanMillis > now) {
lastEmission.set(emission + minSpanMillis);
return Observable.just(v).delay(emission + minSpanMillis - now, TimeUnit.MILLISECONDS);
}
lastEmission.set(now);
return Observable.just(v);
});
})
.timeInterval()
.toBlocking()
.subscribe(System.out::println);
}
}
Here, the source is delayed by the number of seconds relative to the start of the problem. 0 should arrive immediately, 5 should arrive # T = 6 seconds and 13 should arrive # T = 13. concatMapEager makes sure the order and timing is kept. Since only standard operators are in use, backpressure and unsubscription composes naturally.

Is there a way to rearrange objects on a DevX report?

I want to enable user to move things around on a devexpress print preview and print it only after it is done. If it is possible, could I get some directions where I can start looking? (I will not have the time to look into the whole documentation, what may sound lazy, but devx is kinda huge for the short time I have.)
I don't think you could do this on the Print preview directly, but what you could do is provide a button which launches the XtraReports Designer and pass in the layout from your currently displayed document. When the user has finished editing then you can reload the document in the print preview, loading its new layout as required. You may need to customize the designer heavily to remove various options restricting the user to only editing certain aspects - you can hide much of the functionality including data source, component tray etc:
designer video
designer documentation
hide options in designer
if(EditLayout(document))
RefreshDocument();
public static bool EditLayout(XtraReport document)
{
using (var designer = new XRDesignRibbonForm())
{
designer.OpenReport(document);
XRDesignPanel activePanel = designer.ActiveDesignPanel;
activePanel.AddCommandHandler(new DesignerCommandHandler(activePanel));
HideDesignerOptions(activePanel);
designer.ShowDialog();
changesMade = activePanel.Tag != null && (DialogResult)activePanel.Tag == DialogResult.Yes; //set this tag in your DesignerCommandHandler
activePanel.CloseReport();
}
return changesMade;
}
Finally, some utility methods for changing a document/reports layout:
internal static byte[] GetLayoutData(this XtraReport report)
{
using (MemoryStream mem = new MemoryStream())
{
report.SaveLayoutToXml(mem);
return mem.ToArray();
}
}
internal static void SetLayoutData(this XtraReport report, byte[] data)
{
using (var mem = new MemoryStream(data))
{
report.LoadLayoutFromXml(mem);
}
}

Actionscript: How to handle events with same type but with different function listener?

I'm attempting to make a sample application for ios using Actionscript (adobe air). But I'm having problems concerning events/event handling. My app basically gives the user the option to take a picture or select one from the camera roll to upload to a server. If the user decides to take a photo, I have to save that photo to the device's camera roll. The part of the code I'm having problem with is below:
private function readMediaData():void {
//set up some variables and data
var file:File = tempDir.resolvePath(filename);
var stream:FileStream = new FileStream();
stream.open(file, FileMode.WRITE);
stream.writeBytes(imageBytes);
stream.close();
file.addEventListener(Event.COMPLETE, uploadComplete, false, 0, true);
//upload file here
}
private function uploadComplete(event:Event):void {
//selectedImage is the MediaPromise
if (selectedImage.file == null) {
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleted);
loader.loadFilePromise(selectedImage);
}
}
private function loaderCompleted(event:Event):void {
//save image
}
The upload is working fine, but once the upload is completed, I get a somewhat infinite loop between loaderCompleted and uploadComplete resulting in multiple images being uploaded to the server. I tried removing the listener for the file once it has entered the uploadComplete function but still get the same result. My guess is that once the event listener for the loader is registered, it triggers an Event.COMPLETE notification which both method (or object) still recognizes. Is there a way to properly handle events with the same type but coming from different objects and with different function listeners?
Try setting the listener to the stream instead of to the file:
// Changed the 'file' with 'stream'
stream.addEventListener(Event.COMPLETE, uploadComplete, false, 0, true);

Unable to close screen after the email attachment download is completed

I am using AttachmentDownloadManager class download method to download the email attachments to the device.I am displaying a progress screen while the attachments gets downloaded to device.In the downloadCompleted() event listener method, I am trying to stop/close the progress screen but unable to do so.
Below is the code snippet:
attachmentDownloadMngr = new AttachmentDownloadManager();
attachmentDownloadMngr.download(attachmentBodyPart, null, new IGAEmailAttachmentListener(this));
Below are DownloadProgressListener event listener methods:
public void updateProgress(Object element, int current, int total)
{
synchronized(UiApplication.getEventLock())
{
Ui.getUiEngine().pushGlobalScreen(progressScreen,1,UiEngine.GLOBAL_QUEUE);
}
}
public void downloadCompleted(Object element)
{
synchronized(UiApplication.getEventLock())
{
Ui.getUiEngine().popScreen(progressScreen);
}
}
public void downloadCancelled(Object element)
{
}
progressScreen is instance of PopupScreen where I am displaying a guagefield. The progress screen does not get any events even after the attachment download is completed.
Please help me to solve this issue.
You seem to be pushing a globalscreen every time you get a callback to updateprogress() . I wouldn't do it. I would rather push the globalscreen with the gaugefield when I start the download, and then set the value of gaugefield using gaugeField.setValue(value).
the way you are doing it would mean, the event thread pushes too many screens on to the stack, and I guess it would throw a run time exception and kill your app.

Resources