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

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.

Related

Codename One back command update form data

I have an Android application that uses Back Command to go back to the start screen.
The start screen has a label with a number inside, that I want to update when the back command is used.
I could figure out a solution with the code inside the back command, but I don't know if my approach is the best, since the ClassOne gets sort of loaded twice.
Here is the code I already have:
public class ClassOne {
public ClassOne(ClassPojo classPojo) {
// I want to change the text of this label when calling the back command
labelOne.setText(classPojo.getStringTest());
formOne.show();
}
}
public class ClassTwo {
public ClassTwo(Form a , ClassPojo classPojo) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
// I am adding the new value for the label here inside the back command
classPojo.setStringTest("testing");
a.showBack();
new ClassOne(classPojo);
}
};
formTwo.setBackCommand(back);
}
I'm not sure what the problem is, your example is a bit generic. However, a complete minimal example where the startScreen form instance is not recreated is this one:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
button1.addActionListener(l -> {
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
form2.show();
});
If you don't even want to recreate the form2 instance, then you can do so:
Form startScreen = new Form("Start screen", BoxLayout.y());
Wrapper<Integer> count = new Wrapper<>(1);
Label numberLabel = new Label(count.get() + "");
Button button1 = new Button("Go to Form 2");
startScreen.addAll(numberLabel, button1);
startScreen.show();
Form form2 = new Form("Form 2", BoxLayout.y());
Label label = new Label("Use the back button");
form2.add(label);
form2.getToolbar().setBackCommand("Back", Toolbar.BackCommandPolicy.ALWAYS, ll -> {
count.set(count.get() + 1);
numberLabel.setText(count.get() + "");
startScreen.showBack();
});
button1.addActionListener(l -> {
form2.show();
});
In my opinion, whether or not to recreate the instances of a Form should be evaluated on a case-by-case basis. Among the variables between taking into consideration, according to my modest opinion, there is also the readability of the code and what it does, especially in complex cases.
The overhead of recreating a form instance is negligible so that wouldn't be a problem but in recent years we try to reuse form instances more. Not because of the performance.
The benefit is in minor behaviors e.g. scroll position within the form. These are very hard to replicate.
During testing, I found an easy solution that is adding the label to the constructor. I hope this snippet can be helpful.
public ClassTwo(Form a, ClassPojo classPojo, Label label) {
Command back = new Command("A") {
#Override
public void actionPerformed(ActionEvent evt) {
label.setText(classPojo.getStringTest());
a.showBack();
}
};

Crash on iOS when downloading and inserting in Database

I have a problem with my application on iOS only (iPad 9.7" 11.3.1), it works perfectly on the simulator and on Android device.
When the app is launched for the first time, the user has to log in. This works fine. After that, the menu form is launched and a Dialog is shown on onShowCompleted():
if (!Preferences.get("download", false)) {
dialog = new Dialog("...");
dialog.setLayout(new BorderLayout());
SpanLabel label = new SpanLabel("...");
dialog.addComponent(BorderLayout.CENTER, label);
dialog.show(Display.getInstance().getDisplayHeight() / 5, ..., ..., ...);
}
In the constructor, a method that downloads informations is launched :
if (!Preferences.get("download", false)) {
dlReferentiel();
}
The method :
public void dlReferentiel() {
source = DataSource.getInstance();
// I also tried with invokeAndBlock() but doesn't work
/*
Display.getInstance().invokeAndBlock(() -> requestS());
Display.getInstance().invokeAndBlock(() -> requestB());
Display.getInstance().invokeAndBlock(() -> requestZ());
Display.getInstance().invokeAndBlock(() -> requestA());
*/
requestS();
requestB();
requestZ();
requestA();
}
The requestX methods get informations from server with ConnectionRequest :
(Exemple with requestA() which needs to be the last because it disposes the dialog, it is the only one that calls the UI)
public void requestA() {
ConnectionRequest connectionRequest = new ConnectionRequest() {
ArrayList<A> as = new ArrayList<>();
#Override
protected void readResponse(InputStream in) throws IOException {
JSONParser json = new JSONParser();
Reader reader = new InputStreamReader(in, "UTF-8");
Map<String, Object> data = json.parseJSON(reader);
for (String s : data.keySet()) {
as.add(new A(s, (String) data.get(s)));
}
}
#Override
protected void postResponse() {
source.createAllA(as); // makes insertions with Transaction
ToastBar.showErrorMessage("Référentiel téléchargé", 10);
dialog.dispose();
Preferences.set("download", true);
}
};
connectionRequest.setUrl(URL_A);
connectionRequest.setPost(true);
NetworkManager.getInstance().addToQueue(connectionRequest);
}
I was able to see some logs with a Mac. Each time, the app crashes after the Dialog is shown and few insertions are made. The same error message is output :
HW kbd: Failed to set (null) as keyboard focus
Unable to get short BSD proc info for 513: No such process
Unable to get proc info for 513: Undefined error: 0
I search for informations about this issue but nothing interesting with Codename One.
I don't know what to do with this error, I tried some things like using invokeAndBlock() or changing build hints like ios.keyboardOpen (just because there is "keyboard" in it) but nothing worked.
Thanks for the help.

How to code an iPhone style popup menu in CN1?

It has probably been covered before, but I couldn’t google anything. What is the best approach for making an iPhone-style pop-up selection menu like attached picture? I've tried with a Dialog, but I haven't found an elegant way to add the Commands so they appear nicely and both trigger the action and close the dialog at the same time. And showing a Cancel entry separately is not supported by a ComponentGroup.
See this sample:
Form hi = new Form("Pop");
Button pop = new Button("Pop");
pop.addActionListener(e -> {
Dialog dlg = new Dialog();
// makes the dialog transparent
dlg.setDialogUIID("Container");
dlg.setLayout(BoxLayout.y());
Command optionACmd = new Command("Option A");
Command optionBCmd = new Command("Option B");
Command optionCCmd = new Command("Option C");
Command cancelCmd = new Command("Cancel");
dlg.add(
ComponentGroup.enclose(
new Button(optionACmd),
new Button(optionBCmd),
new Button(optionCCmd)
)).
add(ComponentGroup.enclose(new Button(cancelCmd)));
Command result = dlg.showStretched(BorderLayout.SOUTH, true);
ToastBar.showMessage("Command " + result.getCommandName(), FontImage.MATERIAL_INFO);
});
hi.add(pop);
hi.show();
Which results in this:
Thanks Shai!
I made it into a component in case anybody has a similar need:
class MyPopupMenu extends Dialog {
private Command cancelCmd = null;
MyPopupMenu(boolean includeCancel, Command... commands) {
this(includeCancel?new Command("Cancel"):null, commands);
}
MyPopupMenu(Command cancelOptional, Command... commands) {
super();
setDialogUIID("Container");
setLayout(BoxLayout.y());
setDisposeWhenPointerOutOfBounds(true); //close if clicking outside menu
ComponentGroup group = new ComponentGroup();
for (Command cmd : commands) {
group.add(new Button(cmd));
}
add(group);
this.cancelCmd = cancelOptional;
if (cancelCmd != null) {
add(ComponentGroup.enclose(new Button(cancelCmd)));
}
/**
* show the menu and execute the selected Command,
* or do nothing if Cancel is selected
*/
public void popup() {
Command choice = showStretched(BorderLayout.SOUTH, true);
if (choice != null && choice != cancelCmd) {
choice.actionPerformed(null);
}
}
}
This is awesome, thanks guys. Any reason my buttons seem to be so small? Trying to figure out which style needs to change to increase the height. Changing the Button padding did not seem to change anything. I used the Business theme as a starting point.

Animate a gif file only once

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();
});

Application Won't 'new' when called

I am calling a new xaml application with some parameter inputs using the word new, but it does not seem to be working. I am also attempting to use onclosing to set it to null. When launching it for the first time it works (everything is new), but launching it after it finished, it seems to continue its previous state (brings to finished score board). Here is the snipplet of the code . . .
quizUI = new QuizzUI.MainWindow(App.User, true);
quizUI.Closed += (o, s) =>
{
quizUI = null;
};
quizUI.LaunchQuiz(qSet);
this is hooked to a button event. does anyone know how i can absolutely new this object's state every time? the two paramters are user's info and second one is to shortcut without prompt screen/loading screen.
Here is the code for QuizzUI.MainWindow.LaunchQuizz:
public void LaunchQuiz(GameQuizzSet quiz)
{
this.Hide();
quizz = new QuizzContainer()
{
QSet = quiz,
};
if (isShortCutted)
{
bool? diag = quizz.ShowDialog();
if (diag.HasValue)
{
quizz.Close();
Close();
}
}
else
{
quizz.ShowDialog();
this.Show();
}
}
the QuizzUI.MainWindow allows the user to select their profile and which quiz to execute.

Resources