Animate a gif file only once - codenameone

I have a gif file added in cn1 through add animation. But what I need is for it to run only once, then stick in the bottom of the screen. Now it is looping continuously all the time.
code:
#Override
protected void beforeAnimation(Form f) {
leaf = (Image) r.getImage("leafFinal.gif");
leafLabel = new Label(leaf);
f.add(FlowLayout.encloseIn(leafLabel));
leafLabel.setVisible(false);
}
#Override
protected void postAnimation(Form f) {
leafLabel.setVisible(true);
leafLabel.getParent().animateLayoutAndWait(2500);
}
How can I get the gif to not run continuosly?
Update: Using scaleImageLabel instead of Label
ScaleImageLabel leafGifImageLabel = new ScaleImageLabel();
f.add(FlowLayout.encloseIn(leafGifImageLabel));
leafGifImageLabel.setVisible(true);
Image leafGif = (Image) r.getImage("800_ng.gif");
Timeline tLeaf = (Timeline) leafGif;
tLeaf.setLoop(false);
leafGifImageLabel.setIcon(tLeaf);
leafGifImageLabel.getParent().revalidate();
It works if i use scaledImageLabel instead of Label but it cancels out transition of other components. And the gif runs very very very slow in devices.

Downcast the image to timeline and use setLoop(false) as such:
Timeline tLeaf = (Timeline) r.getImage("leafFinal.gif");
tLeaf.setLoop(false);
leaf = tLeaf;
Notice that this might start before the transition completed so write this code as:
f.addShowListener(e -> {
Timeline tLeaf = (Timeline) r.getImage("leafFinal.gif");
tLeaf.setLoop(false);
myLabel.setIcon(tLeaf);
myLabel.getParent().revalidate();
});

Related

How can I prevent my app from restarting when I switch apps?

What I want is that when I lose focus and return to my app, it continues where it left off.
To show the problem I have created a 45 second video at this link enter link description here
The same thing happens when running simulator. In the main class I make use of Lifecycle (generated from CN1).
I am copying the main class to it for validation.
MainClass
public class TarifaTaxiPredictivo extends Lifecycle {
private Usuario iU;
#Override
public void runApp() {
String sIdioma = L10NManager.getInstance().getLanguage().toLowerCase();// Salva el tipo de idioma
if (!sIdioma.equals(IDIOMA_INGLES) && !sIdioma.equals(IDIOMA_ESPANOL)) {
sIdioma = IDIOMA_ESPANOL;
}
iU = Usuario.getInstancia();
iU.setIdioma(sIdioma);
iU.setAumento(calculatePorcentajeAumento());
showSplashAnimation();
}
public void showSplashAnimation() {
Form splash = new Form(new LayeredLayout());
splash.setUIID("Splash");
ScaleImageLabel iconBackground = new ScaleImageLabel(getGlobalResources().getImage("attt.png"));
iconBackground.setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FILL);
Container centerBackground = BorderLayout.center(iconBackground);
splash.add(centerBackground);
Label iconForeground = new Label(getGlobalResources().getImage("attt.png"));
Container centerIcon = BorderLayout.centerAbsolute(iconForeground);
splash.add(centerIcon);
splash.show();
Display.getInstance().callSerially(() -> {
((BorderLayout) centerBackground.getLayout()).setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE);
centerBackground.setShouldCalcPreferredSize(true);
centerBackground.animateLayoutAndWait(2000);
iconForeground.remove();
iconBackground.remove();
centerIcon.remove();
Container cnFondo = LayeredLayout.encloseIn(
new Label(iconBackground.getIcon(), "CenterIcon"));
Container boxy = BoxLayout.encloseY(cnFondo);
Label placeholder = new Label();
placeholder.setShowEvenIfBlank(true);
Label lbTit = new Label("Tarifa de Taxi", "SplashSubTitulo");
Component.setSameHeight(placeholder, lbTit);
Component.setSameWidth(placeholder, lbTit, boxy);
centerBackground.add(BorderLayout.CENTER, boxy);
splash.revalidate();
Display.getInstance().callSerially(() -> {
placeholder.setText(" ");
boxy.add(placeholder);
boxy.setShouldCalcPreferredSize(true);
boxy.getParent().animateLayoutAndWait(1500);
boxy.replaceAndWait(placeholder, lbTit, CommonTransitions.createFade(1500));
Label lbNuevoTitulo = new Label(" ");
Label lbTitulo = new Label("ATTT", "SplashTitulo");
Component.setSameHeight(lbNuevoTitulo, lbTitulo);
Component.setSameWidth(lbNuevoTitulo, lbTitulo);
boxy.add(lbNuevoTitulo);
boxy.getParent().animateLayoutAndWait(250);
boxy.replaceAndWait(lbNuevoTitulo, lbTitulo, CommonTransitions.createFade(3000));
new Tarifa().show();
});
});
}
}
This is OS behavior. When an app is sent to the background the stop() method is invoked. It's important that we stop all connections etc. otherwise the phone will kill the app to preserve battery life.
When the app is restored start() will be invoked again. If your app always restarts when start() is invoked then you will get the effect of a clean restart.
You can test this in the simulator via the pause/resume functionality. You will be able to see how your app is supposed to act when the phone pauses it and restores it.
Assuming this works correctly in the simulator but fails in the device this would be because you're using resources in the background which you can only do through a background service facility.

When videos are just loaded or paused, they are completely black (on Android)

This happens on Android only. Videos work properly on iOS and Simulator.
The problem is that when my videos are loaded into the MediaPlayer, the displayed frame is and remains completely black. After pressing play, the video displays correctly. By pressing pause, the frame on which the pause occurred is shown correctly. However, if the app switches to background and then back to foreground, the paused video goes back to completely black.
How can I fix this problem?
The relevant part of my code is more or less as follows:
Media video = MediaManager.createMedia(videoFilePath, true, () -> {
Runnable callback = runOnVideoCompletion.get();
if (callback != null) {
callback.run();
}
});
video.setNativePlayerMode(false);
video.prepare();
MediaPlayer mediaPlayer = new MediaPlayer(video) {
#Override
public Dimension getPreferredSize() {
return new Dimension(size, size);
}
};
mediaPlayer.setHideNativeVideoControls(true);
mediaPlayer.setMaximize(false);
mediaPlayer.hideControls();
mediaPlayer.setAutoplay(false);
Container mediaPlayerCnt = new Container(new LayeredLayout(), "NoMarginNoPadding") {
#Override
public Dimension getPreferredSize() {
return new Dimension(size, size);
}
};
mediaPlayerCnt.add(mediaPlayer);
[...]
UPDATE: please see the comment Reduce the impact of videos in memory to prevent crashes, since there is a better approach than the solution I proposed here.
While waiting for a proper solution, I came up with a workaround that seems to solve the problem perfectly on my Android phone.
This code is in a method invoked to create each video and is therefore executed before form.show(), so the UITimer runnable is invoked about half a second after the form is shown.
/*
* Workaround for the issue: https://stackoverflow.com/q/66144759
* "video" is a Media instance returned by MediaManager.createMedia
* This workaround is for Android only and it assumes we don't use autoplay
*/
if (form != null && DeviceUtilities.isAndroidNative()) {
UITimer.timer(500, false, form, () -> {
CN.invokeAndBlock(() -> {
video.play();
video.setVolume(0);
while (!video.isPlaying()) {
/*
* When in a scrollable container there are many videos,
* play does not start immediately, but only when the
* video becomes visible, so we must wait.
*/
Util.sleep(100);
}
Util.sleep(100); // extra wait to be sure that the waiting is not zero
video.pause();
video.setVolume(100);
});
form.addShowListener(l -> {
CN.invokeAndBlock(() -> {
video.play();
video.setVolume(0);
while (!video.isPlaying()) {
// same as above
Util.sleep(100);
}
Util.sleep(100); // as above
video.pause();
video.setVolume(100);
});
});
});
}

Restore all layered pane components in their positions and repaint them

This question is related to this: Keep Codename One components in invalid positions after app stop/resume
In the linked question, the solution proposed is to use a fake layout. I tried that, but it produces side effects when I try to restore the original layouts.
I tried a completely different approach, that works fine on Android. My question is why the following code works well in Android only (it doesn't work on iPhone, that seems to ignore that code) and if there are small changes that make that code working also on iPhone.
The code:
private Map<Component, Dimension> layeredPaneCmps = new HashMap<>();
public void start() {
if (current != null) {
current.show();
layeredPaneRestore(); // it works on Android, but not on iOS
return;
}
[...]
}
public void stop() {
current = getCurrentForm();
if (current instanceof Dialog) {
((Dialog) current).dispose();
current = getCurrentForm();
}
layeredPaneSave(null);
}
/**
* Save the position of all layered pane components in a recursive way: just
* invoke with null as cnt.
*
* #param cnt
*/
private void layeredPaneSave(Container cnt) {
if (cnt == null) {
layeredPaneCmps.clear();
cnt = Display.getInstance().getCurrent().getLayeredPane(this.getClass(), true);
}
for (int i = 0; i < cnt.getComponentCount(); i++) {
layeredPaneCmps.put(cnt.getComponentAt(i), new Dimension(cnt.getComponentAt(i).getX(), cnt.getComponentAt(i).getY()));
if (cnt.getComponentAt(i) instanceof Container) {
layeredPaneSave((Container) cnt.getComponentAt(i));
}
}
}
/**
* Restores all layered pane components in their position and repaints them.
*/
private void layeredPaneRestore() {
Container layeredPane = Display.getInstance().getCurrent().getLayeredPane(this.getClass(), true);
for (Component cmp : layeredPaneCmps.keySet()) {
cmp.setX(layeredPaneCmps.get(cmp).getWidth());
cmp.setY(layeredPaneCmps.get(cmp).getHeight());
cmp.repaint();
}
layeredPane.repaint();
}
Android and iOS have very different suspend/resume behaviors where iOS tries to minimize repaints and back-grounding while Android constantly suspends/resumes. I would suggest logging in the stop()/start() method to make sure they aren't invoked multiple times.
Notice that you shouldn't invoke repaint() it would be invoked for you. Since a repaint() might trigger a layout this could be a problem. Also the repaint() of the parent component loops into painting the components so layeredPane would be enough and doesn't require also cmp.repaint();.

How to take a screenshot of a scrollable in Selenium using Java?

I am a beginner in Selenium.
I have very long scroll-able form which will automation script will fill, is there a way where I can take screenshot when the page is scrolled down every time (web-driver scrolls to fill the form, I haven't scripted to scroll down as web-driver takes care of it), so that the entire form can be captured in multiple screenshots.
Here is how I would do it. On every swipe You can take screenshot, like as step screenshot.
Here is part of code for screenshot take:
public static String takeScreenshot(WebDriver driver) {
driver.get("http://www.google.com/");
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
// Now you can do whatever you need to do with it, for example copy somewhere
File dest = new File("c:\\tmp\\screenshot.png");
FileUtils.copyFile(scrFile, dest);
return dest.getAbsolutePath();
}
And here is the code for vertical swipe:
public static void swipeVertical(WebDriver driver) {
Dimension size = driver.manage().window().getSize();
System.out.println(size);
int starty = (int) (size.height * 0.70);
int endy = (int) (size.height * 0.30);
int startx = size.width / 2;
new TouchAction(driver)
.press(startx, starty)
.waitAction(Duration.ofMillis(1000))
.moveTo(startx, endy)
.release()
.perform();
}
so my pseudo code would look like this:
WebDriver driver = initDriver();
load page and elements
inputText(text);
takScreenShot(driver);
swipe(driver);
...
repeat until end...
You can capture as many screenshots as you want. After fill in each column, after fill in each third column, whatever.
Or you can let the webdriver fill in the whole form and scroll up/down using press pgup/pgdn simulation:
import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
Robot rob = new Robot();
rob.keyPress(KeyEvent.VK_PAGE_DOWN);
rob.keyRelease(KeyEvent.VK_PAGE_DOWN);

Load PNG images to array and use as textures

I've been attempting to use PNG images as textures in Unity, when I use only one or two its easy to drag and drop them in the inspector. In my current project I have over 300 images which I am trying to load into an array, I then want to change the texture each time round the update so it appears like a video.
Here is what I have so far:
using UnityEngine;
using System.Collections;
public class ChangeImage : MonoBehaviour {
public Texture[] frames;
public int CurrentFrame;
public object[] images;
void OnMouseDown() {
if (GlobalVar.PlayClip == false){
GlobalVar.PlayClip = true;
} else {
GlobalVar.PlayClip = false;
}
}
public void Start() {
images = Resources.LoadAll("Frames");
for (int i = 0; i < images.Length; i++){
Texture2D texImage = (Texture2D) images[i];
frames[i] = texImage;
}
}
// Update is called once per frame
void Update () {
if(GlobalVar.PlayClip == true){
CurrentFrame++;
CurrentFrame %= frames.Length;
GetComponent<Renderer>().material.mainTexture = frames[CurrentFrame];
}
}
}
I have been attempting to load the images into an object array convert them to textures then output to a texture array. Does anyone know where I am going wrong with this it does not seem to give any errors but the texture is not changing?
Any advice is much appreciated
Thanks
What you are doing is kinda slow and inappropriate.
What I would recommend is to use the Animator and an animation. Have all your textures into a atlas texture, this way you will limit the draw call amount. Make that texture a sprite and use the sprite editor to cut in sub sprite.
Add an animator and create an animation. Select the whole sub sprites and drag them into the animation. Done.
Now you can easily control the speed and the playing via the animator component.
I managed to fix the problem eventually, the problem seemed to be the loading of assets was not working correctly and this was why the frame was not changing. I also changed the name of the folder containing the images from "Frames" to "Resources". Here is the completed code for anyone else who needs it:
using UnityEngine;
using System.Collections;
public class ChangeImage : MonoBehaviour {
public Texture[] frames;
public int CurrentFrame;
void OnMouseDown() {
if (GlobalVar.PlayClip == false){
GlobalVar.PlayClip = true;
} else {
GlobalVar.PlayClip = false;
}
}
public void Start() {
frames = Resources.LoadAll<Texture>("");
}
// Update is called once per frame
void Update () {
if(GlobalVar.PlayClip == true){
CurrentFrame %= frames.Length;
CurrentFrame++;
Debug.Log ("Current Frame is " + CurrentFrame);
GetComponent<Renderer>().material.mainTexture = frames[CurrentFrame];
}
}
}
Thanks for the advice on animations I will still look into it as the performance of the images is not great.

Resources