Codename One https://www.codenameone.com en-us Fri, 23 Oct 2020 08:10:35 +0300 1.2 https://www.codenameone.com https://www.codenameone.com/blog/ 1 2 3 https://wordpress.org/?v=5.5 New TestRunner Component blog/new-testrunner-component.html Fri, 23 Oct 2020 00:00:00 +0300 steve https://www.codenameone.com/blog/new-testrunner-component.html

A while back we added a new TestRunnerComponent that provides a visual UI for running unit tests inside an app. Sometimes, while I’m developing unit tests, I find it easier to write them inside a regular app project instead of in the "tests" directory. This allows me to debug the unit tests in the IDE more easily, just like I debug regular apps. The TestRunner component makes it simple to do this.

All you need to do is create some unit tests in your app (i.e. a class that extends AbstractTest. E.g.

public class MyTest extends AbstractTest {


    /**
     * Overridden to return true so test runs on EDT
    @Override
    public boolean shouldExecuteOnEDT() {
        return true;
    }


    /**
     * Actual body of the test.
    @Override
    public boolean runTest() throws Exception {

        // run tests here.

        return true;
    }

    /**
     * Override toString() so that the test shows up nicely in the testrunner.
    @Override
    public String toString() {
        return "MyTest";
    }
}

You can then just add an instance of each Test class to your TestRunnerComponent. E.g. in your app’s start method you might have something like:

public void start() {
    if(current != null){
        current.show();
        return;
    }
    TestRunnerComponent runner = new TestRunnerComponent();
    runner.add(new MyTest());
    // add other tests here..

    runner.showForm();
}

When you run the app, it will display a form with single button "Run Tests".

TestRunnerComponent

When you press this button, it will run the tests and show the results on the screen.

TestRunnerComponent results

The above screenshot is taken from the CodeRADTests project which contains unit tests for the CodeRAD cn1lib.

]]>
1,001 New TestRunner Component 0 0 0
Smarter, Faster Rendering blog/new-flags-for-rendering-performance.html Fri, 16 Oct 2020 00:00:00 +0300 steve https://www.codenameone.com/blog/new-flags-for-rendering-performance.html

We’ve recently added some new display settings, and a new method, revalidateLater(), which can significantly increase your app’s rendering throughput in some cases. Before I get to the specifics of these changes, let’s review how UI rendering works in Codename One.

The Codename One UI rendering can be broken down into two phases:

  1. Layout - Calculating the bounds for each component on the screen, and "laying" them out.

  2. Painting - Actually drawing each component.

Generally you only want to run the "layout" step when the layout has changed. This would include when components are added or removed from a Container, or when a component’s bounds or position has changed.

When you make changes to a component and you want these changes to be updated on the screen, generally you would call repaint() or revalidate(), depending on whether your changes require a "layout" phase. repaint() will only trigger a "paint" without updating the layout. revalidate() will first layout the container, and then it will paint it.

revalidate() isn’t the only method that can be used to trigger "layout". If you want the UI component changes to be animated, you can use animateLayout(), animateHierarchy(), or several other animateXXX() methods of the Container class.

Unintended Revalidation Triggers

One cause of rendering performance issues is unintended revalidations.

As I mentioned above, revalidation is expensive, so you should strive to revalidate only when necessary, and only revalidate the portions of the UI that require revalidation. It gets tricky, however, because you may not always be aware of revalidations that are triggered by other actions. For example, if you make a change to a style in a Component, it will trigger a revalidation of the component’s parent container. E.g.

myLabel.getStyle().setBgColor(0xff0000);
    // This will trigger a revalidate() call in the parent container!!!

One accidental revalidate() call is not a big deal, but suppose you are setting a whole bunch of styles in sequence:

myLabel.getStyle().setBgColor(0xff0000);
myLabel.getStyle().setFont(...);
myLabel.getStyle().setBgTransparency(0xff);
myLabel.getStyle().setPadding(0,0,0,0);
myLabel.getStyle().setMargin(0,0,0,0);
myLabel.getStyle().setTextDecoration(...);
myLabel.getStyle().setUnderline(...);
myLabel.getStyle().setStrikeThru(...);
myLabel.getStyle().setFgColor(0x0);
myLabel.getStyle().setBorder(...);
    // The above calls will trigger 10 revalidations()!!!

The above example shows some code that sets 10 style properties on a label. This code will trigger 10 calls to revalidate() in "myLabel"'s parent container.

How do we avoid these redundant revalidations?

One simple way to avoid the redundant revalidations in the example above is to set all of the style properties before adding myLabel to a container. Then no revalidations would occur.

However, we have just recently added a display property that will prevent style changes from triggering revalidations at all. Add the following into the init() method of your app’s main class:

CN.setProperty("Component.revalidateOnStyleChange", "false");

Currently I recommend setting this property to "false" in all apps. Future versions of Codename One may change the default behaviour to "false", but for now, the default is "true" because it is possible that some existing apps depend on the current behaviour.

Unintended Revalidation Scope

Another performance trap that you may accidentally step into is a revalidation scope that is greater than you intend. If you call myContainer.revalidate(), the current Codename One default behaviour is to trigger a revalidation on the entire form - not just "myContainer". This behaviour was likely implemented to cover some edge, but it is a major performance killer. To fix this issue, we have added another display property "Form.revalidateFromRoot", which can be set to "false" to prevent this behaviour. E.g. Add the following to your init() method:

CN.setProperty("Form.revalidateFromRoot", "false");

This way, when you call myContainer.revalidate() it will relayout "myContainer" and only "myContainer".

Redundant Revalidations

It is also possible to initiate a number of redundant calls to revalidate() if you’re not careful. E.g. If your UI is build with views that are bound to one or more model objects, and the views are directed to "revalidate" whenever a property of the model is changed, it is possible, and even likely, that your model may generate 10 or 20 property change events in one batch, which will propagate into 10 or 20 revalidation calls on the same container.

For example, consider the following excerpt from a "View" class:

/**
 * A UI view for a Person
 */
public class PersonView extends Container {

    private Label name, description, ...;
    private Person model;

    public PersonView(Person model) {
        this.model = model;
        this.model.addPropertyChangeListener(evt -> update()); (1)
        ...
    }

    /**
     * This method is triggered whenever a property is changed in the model.
     */
    public void update() {
        boolean changed = false;
        if (!Objects.equals(name.getText(), model.getName())) {
            name.setText(model.getName());
            changed = true;
        }
        if (!Objects.equals(description.getText(), model.getDescription()) {
            description.setText(model.getDescription());
            changed = true
        }

        // ...  etc... check all the properties to see if they have changed
        // and update the corresponding UI component accordingly

        if (changed) {
            // If a change has occurred, then revalidate()
            revalidate(); (2)
        }
    }
}
1 In the constructor of this view, we register a PropertyChangeListener on the model so that the update() method will be called whenever a property is changed on the model.
2 Inside the update() method, we trigger a revalidate() if any changes were made in the model that required an update in the view.

Now consider a fairly typical snippet of code where we load data into the model. E.g.:

person.setName("...");
person.setDescription("...");
person.setAge(21);
person.setOnline(true);
person.setHeight(121);
// ... etc... set all the other properties

Given the way that our model is bound to the view, this code will generate a property change event for each property. If we set 30 property values, then we trigger 30 property change events. And each property change event results in the update() method being called, and revalidate() being triggered. Therefore, this code could result in 30 revalidations of the view (and, if we aren’t using CN.setProperty("Form.revalidateFromRoot", "false"), this also results in 30 revalidations of the full form). It is easy to see that this is a waste. You might not notice the performance degradation in simple interfaces, but it will become more evident as the UI grows more complex.

How to fix this

To combat the problem of redundant revalidations, we have introduced a new method, revalidateLater() which will defer the revalidation until just before the next paint cycle. This method will automatically eliminate duplicate revalidations, which will result in a significant performance increase in situations like the one depicted here. In our particular example, if we changed our view code from:

if (changed) {
    revalidate();
}

to

if (changed) {
    revalidateLater();
}

Then changing 30 properties on the model would trigger only a single revalidation instead of 30.

Summary

So summarize the new features discussed in this post:

  1. Revalidation should be done as little as possible for best performance.

  2. Style changes on components trigger revalidations by default.

  3. You can set the "Component.revalidateOnStyleChange" display property to "false" to prevent style changes from triggering revalidations. E.g. In your app’s init() method do:

    CN.setDisplayProperty("Component.revalidateOnStyleChange", "false");
  4. Calling revalidate() on a container, will trigger revalidation of the entire form by default.

  5. You can set the "Form.revalidateFromRoot" display property to "false" to prevent revalidate() from triggering revalidation of the entire form. E.g. In your app’s init() method, do:

    CN.setDisplayProperty("Form.revalidateFromRoot", "false");
  6. You can use revalidateLater() instead of revalidate() to defer revalidation until the next paint cycle, and avoid redundant revalidations. You should prefer revalidateLater() in most cases. The only time when revalidate() might be required is if you need to perform calculations on measurements after revalidation occurs.

]]>
1,002 Smarter, Faster Rendering 0 0 0
New Website Launch blog/new-website-launch.html Fri, 25 Sep 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/new-website-launch.html

Important update below. After a very long journey we will launch the new Codename One website on October 2nd. Since this will only deal with the website itself most things should still work as they did before. Thanks to the cloud dashboard the migration shouldn’t disrupt any users and should be 99% superficial.

The launch will be delayed tentatively to October 9th but possibly even later

One thing that might be impacted are website comments which we will need to migrate to a new system. Initially the new website will launch without comments which we will need to re-integrate.

There might also be some disruption to discussion forum posts as we plan to migrate the discussion forum to a new medium.

Notice that we will try to keep links as close as possible to the previous links but that might be challenging so some links (specifically to the developer guide) will redirect to a new location. There might be a period of DNS reshuffling as the new website will occupy a different server.

That’s one of the motivations for conducting this migration on a Friday

As part of this migration login on the website will no longer be available/required. Since the dashboard is no longer in this site this shouldn’t be an issue. Some pages will still be unavailable and will be added as we move forward.

]]>
1,003 New Website Launch 0 0 0
Removing Old Dashboard blog/removing-old-dashboard.html Fri, 28 Aug 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/removing-old-dashboard.html

We announced a while back that we’re working on a new website. This work was 90% complete but we decided to scrap it in favor of a complete rewrite of the site. That means some links might break and some functionality might be impacted but this is a crucial change for the continued growth of the company.

One thing that we plan to remove entirely is the old dashboard. It will be removed on September 18th in favor of the build cloud. Please let us know of any missing features or problems so we can address them in time for the migration.

We intend to provide support for:

  • Emailing the build results

  • QR code for OTA install

This is in addition to the UI overhaul which is worked on here.

]]>
1,004 Removing Old Dashboard 0 0 0
Security Issues of Cross Platform Tools blog/security-issues-cross-platform-tools.html Fri, 7 Aug 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/security-issues-cross-platform-tools.html

A couple of weeks ago I answered a question on Quora about the security of cross platform tools. I try to rise about my confirmation bias when discussing these things. I won’t discuss Codename One in this context or any other specific tool. Only general ideas.

Security depends a lot on the tools involved and their level of support for security features such as certificate pinning, storage/db encryption etc. Some tools also store the code of the app as plain text or obfuscated scripting code which is still fully readable, this can have a serious impact on security.

In fact this level of insecurity spawned a thriving cottage industry of repackaging. Where people unzip the application and repackage/sign it and upload it to the store under a different package name. Then use ads/payment to earn from the stolen app. This can be very profitable to them as the time gap until detection and takedown process can be pretty long.

Reverse Engineering

Reverse engineering is possible no matter what tool you use. Cross platform tools can make this either easier or harder.

There are plenty of off the shelf tools to reverse engineer native apps. You can literally view the full UI design used by the developer and then search for the event handling code within the decompiled application. E.g. if you have a login form a hacker can find the login button, run the app and find out a lot about the process.

Here the cross platform tools divide into three distinct categories:

  • Native GUI tools — These are usually on par or worse than native apps when it comes to security. The native communication/layout is often visible via standard reverse engineering tools

  • Web tools — Cross platform tools that are based on web technologies are usually very easy to reverse engineer. To a level where a hacker can change JavaScript on the spot or even use web debugging tools to debug the app remotely

  • Lightweight Tools — Tools that render their own UI are usually more secure in that sense. Decompiler tools can’t always see some of these tools and find it really hard to deal with their UI. Such tools can be much harder to reverse engineer than native apps

Obfuscation

Obfuscation is the first line of defense against reverse engineering. It’s an essential tool to make reverse engineering harder.

Some tools and some common native 3rd party libraries, discourage obfuscation. A lot of tools limit the scope of obfuscation which is generally a bad thing to do.

Tips

Things to ask your cross platform tool vendor:

  • Is my code visible in the final binary?

  • What level of obfuscation do I have here? Is there a separately obfuscated scripting language (e.g. javascript)?

  • Can code be injected remotely? This is sometimes presented as a "feature" where you can circumvent the appstore submission process. Apple made that illegal and removed such tools in the past

  • Do you support encrypted storage/DB?

  • Do you support certificate pinning?

  • Do you use custom socket communication and not the OS level connection (this is important as there might be a low level vulnerability in a custom implementation of SSL)?
    It’s more secure to use the OS native APIs when doing networking operations

  • I disable copy and paste?

  • Can I disable the OS screenshot feature in the task manager (this isn’t possible on all OS’s)?

  • Can I detect jailbroken devices?
    Notice that this isn’t always possible and is a bit flaky

  • Do you support biometric authentication primitives

  • Who do I contact when I find something and need help?

]]>
1,005 Security Issues of Cross Platform Tools 0 0 0
Moving to API Level 29 and CEF Update blog/moving-api-level-29-cef.html Fri, 31 Jul 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/moving-api-level-29-cef.html

Steve just updated our CEF support which should now work on all platforms. We updated the original post with additional information. We’ve also updated the build servers to use API level 29 on Android.

The API level change should be seamless to most of you but might impact some edge case functionality and cause some native/cn1libs to fail in odd ways. So be sure to do extra checks on Android when building a new release.

This change is mandated by Google who require that you target the latest Android version within 6 months of its release.

]]>
1,006 Moving to API Level 29 and CEF Update 0 0 0
New Settings UI blog/new-settings-ui.html Fri, 24 Jul 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/new-settings-ui.html

With today’s update we’re releasing a design overhaul for the settings (preferences) application. This overhaul will improve the look and reliability/consistency of this app which is core to Codename One. During this transition period we also have the ability to go back to the legacy UI for 100% compatibility but it will be removed.

Common important features such as "Update Client Libs" are now available in the menu on the top right of the UI. The currently logged in user isn’t listed in the UI only in the about dialog. For most of the UI it doesn’t matter as much since we don’t change as much as we used to. E.g. we now let everyone create a push certificate. This is a pre-cursor to enabling push for everyone with a very low quota for free/basic users.

This isn’t available yet in the servers and I’m not sure when we’ll have that out
settings light
Figure 1. Light Mode
settings dark
Figure 2. Dark Mode

Focus On Design

This is one of the first signs of very big changes we have coming in the next few months. We intend to overhaul a lot of things in the UI, deep functionalities and even in the free/basic tier features.

The design was made by our new in-house designer Renzo whose work you’ve seen for a while now in our new orignal blog post images. He completely redesigned the website a while back but we’re still having issues in getting it all out there…​

He did a lot of great things and I’m confident he’ll help us improve the one part where we sorely lack: good looking by default.

To help him please check out the issues in the issue tracker assigned to him and feel free to provide feedback. Any feedback given during the UI design stage is far more valuable. Later on it’s much harder to revisit/adapt an implementation.

To get started check out and follow these two issues for GUI Builder Redesign and Codenme One Build Redesign. Once the tools incorporate this aesthetic, we’ll bring it to the themes we generate. We plan to overhaul project creation and make it easier to change the pre-existing templates in Codename One in an IDE agnostic way.

I’m pretty excited about that…​

]]>
1,007 New Settings UI 0 0 0
Big Changes and CEF blog/big-changes-jcef.html Fri, 17 Jul 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/big-changes-jcef.html

Today we released one of the biggest changes to Codename Ones simulator in ages. We added the ability to use CEF (Chrome Embedding Framework). This is currently off by default but even if you don’t use it you might feel the impact so it’s crucial that you read this post.

Updated July 31st with additional platform instructions below
Updated August 2nd with correction to the Linux install script
Updated August 4th with another correction to the Linux install script

The TL;DR

The big change for those of you who don’t care about the details is this: FX will no longer install automatically if it’s missing. That might be a good thing for some applications. But if you rely on media/web things might break in the simulator/debugger.

The short term workaround is to install a JVM that supports JavaFX out of the box such as ZuluFX and make sure your IDE uses it. Make sure it’s first in your path and that JAVA_HOME points at it.

Another option is to migrate to CEF which might not be an option right now if your needs are mostly media related. Read on for the details.

Why?

I wrote before about our desire to kick JavaFX and its problems to the curve. There’s no way around it. It’s outdated and buggy.

CEF is literally Chrome. It’s modern and up to date, so newer browser features behave as expected. It’s also easy to debug and has a lot of other great features we can use. It would also free us from some of the JVM dependencies and let us build smaller desktop apps moving forward.

The reason we really need it at this moment is support for WebRTC which isn’t available in the JavaFX version of the browser but is available in Chromium.

You can easily debug CEF BrowserComponent in Chrome by navigating to http://localhost:8088/ in Chrome

How will this Impact Me?

Hopefully you won’t run into any problem. If you’re using a JDK that doesn’t include JavaFX you might run into a problem in this transition period and things might fail. We recommend ZuluFX for now.

Once this transition period is done this should work with any JVM

By default CEF is off but you can turn it on explicitly instead of installing JavaFX.

Turn on CEF

This post was updated on July 31st with details on all platforms

When complete we will automatically download and install CEF on the first activation effectively disabling the JavaFX mode.

If the ~/.codenameone/cef directory is present we assume CEF is installed and try to load it instead of JavaFX.

Mac Install

To install manually download this file. Then perform the following command in the terminal:

unzip ~/Downloads/cef-mac.zip -d ~/.codenameone

To uninstall CEF in case of a problem do:

rm -Rf ~/.codenameone/cef

Windows Install

If you’re using Win32 download this file.

For Win64 download this file.

Open your user directory and search for the .codenameone directory. In that directory unzip the downloaded zip file. It should include a cef directory. If not make sure to unzip the content into a directory named cef.

You can uninstall it by deleting the cef directory at any time.

Linux Install

We only support 64 bit Linux at this time. If there are developers using 32 bit Linux as their desktops please let us know

Download the file this file.

Then install using:

mkdir ~/.codenameone/cef
unzip cef-linux64.zip -d ~/.codenameone/cef
chmod 755 ~/.codenameone/cef/lib/linux64/jcef_helper

To uninstall CEF in case of a problem do:

rm -Rf ~/.codenameone/cef

What’s Missing?

With the CEF pipeline media is implemented using the browser component. So videos literally play in the Chrome browser (seamlessly, you wouldn’t know). This removes the need for JavaFX completely and simplifies a lot of things.

However, there’s one missing piece at the moment: h264 support.

By default JCEF doesn’t include the h264 codec due to patent restrictions. This isn’t a problem for our use case but it means we need to get a binary build of CEF working and the build environment for Chrome is "tough". So right now h264 isn’t working.

Other than that we’re still missing Windows and Linux support. We’re also missing an installer that will deliver CEF seamlessly. All of those will ship together as part of an update in the next couple of weeks once all issues are resolved.

How does this Work?

Up until now the JavaSE port had one version which was JavaSEPort. This is now a base class for three implementations:

  • JavaFX — a compatibility mode implementation which is currently the default.

  • CEF — the new mode which will run if the cef directory is available/

  • JMF — a special case that uses Java Media Framework for media playback instead of JavaFX or JCEF. It has the advantage of being very small. It works very similarly to the CEF approach by searching for the JMF jar in the .codenameone directory and using it if it’s available. We’re not sure this is a use case worth pursuing.

On launch we pick the best option. If CEF is available under the .codenameone directory we pick that implementation. This uses the native library and integrates directly into the UI.

Up Next

Once this migration is done we’ll follow up with some posts on debugging under CEF etc. Please let us know if you run into trouble ASAP.

]]>
1,008 Big Changes and CEF 0 0 0
Diagonal Badging blog/diagonal-badging.html Fri, 10 Jul 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/diagonal-badging.html

Recently I got a question on stackoverflow about doing a diagonal badge that’s actually quite similar to the one on our website. This isn’t hard to do in Codename One but it requires a bit of tinkering so here’s how you would implement that.

As a matter of fact the original code I provided in the answer had a bit of a misbehavior which I’m fixing for this blog post so everything will look correct:

Form hi = new Form("Hi World", BoxLayout.y());

// just filling some space so the layer below will be visible
SpanLabel base = new SpanLabel("Hi World,\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\nLorem Ipsum\n");

// this is the green label
Label green = new Label("Green Stuff") {
    private int actualHeight = 10;

    // we ask for more space so when we rotate the label it won't be clipped out
    @Override
    protected Dimension calcPreferredSize() {
        Dimension d = super.calcPreferredSize();

        // since we asked for more space the background will become a sqare
        // we don't want that so we save the "real" height here
        actualHeight = d.getHeight();
        d.setHeight(d.getWidth());
        return d;
    }

    @Override
    public void paint(Graphics g) {
        // I move the drawing context up and to the left otherwise
        // the banner will be cropped on the corner and look odd
        g.translate(-(actualHeight / 2), -(actualHeight / 2)); (1)

        // we rotate by 45 degrees in radians around the pivot point
        // which is the center of the component
        g.rotateRadians((float)(-Math.PI / 4.0),
                getX() + getWidth() / 2,
                getY() + getHeight() / 2);

        // we save the old color and set a background color then
        // draw the background manually
        int c = g.getColor();
        g.setColor(0xff00);

        // we take extra space so the banner will stretch further
        // I use fill here but I can use draw image if I have an
        // image from the designer that looks better
        g.fillRect(getX() - 50,
                getY() + getHeight() / 2 - actualHeight / 2,
                getWidth() + 100, actualHeight);

        // we let the label draw its content
        super.paint(g);

        // restoring the graphics context to the original value
        g.setColor(c);
        g.resetAffine();
    }
};

// we're drawing the background manually so we must make it transparent
Style s = green.getUnselectedStyle();
s.setBgTransparency(0);

// I want extra side padding because the rotation will cause the sides
// to crop so the text needs to be in the center
s.setPaddingUnit(Style.UNIT_TYPE_DIPS); (2)
s.setPadding(1, 1, 3, 3);

// we're layering the component on top of one another. The green
// label is positioned in the top left coordinate.
Container cnt = LayeredLayout.encloseIn(base,
        FlowLayout.encloseIn(green));
hi.add(cnt);

hi.show();
1 The original post was missing this line to move the context up and to the left
2 It was also missing these two lines

So this is what we see in the new code:

The Correct Banner
Figure 1. The Correct Banner

But the older code cropped things badly causing an arrow effect:

The Arrow Effect
Figure 2. The Arrow Effect

What we see here is the banner being drawn at the edge of the component bounds. As a result the component bounds crop it on the right and bottom producing an arrow.

Moving the drawing a bit up and to the left makes sure we reach the edge at just the right place.

]]>
1,009 Diagonal Badging 0 0 0
Exif Orientation Tag and Smart Downloads blog/exif-orientation-automatic-captured-image-rotation.html Fri, 3 Jul 2020 00:00:00 +0300 https://www.codenameone.com/blog/exif-orientation-automatic-captured-image-rotation.html

On some devices, Capture APIs return images with the correct orientation, meaning that they do not need to be changed to display correctly; on other devices, they return images with a fixed orientation and an EXIF tag that indicates how they must be rotated or flipped to display correctly.

More precisely, the Orientation Tag indicates the orientation of the camera with respect to the captured scene and can take a value from 0 to 8, as illustrated on the page Exif Orientation Tag. For testing purposes, you can download landscape and portrait images with all possible orientation values from the EXIF Orientation-flag example images repository.

What happens if we ignore the Orientation Tag

Suppose we acquire an image with the following code:

Form hi = new Form("Capture Test", BoxLayout.y());
Button button = new Button("Take Photo");
ScaleImageLabel photoLabel = new ScaleImageLabel();
hi.addAll(button, photoLabel);
hi.show();

button.addActionListener(l -> {
    String photoTempPath = Capture.capturePhoto();
    if (photoTempPath != null) { (1)
        try {
            String photoStoragePath = "myPhoto.jpg";
            Util.copy(FileSystemStorage.getInstance().openInputStream(photoTempPath), Storage.getInstance().createOutputStream(photoStoragePath)); (2)
            photoLabel.setIcon(EncodedImage.create(Storage.getInstance().createInputStream(photoStoragePath), Storage.getInstance().entrySize(photoStoragePath))); (3)
            hi.revalidate();
        } catch (IOException ex) {
            Log.p("Error after capturing photo", Log.ERROR);
            Log.e(ex);
            Log.sendLogAsync();
        }

    }
});

A few remarks:

1 photoTempPath is null if the user has cancelled the photo capture;
2 in this case, copying the file from the FileSystemStorage temporary folder to a "secure" location in FileSystemStorage or Storage is not strictly necessary, but it is a good habit that in certain circumstances prevents issues;
3 it is always preferable to use EncodedImage when we want to keep the impact on memory low.

Desired result

On my iPhone, the image is always in portrait orientation:

85854279 64b80800 b7b4 11ea 88e2 98a6d57fc09f

Unwanted result

This is the case of my Samsung Galaxy, the image was taken in portrait, but shown in a different orientation:

85853565 050d2d00 b7b3 11ea 8053 695772ecbfd0

Solving This Problem

All it takes is a small change to the code to solve this issue.

Just replace:

String photoStoragePath = "myPhoto.jpg";
Util.copy(FileSystemStorage.getInstance().openInputStream(photoTempPath), Storage.getInstance().createOutputStream(photoStoragePath));
photoLabel.setIcon(EncodedImage.create(Storage.getInstance().createInputStream(photoStoragePath), Storage.getInstance().entrySize(photoStoragePath)));

with:

String photoSafePath = FileSystemStorage.getInstance().getAppHomePath() + "/myPhoto.jpg"; (1)
Image img = Image.exifRotation(photoTempPath, photoSafePath, 1000); (2)
photoLabel.setIcon(img); (3)
1 In this case, we have to use FileSystemStorage rather than Storage due to a limitation of the exifRotation API, which maybe will be solved in the future;
2 the third parameter is optional, but as explained in the exifRotation Javadoc, the rotation of a high-resolution image is very inefficient, it is better to set the maximum size (width or height) that the image can assume, in this case 1000px, to obtain a significant advantage in processing time on less performing devices;
3 note that the instance of the Image object returned by exifRotation is an EncodedImage, to keep the impact on memory low.

Final result

On my iPhone the result is the same (as expected), while on my Android, taking the same photo, I get:

85862326 e82c2600 b7c1 11ea 9135 657f2a0eead7

This is the desired result. As a final note, I mention the Image.getExifOrientationTag API which allows you to get the EXIF orientation tag of an image, if available.

Network error resistant downloads with automatic resume

When we download a big file, such as a high-resolution image or a video, there are problems that can prevent the download from finishing:

  • The user moves the app to the background or external conditions (such as a phone call) move the app to the background;

  • The operating system enters power saving mode;

  • The Internet connection is lost or any other network error interrupts the download.

A server-side error may also occur, but this cannot be resolved client-side. All the other circumstances mentioned above can, provided that the server supports partial downloads via HTTP headers. Fortunately, this is a feature available by default on most common servers (such as Apache or Spring Boot). Almost all download managers allow to resume interrupted downloads, so I thought it was important to add such a feature to Codename One.

The solution

THe new API Util.downloadUrlSafely safely download the given URL to the Storage or to the FileSystemStorage.

This method is resistant to network errors and capable of resume the download as soon as network conditions allow and in a completely transparent way for the user.

Server requirements

The server must correctly return the Content-Length header and it must supports partial downloads.

Global network error handling requirements

In the global network error handling, there must be an automatic .retry() of the ConnectionRequest in the case of a network error.

I think the best way to show the use of this API is an actual complete example, which you can try as it is in the Simulator and on real devices:

public class MyApplication {

    private Form current;
    private Resources theme;

    public void init(Object context) {
        // use two network threads instead of one
        updateNetworkThreadCount(2);

        theme = UIManager.initFirstTheme("/theme");

        // Enable Toolbar on all Forms by default
        Toolbar.setGlobalToolbar(true);

        // Pro only feature
        Log.bindCrashProtection(true);

        // Manage both network errors (connectivity issues) and server errors (codes different from 2xx)
        addNetworkAndServerErrorListener();
    }

    public void start() {
        if(current != null){
            current.show();
            return;
        }

        String url = "https://www.informatica-libera.net/video/AVO_Cariati_Pasqua_2020.mp4"; // 38 MB

        Form form = new Form("Test Download 38MB", BoxLayout.y());
        Label infoLabel = new Label("Starting download...");
        form.add(infoLabel);

        try {
            Util.downloadUrlSafely(url, "myHeavyVideo.mp4", (percentage) -> {
                // percentage callback
                infoLabel.setText("Downloaded: " + percentage + "%");
                infoLabel.repaint();
            }, (filename) -> {
                // file saved callback
                infoLabel.setText("Downloaded completed");
                int fileSizeMB = Storage.getInstance().entrySize(filename) / 1048576;
                form.add("Checking files size: " + fileSizeMB + " MB");
                form.revalidate();
            });
        } catch (IOException ex) {
            Log.p("Error in downloading: " + url);
            Log.e(ex);
            form.add(new SpanLabel("Error in downloading:\n" + url));
            form.revalidate();
        }

        form.show();


    }

    public void stop() {
        current = getCurrentForm();
        if(current instanceof Dialog) {
            ((Dialog)current).dispose();
            current = getCurrentForm();
        }
    }

    public void destroy() {
    }

    private void addNetworkAndServerErrorListener() {
        // The following way to manage network errors is discussed here:
        // https://stackoverflow.com/questions/61993127/distinguish-between-server-side-errors-and-connection-problems
        addNetworkErrorListener(err -> {
            // prevents the event from propagating
            err.consume();

            if (err.getError() != null) {
                // this is the case of a network error,
                // like: java.io.IOException: Unreachable
                Log.p("Error connectiong to: " + err.getConnectionRequest().getUrl(), Log.ERROR);
                // maybe there are connectivity issues, let's try again
                ToastBar.showInfoMessage("Reconnect...");
                Timer timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        err.getConnectionRequest().retry();
                    }
                }, 2000);
            } else {
                // this is the case of a server error
                // logs the error
                String errorLog = "REST ERROR\nURL:" + err.getConnectionRequest().getUrl()
                        + "\nMethod: " + err.getConnectionRequest().getHttpMethod()
                        + "\nResponse code: " + err.getConnectionRequest().getResponseCode();
                if (err.getConnectionRequest().getRequestBody() != null) {
                    errorLog += "\nRequest body: " + err.getConnectionRequest().getRequestBody();
                }
                if (err.getConnectionRequest().getResponseData() != null) {
                    errorLog += "\nResponse message: " + new String(err.getConnectionRequest().getResponseData());
                }
                if (err.getConnectionRequest().getResponseErrorMessage() != null) {
                    errorLog += "\nResponse error message: " + err.getConnectionRequest().getResponseErrorMessage();
                }
                Log.p(errorLog, Log.ERROR);

                Log.sendLogAsync();
                ToastBar.showErrorMessage("Server Error", 10000);
            }
        });
    }

}

Safe Uploads?

Implementing uploads with the same features (network error resistance and automatic resume) is more complex, because in this case we do not have a reference standard available by default on the most common servers.

Moreover, the possibility of partial uploads assumes that, after a network error, the server must keep the partially uploaded file and there are no ambiguities about which client has partially uploaded which file.

Applications such as Dropbox, Google Drive, OwnCloud and similar use specific internal standards. As far as I’m concerned, I’m almost completed deploying my own client-server solution to allow secure, network error-resistant with automatic resume uploads with Codename One and Spring Boot. This solution, however, is too specific to be included in the Codename One API and, anyway, I still have to do a lot of testing to make sure it works as it should. I’ll possibly publish a tutorial about it when it is finished.

]]>
1,010 Exif Orientation Tag and Smart Downloads 0 0 0
Dark Mode blog/dark-mode.html Fri, 26 Jun 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/dark-mode.html

We recently added support to detect whether a device is running in dark/light mode based on this issue. Some of the code in the implementation is also derived from that issue submitted by Javier.

Detecting Dark Mode

Dark mode can be detected using APIs in the CN and Display classes. Specifically isDarkMode() and setDarkMode(Boolean).

Notice that isDarkMode() returns Boolean and not boolean. This means that null is a valid value for this method. The case of null indicates that dark mode detection isn’t available or isn’t working on this platform.

You can override the dark mode setting for the platform using setDarkMode().

At this time dark mode detection only works on iOS, Android and JavaScript. We tried adding desktop support for that, but it proved a bit challenging. UWP detection isn’t supported at the moment.

We don’t currently have an event based API for dark mode detection. While nice, this isn’t universally supported and can be circumvented with a simple timer.

Native/Builtin Theme Support

Ideally, the app would just switch to dark mode seamlessly but right now this isn’t the case. The theme constant darkModeBool changes some deep core theme styles to match dark mode if isDarkMode() is true. In the future it might trigger a dark version of the native theme. At the moment we don’t have dark versions of the themes.

To create a dark version of your app create a new resource file for dark mode and load it conditionally based on dark mode. You can do the same for CSS by creating a CSS file called dark.css and editing your build.xml file to replicate the CSS conversion line. Specifically:

<target name="-cn1-compile-css" if="codename1.cssTheme">
    <java jar="${user.home}/.codenameone/designer_1.jar" fork="true" failonerror="true">
        <jvmarg value="-Dcli=true"/>
        <arg value="-css"/>
        <arg file="css/theme.css"/>
        <arg file="src/theme.res"/>
    </java>
    <java jar="${user.home}/.codenameone/designer_1.jar" fork="true" failonerror="true">
        <jvmarg value="-Dcli=true"/>
        <arg value="-css"/>
        <arg file="css/dark-theme.css"/>
        <arg file="src/dark-theme.res"/>
    </java>
</target>

Then in Java code you can load a different theme and change themes at runtime using our builtin theming.

You can put two themes in a single resource file but since most settings can’t be reused between dark/light the benefit is limited

You can load a new resource file theme by using:

theme = UIManager.initFirstTheme("/resource");

You can then apply the theme to the current form dynamically using refreshTheme() on the form.

Moving Forward

We hope to add additional dark/light templates and also port the native themes to include dark counterparts. This work isn’t scheduled yet but this is the direction.

]]>
1,011 Dark Mode 0 0 0
Moving Away from Java FX blog/moving-away-from-fx.html Fri, 19 Jun 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/moving-away-from-fx.html

Codename One itself never depended on JavaFX. This kept us small and performant. However, we need JavaFX to support HTML and media in the simulator and on the desktop ports. This was a choice we made easily back in the Java 8 days. JavaFX was integrated into the official JDK and this was an easy choice to make.

Then Java 9 came out and everything broke. Most JVMs ship without JavaFX now and downloading it dynamically for the simulator is error prone to say the least. Even I had problems setting up our environment on some foreign machines. Every day we need to deal with multiple support queries and people who have issues with VM configuration. 99% are due to the pain of dealing with JavaFX installation on top of the VM.

Not Worth Fixing

There are many approaches we could take to try and solve this. All of them suck but they are possible…​

We could create different versions of Codename One for each platform and ship with our own OpenJDK port that includes everything we need. This would have ballooned the size of install and made it harder for you to customize/tinker with Codename One.

The problem is we’d still be stuck with FX.

The main reason for using FX is the BrowserComponent. Swing just doesn’t have a decent browser widget and FX provides the closest thing to a browser we can use…​

The thing is, it still sucks. Newer web standards aren’t supported. Debugging is difficult and it crashes…​ A lot.

A Better Way

Recently IntelliJ took the same path, they decided to deprecate the use of JavaFX in favor of JCEF.

If we migrate to JCEF we’d have access to the latest Chromium APIs and tools. Ideally we’d also enjoy better stability, control and JVM compatibility. The drawback is that we’d need to write native code and possibly increase the Codename One download size.

The big missing piece here is media. We’re still testing the waters on this but a good direction might be to use the media capabilities of Chromium to show things in the simulator and desktop ports.

Compatibility

In order to maintain compatibility for developers using the desktop port we’ll keep the existing implementation that relies on JavaFX for the short term. Since the desktop port packages the VM within this shouldn’t be a problem.

However, we will change the default build to use JCEF once we deem this stable enough and might eventually sunset the FX port entirely based on your feedback. This will have a big size advantage for developers as we’ll be able to package a smaller VM without the JavaFX dependency.

]]>
1,012 Moving Away from Java FX 0 0 0
Samsung Lowers Resolution Randomly blog/samsung-lowers-resolution-randomly.html Fri, 5 Jun 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/samsung-lowers-resolution-randomly.html

A few weeks ago we got this question on stackoverflow. At first I didn’t think this issue was special…​ But as the investigation continued it became clear that we’re facing a weird issue…​

The issue started innocently enough. A device whose native resolution is high was rendering the UI in low resolution. This can happen because of a new DPI setting or configuration in a new SDK.

But the odd thing was this: if the apps package name was changed the resolution went back to normal!

It’s Samsung’s Fault

Skipping to the end: it’s Samsung’s fault. The problem starts shortly after the submission to the play store. Samsung classifies the app as a game and in order to increase performance it runs it with a lower resolution/density.

Use the contact option from this app and ask them to reclassify your app so it isn’t resized.

This information was available online but was remarkably hard to discover since we incorrectly assumed Google was at fault and didn’t think it was Samsung. There was no incriminating information in the console output that we could find or any hint of what had happened.

]]>
1,013 Samsung Lowers Resolution Randomly 0 0 0
RAD Chat Room - Part 5 blog/rad-chatroom-part-5.html Fri, 29 May 2020 00:00:00 +0300 steve https://www.codenameone.com/blog/rad-chatroom-part-5.html

This is part 5 of the RAD Chatroom tutorial. You can find part 1 here, part 2 here, part 3 here and part 4 here.

Adding A Photo Capture Feature

Most messaging applications include the ability to add photos to messages. Let’s add this feature to our chat app now.

First we’ll define a new action called "capturePhoto", and add to the the TEXT_ACTIONS category of our view node.

public static final ActionNode capturePhoto = action(
        icon(FontImage.MATERIAL_CAMERA)
);

...

ViewNode viewNode = new ViewNode(
    actions(ChatRoomView.SEND_ACTION, send),
    actions(ProfileAvatarView.PROFILE_AVATAR_CLICKED_MENU, phone, videoConference),
    actions(ChatBubbleView.CHAT_BUBBLE_LONG_PRESS_MENU, likeAction),
    actions(ChatBubbleView.CHAT_BUBBLE_BADGES, likedBadge),
    actions(ChatRoomView.TEXT_ACTIONS, capturePhoto) (1)
);
1 Added capturePhoto action to the TEXT_ACTIONS category so that it will appear as a button beside the text field.

And we’ll also add a handler for this action, which will capture a photo, and emed the photo in a message that we will add to the chat room’s view model.

addActionListener(capturePhoto, evt->{
    evt.consume();
    String photoPath = Capture.capturePhoto();
    if (photoPath == null) {
        // User canceled the photo capture
        return;
    }

    File photos = new File("photos"); (1)
    photos.mkdirs();
    Entity entity = evt.getEntity();
    File photo = new File(photos, System.currentTimeMillis()+".png");
    try (InputStream input = FileSystemStorage.getInstance().openInputStream(photoPath);
            OutputStream output = FileSystemStorage.getInstance().openOutputStream(photo.getAbsolutePath())) {
        Util.copy(input, output);

        ChatBubbleView.ViewModel message = new ChatBubbleView.ViewModel();
        message.attachmentImageUrl(photo.getAbsolutePath()); (2)
        message.isOwn(true);
        message.date(new Date());
        EntityList messages = entity.getEntityList(ChatRoom.messages); (3)
        if (messages == null) {
            throw new IllegalStateException("This chat room has no messages list set up");
        }
        messages.add(message); (4)

    } catch (IOException ex) {
        Log.e(ex);
        ToastBar.showErrorMessage(ex.getMessage());
    }
});
1 We will create a directory named "photos" where we store all of the photos for the app.
2 Set the path of this photo under attachmentImageUrl. The ChatBubbleView will accept http, https, and file URLs, as well as storage keys. It will render them correctly in the view according to the type of URL it is.
3 The "entity" of this event is the view model for the ChatRoomView. Here we use the ChatRoom.messages tag to access the messages list in a loosely coupled way. This code will work even if we change the class that we use for the ChatRoomView’s view model.
4 Adding the message to the messages entity list will trigger a list change event and it will be rendered automatically in the chat room.

Now, let’s fire the chat up again and take it for a test drive.

The capturePhoto action is rendered as a button beside the input text field
Figure 1. The capturePhoto action is rendered as a button beside the input text field

You should now be able to click on the "capture photo" button to capture an image. In the simulator, it will open a file dialog to select an image. On device, it will activate the devices camera so that you can take a photo. After capturing an image, it should be added to the chat inside a message bubble as shown below:

Photo appears in chat after capture
Figure 2. Photo appears in chat after capture

Linking to a Back-end Chat Server

In this tutorial we created a mock chat application in order to demostrate the ChatRoomView, which is a user interface component. It did not include any integration with a server so it doesn’t allow you to actually chat with other people. Linking to a server is not difficult, and the MVC architecture of this example should make it very clear how the integration should occur. I’ll leave this integration as an exercise for the reader. As a starting point, I recommend checking out the cn1-websockets library, and its chat demo.

]]>
1,014 RAD Chat Room - Part 5 0 0 0
RAD Chat Room - Part 4 blog/rad-chatroom-part-4.html Fri, 22 May 2020 00:00:00 +0300 steve https://www.codenameone.com/blog/rad-chatroom-part-4.html

This is part 4 of the RAD Chatroom tutorial. You can find part 1 here, part 2 here and part 3 here.

Adding More Actions

So far we’ve implemented the basic requirements of a chat room. It can display messages, show particpants, and it allows users to send new messages. Now let’s go a step further and add some more actions. CodeRAD views like ChatRoomView allow for customization in a variety of ways, but the two primary methods are:

  1. Actions

  2. View properties

We’ve already used one action to implement the "send" function. As a reminder, we defined the action in our controller, then we passed it as an attribute to the ViewNode when creating the view:

public static final ActionNode send = action( (1)
    enabledCondition(entity-> {
        return !entity.isEmpty(ChatRoom.inputBuffer);
    }),
    icon(FontImage.MATERIAL_SEND)
);
....
ViewNode viewNode = new ViewNode(
    actions(ChatRoomView.SEND_ACTION, send) (2)
);
....
ChatRoomView view = new ChatRoomView(createViewModel(), viewNode, f); (3)
1 >Defining the "send" action.
2 Adding the "send" action to the view node, under the ChatRoomView.SEND_ACTION category. The category is a hint to the view about where and how the action should be incorporated into the View.
3 Creating new ChatRoomView, passing our ViewNode as a parameter
A ViewNode is a user interface descriptor that can be used to customize the behaviour of a View. It provides a declarative way to define complex user interfaces in a simple way. For the purpose of this tutorial, we will only use the node as a means to pass actions to the ChatRoomView.

The "Send" action was added to the ChatRoomView.SEND_ACTION category, but the ChatRoomView also supports some other categories:

  1. ChatBubbleView.CHAT_BUBBLE_CLICKED - An action that will be "fired" when the user clicks a chat bubble.

  2. ChatBubbleView.CHAT_BUBBLE_LONG_PRESS - An action that will be "fired" when the user long presses a chat bubble.

  3. ChatBubbleView.CHAT_BUBBLE_CLICKED_MENU - Actions that will be displayed in a popup-menu when the user clicks on a chat bubble. This category many include more than one action, and all of supplied actions will be included as menu items in the menu.

  4. ChatBubbleView.CHAT_BUBBLE_CLICKED_MENU - Actions that will be displayed in a popup-menu when the user long presses on a chat bubble.

  5. ChatBubbleView.CHAT_BUBBLE_LONG_PRESS_MENU - Actions that will be displayed in a popup-menu when the chat bubble is long pressed.

  6. ChatBubbleView.CHAT_BUBBLE_BADGES - Actions in this category will be rendered as "badge" icons next to the chat bubble. This is useful, for example, for displaying a "Like/Heart" badge on a chat bubble.

  7. ProfileAvatarView.PROFILE_AVATAR_CLICKED - An action that will be "fired" when the user clicks on one of the profile avatars next to a chat bubble, or in the title component.

  8. ProfileAvatarView.PROFILE_AVATAR_LONG_PRESS - An action that will be "fired" when the user long presses on one of the profile avatars.

  9. ProfileAvatarView.PROFILE_AVATAR_CLICKED_MENU - Actions in this category will be rendered in a popup menu when the user clicks on an avatar.

  10. ProfileAvatarView.PROFILE_AVATAR_LONG_PRESS_MENU - Actions in this category will be rendered in a popup menu when the user long presses on an avatar.

  11. ChatRoomView.TEXT_ACTIONS - Actions in this category will be rendered as buttons next to the text input field. This is an appropriate place to add "Photo" or "Video" capture capabilities.

Adding Phone and Video Conferencing

To get our feet wet with actions, let’s add some options to initiate a phone-call or video conference with one of the participants. When the user taps on a profile’s avatar, we’ll present the user with a menu to start a call or video conference.

In the ChatFormController, we’ll add a couple of new actions.

public static final ActionNode phone = action(
    icon(FontImage.MATERIAL_PHONE)
);

public static final ActionNode videoConference = action(
    icon(FontImage.MATERIAL_VIDEOCAM)
);

...

ViewNode viewNode = new ViewNode(
    actions(ChatRoomView.SEND_ACTION, send),
    actions(ProfileAvatarView.PROFILE_AVATAR_CLICKED_MENU, phone, videoConference) (1)
);
1 We add the phone and videoConference actions to the ViewNode in the ProfileAvatarView.PROFILE_AVATAR_CLICKED_MENU category so that they’ll be rendered in a popup-menu when the user presses on an avatar.

Now run the app and click on the title component:

Menu when clicking on the title component
Figure 1. Menu when clicking on the title component

Or tap on an avatar next to one of the chat bubbles:

Pop-up menu when tapping on an avatar
Figure 2. Pop-up menu when tapping on an avatar

Currently, clicking on the "phone" or "camera" icon doesn’t do anything because we haven’t defined a handler. Let’s do that now:

addActionListener(phone, evt->{
    evt.consume();
    if (!CN.canDial()) {
        Dialog.show("Not supported", "Phone calls not supported on this device", "OK", null);
        return;
    }
    if (evt.getEntity().isEmpty(Person.telephone)) {
        Dialog.show("No Phone", "This user has no phone number", "OK", null);
        return;
    }

    String phoneNumber = evt.getEntity().getText(Person.telephone);
    CN.dial(phoneNumber);

});

In this handler we first check to see if the platform supports phone calls, and fail with a dialog if it doesn’t. Then we check if the entity in question has a phone number. This code makes use of loose-coupling as we using the Person.telephone tag to check for a phone number rather than a particular property. This will allow this code to work with any entity that has such a property. We also make use of the handy Entity.isEmpty(Tag) method, which will return true if this entity doesn’t have a matching property, or if the entity has the property, but has an "empty" value for it.

If you try the app out and attempt to phone any of the users, you’ll receive this dialog:

Currently our ChatAccount entity doesn’t include any properties with the Person.telephone tag
Figure 3. Currently our ChatAccount entity doesn’t include any properties with the Person.telephone tag, so attempting to phone a user will yield this error dialog

Let’s remedy this situation by adding a property to the ChatAccount entity type.

package com.codename1.cn1chat;
import com.codename1.rad.models.Entity;
import com.codename1.rad.models.EntityType;
import static com.codename1.rad.models.EntityType.tags;
import com.codename1.rad.models.StringProperty;
import com.codename1.rad.schemas.Person;
import com.codename1.rad.schemas.Thing;

/**
 * View model for an account profile.
 * @author shannah
 */
public class ChatAccount extends Entity {
    // The name property
    public static StringProperty name, thumbnailUrl, phone;

    private static final EntityType TYPE = new EntityType() {{
        name = string(tags(Thing.name));
        thumbnailUrl = string(tags(Thing.thumbnailUrl));
        phone = string(tags(Person.telephone)); (1)
    }};
    {
        setEntityType(TYPE);
    }

    public ChatAccount(String nm, String thumb, String phoneNum) {
        set(name, nm);
        set(thumbnailUrl, thumb);
        set(phone, phoneNum);
    }
}
1 Creating the phone property as a string property with the Person.telephone tag.

And we’ll update the code in our ChatFormController that creates our participants to add a phone number.

Adding a phone number to the George account in the view controller. We leave Kramer’s phone number null:

room.addParticipants(
    new ChatAccount("George", georgeThumb, "712-555-1234"),
    new ChatAccount("Kramer", kramerThumb, null)
);

Let’s start up the app again. There are a few things to notice here:

  1. If you press on either George or Kramer’s avatar next to one of their chat bubbles, and try to phone them, they’ll both give you the "This user has no phone number" message. Thats because the avatar that appears next to the chat bubble is actually the ChatMessage.ViewModel entity, and not our ChatAccount entity. The ChatMessage.ViewModel entity doesn’t include a telephone field. The ChatAccount entities are only used to render the title component of the form.

  2. If you try to phone Kramer via the title component, you’ll get the same "This user has no phone number" message. This is correct, because we didn’t give Kramer a phone number.

  3. If you try to phone George via the title component, it will dial the number that we registered with the George account. (If you’re running in the simulator, it won’t dial…​ it will just display a message in the console indicating that it is dialing the number).

This is progress, but why don’t we save the user the agony of having to click "phone" to find out if the app can actually make a phone call to that user. We have two options for this, we can either "disable" the phone action conditionally, like we did for the "send" action when the input field is empty. This will still show the phone button in the menu, but it will be greyed out and disabled. Alternatively we could actually remove the phone action in such cases so that it isn’t displayed at all for entities that don’t support it.

Let’s try it both ways:

public static final ActionNode phone = action(
    icon(FontImage.MATERIAL_PHONE),
    enabledCondition(entity->{
        return CN.canDial() && !entity.isEmpty(Person.telephone);
    })
);

Result:

Kramer’s phone button is disabled because we didn’t provide a phone number for him
Figure 4. Kramer’s phone button is disabled because we didn’t provide a phone number for him

If we want to remove the action from menus where it isn’t supported, then we simply change enabledCondition() to condition().

Removing the phone action for entities that don’t have a phone number:

public static final ActionNode phone = action(
    icon(FontImage.MATERIAL_PHONE),
    condition(entity->{ (1)
        return CN.canDial() && !entity.isEmpty(Person.telephone);
    })
);
1 We use the condition(…​) attribute instead of enabledCondition(…​) to disable/hide the action

And the result:

Kramer has no
Figure 5. Kramer has no "phone" option now because he doesn’t have a phone number

Adding a "Like" Badge

Most messaging apps provide a way to "like" a chat message. Let’s add this functionality to our app by using the ChatBubbleView.CHAT_BUBBLE_BADGES category to display the "liked" badge. We’ll use the ChatBubbleView.CHAT_BUBBLE_LONG_PRESS_MENU category to display the toggle button for the user to "like" and "unlike" the message.

public static final ActionNode likedBadge = UI.action(
    UI.uiid("ChatBubbleLikedBadge"), (1)
    icon(FontImage.MATERIAL_FAVORITE),
    condition(entity->{ (2)
        return !entity.isFalsey(ChatMessage.isFavorite); (3)
    })
);

public static final ActionNode likeAction = UI.action(
    icon(FontImage.MATERIAL_FAVORITE_OUTLINE),
    uiid("LikeButton"), (4)
    selected(icon(FontImage.MATERIAL_FAVORITE)), (5)
    selectedCondition(entity->{
        return !entity.isFalsey(ChatMessage.isFavorite); (6)
    })

);

...

ViewNode viewNode = new ViewNode(
    actions(ChatRoomView.SEND_ACTION, send),
    actions(ProfileAvatarView.PROFILE_AVATAR_CLICKED_MENU, phone, videoConference),
    actions(ChatBubbleView.CHAT_BUBBLE_LONG_PRESS_MENU, likeAction), (7)
    actions(ChatBubbleView.CHAT_BUBBLE_BADGES, likedBadge) (8)
);
1 We set the UIID of the badge to "ChatBubbleLikedBadge" which is a style defined in the RADChatRoom cn1lib’s stylesheet. It will make the badge small and red.
2 Use the condition() attribute to ensure that the "liked" badge only shows up if the message has been liked.
3 We are using the convenience method Entity.isFalsey(Tag) to determine if the chat message has been liked. This returns "true" if the value of this field is anything "falsey", like null, or "", or 0, or false. This allows for flexibility about how the view model wants to store whether the message is a favourite or not.
4 We define a UIID for the "Like" action so that we can make the button look how we like.
5 We use the selected(…​) attribute on the likeAction to define a different icon for the action when the action is "selected".
6 We use selectedCondition() on the like action to cause the action to be selected conditionally on whether the message is "liked". This works similar to the condition() and enabledCondition() attributes, except this will affect the selected state of the action’s button. The presence of this attribute causes the button to be rendered as a toggle button instead of a regular button.
7 We add the like action to the CHAT_BUBBLE_LONG_PRESS_MENU category.
8 We add the liked action to the CHAT_BUBBLE_BADGES category.

And, of course, we need to handle the "like" action to toggle the property on and off in the view model.

addActionListener(likeAction, evt->{
    evt.consume(); (1)
    Entity chatMessage = evt.getEntity();
    chatMessage.setBoolean( (2)
            ChatMessage.isFavorite, (3)
            chatMessage.isFalsey(ChatMessage.isFavorite) (4)
    );
});
1 We consume the event so that the view knows that we handled it. This prevents any default behaviour from conflicting.
2 We use the Entity.setBoolean(…​) method to signify that we are setting the value as a boolean. This will ensure that the value is converted to the correct type for the underlying property.
3 We use the ChatMessage.isFavorite tag to target the field for loose coupling. The ChatBubbleView.ViewModel class that we’re using does implement a property with this tag, but we are writing code in such a way that we don’t need to care about which property it is.
4 Again using isFalsey() to get the current value of the flag, and we toggle it to be opposite.

Finally, our "Like" button will be a heart icon. When selected it will be a filled heart icon. When unselected, it will be contour of a heart. We specified a UIID of "LikeButton" for this action in its definition. We just need to add this style to our stylesheet. Open the project’s stylesheet (at css/theme.css) and add the following:

LikeButton {
    background-color:transparent;
    cn1-border-type: none;
    color: red;
}

And the test drive…​ Open up the app again, long press on a chat message, and click the "Like" action. Then it should display a red heart badge next to the chat bubble.

Menu appears when you long-press on a chat bubble. Clicking on the button will fire the
Figure 6. Menu appears when you long-press on a chat bubble. Clicking on the button will fire the "Like" action
After we
Figure 7. After we "like" George’s message, it displays the "liked" badge

Next Week

For our final part we’ll cover adding a photo capture feature.

]]>
1,015 RAD Chat Room - Part 4 0 0 0
RAD Chat Room - Part 3 blog/rad-chatroom-part-3.html Fri, 15 May 2020 00:00:00 +0300 steve https://www.codenameone.com/blog/rad-chatroom-part-3.html

This is part 3 of the RAD Chatroom tutorial. You can find part 1 here and part 2 here.

Adding Text Messages from Other Users

Our current example only includes messages that the current user posted themself. I.e. We only have chat bubbles on the right-hand side of the view. Let’s add some more sample data to our view model to give us a feel for how a real chat will look. In the ChatFormController class, we’ll change the createViewModel() method as follows:

Creating more interesting sample data for the ChatRoom’s view model. We add messages from both the current user and other users.

// Create a view model for the chat room
private Entity createViewModel() {
    ChatRoomView.ViewModel room = new ChatRoomView.ViewModel();

    // Make up some dummy times for the chat messages.
    long SECOND = 1000l;
    long MINUTE = SECOND * 60;
    long HOUR = MINUTE * 60;
    long DAY = HOUR * 24;

    // Make first message 2 days ago.
    long t = System.currentTimeMillis() - 2 * DAY;

    // Some thumbnails for the avatars of the participants
    String georgeThumb = "https://weblite.ca/cn1tests/radchat/george.jpg";
    String kramerThumb = "https://weblite.ca/cn1tests/radchat/kramer.jpg";

    room.addMessages(createDemoMessage("Why couldn't you have made me an architect? You know I always wanted to pretend that I was an architect. "
            + "Well I'm supposed to see her tomorrow, I'm gonna tell her what's goin on. Maybe she likes me for me.",
            new Date(t), "George", georgeThumb));
    t += HOUR;
    room.addMessages(createDemoMessage("Hey", new Date(t), "Kramer", kramerThumb));
    t += MINUTE;
    room.addMessages(createDemoMessage("Hey", new Date(t), null,  null));

    return room;
}

// Create a single demo message
private Entity createDemoMessage(String text,
        Date datePosted,
        String participant,
        String iconUrl) {
    ChatBubbleView.ViewModel msg = new ChatBubbleView.ViewModel();
    msg.messageText(text)
            .date(datePosted)
            .iconUrl(iconUrl)
            .isOwn(participant == null);
    if (participant != null) {
        msg.postedBy(participant);
    }
    return msg;
}

To make things easier to read, I’ve broken out the code for creating a message into a separate method so we can call create new messages more easily. I’ve created a couple of pretend users, "George" and "Kramer", and I’ve provided some thumbnail URLs for them, which can be used as avatars in the chat room.

And the result:

Chat room now includes messages from two other users
Figure 1. Chat room now includes messages from two other users, George and Kramer

Notice that it shows the time of the first chat message, but not the others. This is intentional. The chat room will only show the time of messages if there is a long delay between it and the previous message. You can see the time of each message by swiping to the left:

Swipe to the left to reveal the date and time of each message
Figure 2. Swipe to the left to reveal the date and time of each message
Adding the "Participants" Title Component

Recall the screenshot of the finished app, in which the form title included a list of participants in the chat room with their avatars.

Participants title component with avatars
Figure 3. Participants title component with avatars

Let’s add this now by adding some participants to the view model. The ChatRoomView.ViewModel includes methods to directly add participants to the model via addParticpant(Entity…​ participants). Each participant entity should implement the Thing.name or Thing.thumbnailUrl tags, or both. If Only Thing.name is provided, then it will generate an avatar with the first letter of their name. If Thing.thumbnailUrl is provided, then it will use the image at this url as the avatar.

Let’s begin by creating a custom entity/view model named "ChatAccount" which will be used as participants in the chat. Create a new Java class named "ChatAccount" with the following contents:

The ChatAccount entity will be used to encapsulate profiles for participants in the chat room.

package com.codename1.cn1chat;
import com.codename1.rad.models.Entity;
import com.codename1.rad.models.EntityType;
import static com.codename1.rad.models.EntityType.tags;
import com.codename1.rad.models.StringProperty;
import com.codename1.rad.schemas.Thing;

/**
 * View model for an account profile.
 * @author shannah
 */
public class ChatAccount extends Entity {

    // The name property
    public static StringProperty name; (1)

    private static final EntityType TYPE = new EntityType() {{ (2)
        name = string(tags(Thing.name)); (3)
    }};
    {
        setEntityType(TYPE); (4)
    }

    public ChatAccount(String nm) {
        set(name, nm);
    }

}
1 The "name" property of our entity.
2 Define an entity type for the ChatAccount entity. The entity type defines which properties are supported by the ChatAccount entity.
3 Generating the "name" property as a string property. Notice that we assign the Thing.name tag to this property, which will allow views to bind to it.
4 Set the entity type inside the "instance" initializer so that all ChatAccount objects have the same entity type. This could have been placed inside the constructor, but placing it simply inside the initializer (i.e. inside {..}) makes for a little less typing, and also helps to signify the declarative nature of this call.

I’ve added some notes about the key lines of the code listing above which should help to get you up to speed if this is your first custom entity. This entity defines a single property, "name". If we were to define this entity as a POJO (Plain-Old Java object), the class might look something like:

What the ChatAccount entity would look like if implemented as a POJO (Plain old java object).

public class ChatAccount {
    private String name;
    public ChatAccount(String name) {
        this.name = name;
    }
}
So why not use a POJO for our entity?

The Entity class, together with EntityType provide lots of useful features such as bindable properties, property change events, data conversion, observability, and reflection. All of these features are necessary to enable the creation of loosely coupled components with clean separation between models, views, and controllers. As you’ll see, this loose coupling greatly enhances our ability to produce complex, reusable components, which results in better apps with less code.

Getting and Setting Properties on Entities

Before proceeding, its worth discussing the basics of how to use entities. The Entity class allows us to get and set properties without needing to define getter and setter methods. It also includes a rich set of convenience methods for handling data-conversion. Finally, one of the most powerful features of entities is its loose coupling. It is possible to get and set property values without any knowledge of which properties exist in the entity, via tags.

First things first: Getting and setting property values.

Getting and setting property values using a direct property reference.

ChatAccount account = new ChatAccount("George");
String name = account.get(ChatAccount.name);  // "George"
account.set(ChatAccount.name, "Kramer");
name = account.get(ChatAccount.name);  // "Kramer"

This code is tightly coupled to the ChatAccount entity because it directly references the ChatAccount.name property. In some cases, this tight coupling is fine. In other cases, such as when you want to develop a reusable component that requires a "name" property, you may prefer to use "loose" coupling, as follows:

Entity account = ...;  // Could be any entity, but happens to be a ChatAccount
account.setText(Thing.name, "George");
String name = account.getText(Thing.name); // "George"
The CodeRAD library includes a hierarchy of schemas which define tags that may be used to tag entity properties. These schemas were adapted from https://schema.org, which defines entities and properties for a large number of common object types. All schemas extend the base schema, Thing. Some common tags include name, identifier, and thumbnailUrl. When creating reusable components, you can use these schema "tags" to access property values of view models in loosely coupled way. The javadocs for View components should list the tags that it expects on its view models, so you can tag the properties on your entities accordingly. For a full list of schemas, check out https://schema.org/docs/full.html. Only a subset has been ported into the CodeRAD library. More will be added over time, and you may contribute your own with a pull request.

Finally…​ Adding the Participants

After a lengthy discussion of Entities, Entity types, Tags, and Properties, we can now go ahead and add some participants to the chat room. Add the following inside our createViewModel() method of the ChatFormController class:

room.addParticipants(new ChatAccount("George"), new ChatAccount("Kramer"));

This adds two profiles to the chat room as participants. Now, if we launch the app we’ll see the form title replaced with the following avatars.

Title component with avatars generated from our participants
Figure 4. Title component with avatars generated from our participants

Now, let’s go a step further and add a "thumbnail url" property to our ChatAccount entity.

Adding a thumbnailUrl property to the ChatAccount entity:

package com.codename1.cn1chat;
import com.codename1.rad.models.Entity;
import com.codename1.rad.models.EntityType;
import static com.codename1.rad.models.EntityType.tags;
import com.codename1.rad.models.StringProperty;
import com.codename1.rad.schemas.Thing;

/**
 * View model for an account profile.
 * @author shannah
 */
public class ChatAccount extends Entity {

    // The name property
    public static StringProperty name, thumbnailUrl;

    private static final EntityType TYPE = new EntityType() {{
        name = string(tags(Thing.name));
        thumbnailUrl = string(tags(Thing.thumbnailUrl));
    }};
    {
        setEntityType(TYPE);
    }

    public ChatAccount(String nm, String thumb) {
        set(name, nm);
        set(thumbnailUrl, thumb);
    }

}

And modify our ChatFormController to set the thumbnail URL on our entity.

room.addParticipants(
    new ChatAccount("George", georgeThumb),
    new ChatAccount("Kramer", kramerThumb)
);

And reload…​

Title component after setting thumbnail URLs for our participants
Figure 5. Title component after setting thumbnail URLs for our participants

Next Week

In part four we’ll discuss adding more actions.

]]>
1,016 RAD Chat Room - Part 3 0 0 0
RAD Chat Room - Part 2 blog/rad-chatroom-part-2.html Fri, 8 May 2020 00:00:00 +0300 steve https://www.codenameone.com/blog/rad-chatroom-part-2.html

This is part 2 of the RAD Chatroom tutorial. You can find part 1 here.

Adding a "Send" Button

A "Send" button is a pretty important part of any chat application. We’ll add a send button to our app by defining an action in our controller, and passing it to the ChatRoomView as follows. First we’ll define the action in our ChatFormController class:

// We're going to use a lot of static functions from the UI class for creating
// UI elements like actions declaratively, so we'll do a static import here.
import static com.codename1.rad.ui.UI.*;

// ...
public class ChatFormController extends FormController {

    // Define the "SEND" action for the chat room
    public static final ActionNode send = action(icon(FontImage.MATERIAL_SEND));

Then we’ll create a ViewNode to pass to the ChatRoomView constructor. This is can contain properties that the chat room uses to render itself, including which actions it should "embed" and where.

ViewNode viewNode = new ViewNode(
    actions(ChatRoomView.SEND_ACTION, send)
);

ChatRoomView view = new ChatRoomView(createViewModel(), viewNode, f);

If this is the first time you’ve seen a ViewNode definition, this may look a little bit foreign. All this does is register our "send" action with the "ChatRoomView.SEND_ACTION" category so that the chat room view knows to use it as the "send" action in the chat room. The full source of the ChatRoomController class after these changes is as follows:

package com.codename1.cn1chat;

import com.codename1.rad.controllers.Controller;
import com.codename1.rad.controllers.FormController;
import com.codename1.rad.models.Entity;
import com.codename1.rad.nodes.ActionNode;
import com.codename1.rad.nodes.ViewNode;
import com.codename1.rad.ui.chatroom.ChatBubbleView;
import com.codename1.rad.ui.chatroom.ChatRoomView;
import static com.codename1.ui.CN.CENTER;
import com.codename1.ui.FontImage;
import com.codename1.ui.Form;
import com.codename1.ui.layouts.BorderLayout;

// We're going to use a lot of static functions from the UI class for creating
// UI elements like actions declaratively, so we'll do a static import here.
import static com.codename1.rad.ui.UI.*;

public class ChatFormController extends FormController {

    // Define the "SEND" action for the chat room
    public static final ActionNode send = action(icon(FontImage.MATERIAL_SEND));

    public ChatFormController(Controller parent) {
        super(parent);
        Form f = new Form("My First Chat Room", new BorderLayout());

        // Create a "view node" as a UI descriptor for the chat room.
        // This allows us to customize and extend the chat room.
        ViewNode viewNode = new ViewNode(
            actions(ChatRoomView.SEND_ACTION, send)
        );

        // Add the viewNode as the 2nd parameter
        ChatRoomView view = new ChatRoomView(createViewModel(), viewNode, f);
        f.add(CENTER, view);
        setView(f);


    }

    /**
     * Creates a view model for the chat room.
     * @return
     */
    private Entity createViewModel() {
        ChatRoomView.ViewModel room = new ChatRoomView.ViewModel();

        ChatBubbleView.ViewModel message = new ChatBubbleView.ViewModel();
        message.messageText("Hello World");
        room.addMessages(message);
        return room;
    }
}

Now, let’s run the app in the simulator again.

rad chat room 6

Notice that a "send" button has been added to the bototm-right of the form, next to the text entry box.

rad chat room 7

This is progress, but you may be disappointed, upon playing with the send button, to discover that it doesn’t do anything. In fact, when you click the "send" button, the view is sending an event to our controller. We just haven’t implemented a handler for it.

Let’s do that now.

Handling the "Send" Action Event

To handle the "send" event, we simply add the following inside the constructor of our form controller:

addActionListener(send, evt->{
    evt.consume();
    ChatRoomView.ViewModel room = (ChatRoomView.ViewModel)evt.getEntity();
    String textFieldContents = room.getInputBuffer();
    if (textFieldContents != null && !textFieldContents.isEmpty()) {
        ChatBubbleView.ViewModel message = new ChatBubbleView.ViewModel();
        message.messageText(textFieldContents);
        message.date(new Date());
        message.isOwn(true); // Indicates that this is sent by "this" user
                            // so bubble is on right side of room view.

        // Now add the message
        room.addMessages(message);

        // Clear the text field contents
        room.inputBuffer("");
    }

});

This listener will be called whenever the "send" action is fired. On mobile devices this will only occur when the user presses the "Send" button. But on desktop, it will also be fired when the user hits "Enter" while the text field is focused.

The event passed to this handler is an instance of ActionEventNode which includes all of the contextual information necessary to identify the source of the action, including the entity (the room), the UI component (the ChatRoomView) object, and the action (send), that triggered the event.

The logic in this handler should be pretty straight forward. It checks if the "input buffer" contains any text. Since the input buffer is bound to the text field, this is just checks if the text field contains any text. It then creates a new message with the input buffer contents, and clears the contents of the input buffer.

All of these property changes will fire PropertyChangeEvents to the view so that the view state will be updated automatically and instantly.

If you run the app in the simulator again, you should be able to enter text into the text field, and press send, to see a new chat bubble animated into place.

rad chat room 8

Bonus Points: Disable Send Button When Input Empty

In out action handler, we include logic to prevent sending empty messages. But it would be nice if we game the user a cue in the user interface that "send" doesn’t work when the field is empty. We can do this using the enabledCondition attribute in our action definition:

public static final ActionNode send = action(
    enabledCondition(entity-> {
        return !entity.isEmpty(ChatRoom.inputBuffer);
    }),
    icon(FontImage.MATERIAL_SEND)
);

This says that the send action should only be enabled when the "entity" is non-empty. The "entity" in this case is the view model for the chat room.

Start the app again in the simulator and notice that the "send" button toggles between enabled and disabled depending on whether there is text in the input field.

Send button is disabled because the input field is empty
Figure 1. Send button is disabled because the input field is empty
Send button is enabled because the input field has text
Figure 2. Send button is enabled because the input field has text

Next Week

Next week we’ll proceed with adding text messages from other users.

]]>
1,017 RAD Chat Room - Part 2 0 0 0
RAD Chat Room - Part 1 blog/rad-chatroom-part-1.html Fri, 1 May 2020 00:00:00 +0300 steve https://www.codenameone.com/blog/rad-chatroom-part-1.html

This tutorial describes how to use the RADChatRoom library to quickly and easily add a nice-looking, fully functional chat room to your Codename One application. This is part 1 of a multi-part series of posts over the next few weeks.

The finished product will look like the following:

rad chat room 1

You can download the full source of this tutorial’s project here.

You can also try it out yourself here.

The demo link uses the CodeName One Javascript port, which allows you to deploy Codename One java applications as native Javascript apps inside the browser without any plugins or extensions. When you build a Codename One project for iOS, Android, Desktop, or any of the other build targets, they will be compiled as native apps, with native performance, and will not use Javascript.

You may also try out an Android build of this project. For other platforms, you can download the sources and build it yourself.

About the ChatRoomView Component

The ChatRoomView component is the first in a new breed of Codename One UI components which go beyond the the fundamental building blocks of user interfaces to provide a rich, fully-functional user experience out of the box. It is built on top of the new CodeRAD library which enables a new level of code-reuse based on the tried-and-true MVC (Model-View-Controller) design pattern. As you’ll see, adding a chat feature to your app is not difficult. All of the minutiae of UI details come working and ready to go. You just need to bind it to your view model and controller.

Prerequisites

In order to create the project in this tutorial, you only need one thing:

  • IntelliJ, NetBeans, or Eclipse with the Codename One plugin installed.

Project Setup

For this tutorial, we’ll create a basic Codename One project, and we’ll add the "CodeRAD" and "RADChatRoom" cn1libs as dependencies. I’ll use NetBeans in this tutorial, but you can use your preferred IDE (IntelliJ or Eclipse).

For the sake of this tutorial, I’m going to name the project CN1Chat, and my package will be "com.codename1.cn1chat". I’ll be using the "Hello World" bare-bones project template.

New project dialog in NetBeans
Figure 1. New project dialog in NetBeans
Page 2 of new project wizard in NetBeans
Figure 2. Page 2 of new project wizard in NetBeans

Step 1: Create a New Codename One project

If you haven’t created a Codename One project before, you can refer to this tutorial, which walks you through the creation of your first Codename One project.

Step 2: Activate CSS

The CodeRAD and RADChatRoom libs require CSS to be activated in your project. See this tutorial for the steps on enabling CSS.

Step 3: Add Dependencies

In Codename One settings, add the following cn1libs:

  1. CodeRAD

  2. RADChatRoom

If you haven’t activated any cn1libs before in your Codename One projects, see this tutorial which explains the process.

Step 4: Create Application Controller

We’ll be using MVC for this app. The CodeRAD cn1lib includes a set of controller classes that help with the structure of such apps. We’ll begin by modifying our app’s main application class (CN1Chat.java) so that it extends ApplicationController, and we’ll replace the class contents with the following:

package com.codename1.cn1chat;

import com.codename1.rad.controllers.ApplicationController;
import com.codename1.rad.controllers.ControllerEvent;

public class CN1Chat extends ApplicationController {
     @Override
    public void actionPerformed(ControllerEvent evt) {
        if (evt instanceof StartEvent) {
            evt.consume();

            // The app has started
        }
    }
}

Step 5: Create A Form Controller

Next we’ll create a controller for the form that will contain the chat. This will create a basic view model, and use it to create a ChatRoomView object, which we will add to the form. The code for the first iteration of this controller is as follows:

package com.codename1.cn1chat;

// imports

public class ChatFormController extends FormController {
    public ChatFormController(Controller parent) {
        super(parent);
        Form f = new Form("My First Chat Room", new BorderLayout());
        ChatRoomView view = new ChatRoomView(createViewModel(), f);
        f.add(CENTER, view);
        setView(f);
    }

    /**
     * Creates a view model for the chat room.
     * @return
     */
    private Entity createViewModel() {
        ChatRoomView.ViewModel room = new ChatRoomView.ViewModel();
        ChatBubbleView.ViewModel message = new ChatBubbleView.ViewModel();
        message.messageText("Hello World");
        room.addMessages(message);
        return room;
    }
}

A couple of things to note with this code:

  1. The createViewModel() method creates a minimal view model for our chat room. It uses the ChatRoomView.ViewModel class for the view model. This class is only a reference implementation of a view model, and the ChatRoomView class doesn’t require you to use this class at all if you don’t want to. Later on, in this tutorial, I’ll show you how to use your own custom class for the view model.

  2. Similarly, the ChatBubbleView.ViewModel is a reference implementation of a view model to encapsulate a message in the chat room, but you can use your own custom classes for these models also.

Step 6: Show the Form

Finally, we need to show the Chat form when the app launches. Modify your Application controller class to create a new instance of ChatFormController() and show its form as follows:

public class CN1Chat extends ApplicationController {
     @Override
    public void actionPerformed(ControllerEvent evt) {
        if (evt instanceof StartEvent) {
            evt.consume();
            new ChatFormController(this).getView().show();
        }
    }
}

Step 7: Run the App

Now that we have the minimal foundation in place, let’s run the app in the simulator. If everything goes well, you should see something like the following.

First run of Chat app in simulator
Figure 3. First run of Chat app in simulator

This looks good, but it’s not a fully functional chat app yet. You’ll notice that it is missing many of the features that are present in the screenshot I shared of the finished project. The finished project included a title component with avatars of the chat participants:

Chat participants listed in title bar in finished version of the app
Figure 4. Chat participants listed in title bar in finished version of the app

This is absent because we didn’t add any participants to the chat model.

In addition, there is no "Send" button, in this version, so there is no apparent way to send messages in this chat.

We’ll correct both of these omissions, and add some other features over the course of this tutorial.

Next Week

Next week we’ll continue by adding a send button and building from there.

]]>
1,018 RAD Chat Room - Part 1 0 0 0
Thread Errors blog/thread-errors.html Sat, 11 Apr 2020 00:00:00 +0300 shai https://www.codenameone.com/blog/thread-errors.html

I wrote before about EasyThread which makes it much easier to write multi-threaded code in Codename One. One problem in that scenario was the inability to define a generic exception handler for that scenario.

With the current version of Codename One we now have a new generic error handling API for easy threads:

public void addErrorListener(ErrorListener err);
public static void addGlobalErrorListener(ErrorListener err);

These methods add a callback for error events, either globally or for a specific thread. Notice that these methods aren’t thread safe and should be invoked synchronously. So make sure to invoke them only from one thread e.g. the EDT.

These methods must never be invoked from within the resulting callback code!

So you can’t do this:

t.addErrorListener((t, c, e) -> {
   // do stuff ...

   // this is illegal:
   t.removeErrorListener(listener);
});

The error listener interface looks like this:

public static interface ErrorListener<T> {
    /**
     * Invoked when an exception is thrown on an easy thread. Notice
     * this callback occurs within the thread and not on the EDT. This
     * method blocks the current easy thread until it completes.
     * @param t the thread
     * @param callback the callback that triggered the exception
     * @param error the exception that occurred
     */
    void onError(EasyThread t, T callback, Throwable error);
}

This should provide you with all the details you need to handle an error in a generic way.

]]>
1,019 Thread Errors 0 0 0
Xcode 11 is now the Default blog/xcode-11-migration-now-default.html Fri, 20 Mar 2020 00:00:00 +0200 shai https://www.codenameone.com/blog/xcode-11-migration-now-default.html

We hope you’re all keeping safe!
We announced a couple of weeks ago that we’re moving our build servers to use xcode 11.3 by default. As a recap, Apple requires a new version of xcode/iOS SDK for apps submitted to the appstore. As a result we had to update the version of xcode on our build servers.

This has been in the cloud servers for a while and is now the default when sending new builds. For most of you this should be seamless…​

Everything should "just work". But for some edge cases things might fail or behave differently. This is especially true if you rely on native libraries but also if you rely on some functionality that we missed in our testing.

If you run into such a problem first verify it by testing against xcode 10.1 using the build hint: ios.xcode_version=10.1.

We suggest removing this build hint once your done so you can use the default target recommended by us
]]>
1,020 Xcode 11 is now the Default 0 0 0
Xcode 11 Migration blog/xcode-11-migration.html Fri, 6 Mar 2020 00:00:00 +0200 shai https://www.codenameone.com/blog/xcode-11-migration.html

Apple keeps moving the goal posts of xcode requirements for developers. This is good as it keeps the technology fresh but it means support for older devices becomes untenable. Unfortunately there isn’t much we can do and we need to move with the times as Apple will no longer accept apps built with older versions of xcode.

The main problem with this is another pain point for iOS developers. Newer versions of code require newer versions of Mac OS. That means we need to update the version of Mac OS on all of our servers. That’s a HUGE pain not just because of the drudge of upgrading every server…​

It’s a pain because Catalina (the new Mac OS) isn’t compatible with xcode 9.2. As a result we killed support for xcode 9.2 and if you explicitly request it in your build hints your build will fail starting today. Next week we’ll upgrade the OS’s and install the new version of xcode. At that point you should be able to send a build with xcode set to 11.3 as an option. To do that you can use the build hint: ios.xcode_version=11.3 or ios.xcode_version=10.1. By April we hope to make 11.3 the default value.

We suggest removing this build hint once your done so you can use the default target recommended by us
]]>
1,021 Xcode 11 Migration 0 0 0
New Sheet Positioning blog/sheet-positions.html Fri, 28 Feb 2020 00:00:00 +0200 steve https://www.codenameone.com/blog/sheet-positions.html

Not so long ago, we released a Sheet component that acts like a non-modal dialog box that slides up from the bottom. It occupies only the amount of space required to house its contents, and it provides built-in navigation controls to go “back” to the previous sheet. By default Sheets are displayed along the bottom of the screen, but we have recently added an update that allows you to position it along the north, east, west, south, or center of the screen, as shown below:

Sheet component positions

In addition we have added the ability to use a different position for tablets and desktop, than for phones. On desktop, it is more common for dialogs to pop up in the center of the screen, whereas on mobile, it is quite common to have a dialog (or sheet) pop up from the bottom.

When positioning a sheet on the east or west, it is quite easy to create your own ad-hoc hamburger menu. This may be easier, in some cases, than using the ToolBar class, as it gives you more control over the result.

For a full working example, see the updated Sheet sample here.

Below is a 35 second screen cast of that demo:

]]>
1,022 New Sheet Positioning 0 0 0
CSS in CN1Libs blog/css-in-cn1libs.html Fri, 21 Feb 2020 00:00:00 +0200 steve https://www.codenameone.com/blog/css-in-cn1libs.html

We’ve just added support for including CSS inside of Codename One library projects so that CSS styles can now be distributed inside a cn1lib. This opens up a world of possibilities for creating module UI libraries and themes.

How it works

To begin, you just need to add a “css” directory inside your Codename One Library project, with a “theme.css” file in it.

Add your CSS styles into the theme.css file, and build the library project.

If you add this module to a Codename One application project, these styles will automatically be included.

If you try to install a cn1lib that includes CSS into a Codename One application project that doesn’t have CSS activated, it will fail. You must activate CSS in the application project first.

Bonus Tip: Auto-Installing Library into Apps when Building Library

This tip is for those of you who are building your own cn1libs. When I’m developing a CN1lib, I always have a separate application project that uses the lib. This is because you can’t test a cn1lib directly inside a library project. The cn1lib first has to be installed into application project before it can be used and tested.

This can create a lot of manual steps each time you make changes to your cn1lib and want to test them out. You need to build the library project, then copy the cn1lib from the library’s dist directory, into the application project’s lib directory. Then you need to select “Refresh Cn1libs” from the Codename One menu in the IDE.

As far as I’m concerned, anything more than a single button press is too much for being able to test my changes. Luckily its really easy to eliminate the extra steps by adding a small snippet into your library project’s build.xml file.

At the end of the “jar” target, add the following:

<copy file="dist/${application.title}.cn1lib" todir="path/to/AppProject/lib"/>
<ant dir="path/to/AppProject" target="refresh-libs-impl" usenativebasedir="true"/>

Now, whenever you build the library project, it will automatically copy it into your application project, and refresh its CN1libs, so that you can test your changes instantly.

]]>
1,023 CSS in CN1Libs 0 0 0
Migrating Legacy Applications to CSS blog/migrating-legacy-applications-to-css.html Fri, 14 Feb 2020 00:00:00 +0200 steve https://www.codenameone.com/blog/migrating-legacy-applications-to-css.html

If you have an existing Codename One app that uses the designer for its theme, then you may have been reluctant to try to migrate it to CSS. Codename One projects are assumed to be using either CSS or the designer for their themes. But not both at the same time. When an app has CSS enabled, it compiles the css/theme.css to src/theme.res when the app is built, and it is kept in sync when changes are made to the theme.css file. Changes that you make manually to the theme.res file, would be lost the next time it synchronizes with the theme.css file. This doesn’t jive with legacy projects where you have customized the theme.res using the designer.

I don’t blame you if you don’t feel like converting your theme.res file into CSS. After years of customization, a theme.res file may contain hundreds of images and styles. In addition, if your app is using the old GUI Builder, your theme.res file may include actual form designs, which can’t be migrated to CSS.

Fortunately, there is a way to add CSS support for your application without having to lose all of your work. Codename One allows you to “layer” themes over top of each other, thus allowing you to use multiple themes. It does this, for example, if you are using the native theme in your app. It uses the native theme for the platform as a base, but overrides it with the styles in your app’s theme.

Steps:

  1. Before you begin, make sure that your theme.res is not currently opened in the designer.

  2. Rename your "theme.res" file to "theme_legacy.res" (This is found in the src directory)

    Change theme.res to theme_legacy.res
  3. Delete the res/theme directory, and the res/theme.xml file from your project.

    Delete the res/theme directory and res/theme.xml file
  4. Open the theme_legacy.res file in the designer.

  5. Under theme constants, add the constant “OverlayThemes”, with a value of “theme”.

    Set OverlayThemes constant

    Then Save

  6. Open your app’s main file, and find where the "theme.res" file is loaded. Look for a line like:

    theme = UIManager.initFirstTheme("/theme");

    And change it to

    theme = UIManager.initFirstTheme("/theme_legacy");

    If your app is an old GUI Builder app, then it might be new StateMachine(“/theme”) instead.

    Change the theme reference in you app main class

    Save

  7. Open Codename One Preferences.

    Click on CSS support
  8. Click on “CSS Support”

    Activate CSS now
  9. Click “Activate CSS Now”

    The screen should then change to a menu as shown here:

    CSS menu after activating CSS
  10. Press “Open CSS File For Editing” Open the CSS file for editing, and make a change that you’ll definitely notice, for testing. Here I’ll just change the color of labels to “Green”.

    Editing CSS File
  11. Save the CSS file, and run your project.

    You should see the CSS take effect. In my example, my label is now green.

    App running in simulator with CSS applied

What did all this do?

This tells the app to load your theme_legacy.res file instead of theme.res. Because of the “OverlayThemes” constant in your theme_legacy.res, Your app will automatically load the theme.res file’s styles over top of your legacy theme. The theme.res file will then be generated from your css/theme.css file, and kept in sync.

]]>
1,024 Migrating Legacy Applications to CSS 0 0 0
Safe Areas blog/safe-areas.html Sat, 8 Feb 2020 00:00:00 +0200 steve https://www.codenameone.com/blog/safe-areas.html

Apple is so very clever with its designs. With the iPhone X, they found way to squeeze in a larger screen without increasing the phone dimensions. The screen nearly covers the entire front face of the phone. The "notch", as it has come to be known, may have been a practical concession (they needed to put the camera and speaker somewhere, after all) or an intentional design choice - or maybe a little of both. However the notch was conceived, it is here to stay, and we developers need to "work around" it.

And it’s not only the notch that we have to work around. The iPhone X screen has round corners too. Not the old, boring, right angled corners that we’re used to. And the coup de gras is the task bar that now appears along the bottom of the screen, that allows the user to "swipe up" to minimize the app. This has made it more difficult to use bottom navigation in our apps because we have to make sure that the user doesn’t accidentally close the app when they try to click on one of our app’s buttons at the bottom of the screen.

We have recently added an API to help you work around these landmines. We’ve added the method Form.getSafeArea(), that will load the "safe" bounds of the current form. If you are drawing components on the screen and want to make sure that they aren’t clipped by "the notch", the corners, or the task bar, then you can use this API to create a safe play-pen for yourself.

We’ve also added Container.setSafeArea(boolean) which you can use to ensure that the container renders its children inside the "Safe" area. The Tabs component has been updated to that its tab buttons are rendered inside a "safe area". When run on the iPhone X, you’ll notice a little bit of extra padding under your tab buttons that appear at the bottom of the screen.

Some Examples

// Tab 1:  A safe area.  Will render all children inside safe area.
Container tab1Contents = new Container();
tab1Contents.setSafeArea(true);
// ... omitted code - building the tab 1 contents.

// Tab 2: A non-safe area.  Children may clip "unsafe" areas.
Container tab2Contents = new Container();
// ... omitted code - building tab 2 contents.

tabs.addTab("Safe Tab", tab1Contents);
tabs.addTab("Unsafe Tab", tab2Contents);

form.add(BorderLayout.CENTER, tabs);
Tab 1 is a safe area
Figure 1. Tab 1 is a safe area, so its children will not get clipped by the notch
Tab 2. Unsafe area
Figure 2. Tab 2 is not a safe area, so some of its children may be clipped by the notch

Further Reading

Check out the SafeAreasSample in the Samples project for the full working example from which the sample code above was taken.

]]>
1,025 Safe Areas 0 0 0
Sign-in with Apple Support blog/sign-in-with-apple-support.html Fri, 31 Jan 2020 00:00:00 +0200 steve https://www.codenameone.com/blog/sign-in-with-apple-support.html

We have just finished the initial release of our "Sign-in with Apple" cn1lib, which adds "Sign-in with Apple" support to Codename One apps. On iOS 13 and higher, this will use Apple’s native Authentication framework. On other platforms (e.g. Android, Desktop, and Simulator), this will use Apple’s Oauth2 authentication service.

The main motivation for adding this functionality is that Apple would require apps that use "sign in with…​" to support its service too. If you won’t support sign in with Apple but include support signin with Facebook/Google your app might be rejected in the future.

If your app doesn’t require sign-in or uses custom login logic there’s no requirement to support "sign in with Apple"

Getting Started

The hardest part of adding Apple sign-in support house-keeping you need to perform in Apple’s developer portal. If you only intend to support Apple sign-in with your iOS app, and not on other platforms, then the process is pretty simple - you just check a box next to "Sign-in with Apple" in the capabilites section of your App ID details page. Set-up for other platforms is a bit more involved. You need to create a "Services ID" (used for the Oauth2 client ID), and generate a private key so you will be able to generate the Oauth2 client secret on-demand.

For full instructions see the setup documentation in the cn1lib’s wiki.

You can find the plugin in Codename One Settings. Once you’ve added the cn1lib to your Codename One project, you can begin using the AppleLogin class to provide Apple sign-in support.

The Code

The following is an example of how to add Apple login support to your app.

AppleLogin login = new AppleLogin();
// If using on non-iOS platforms, set Oauth2 settings here:
// login.setClientId(...);
// login.setKeyId(...);
// login.setTeamId(...);
// login.setRedirectURI(...);
// login.setPrivateKey(...);

if (login.isUserLoggedIn()) {
    new MainForm().show();
} else {
    new LoginForm().show();
}


....


class LoginForm extends Form {
    LoginForm() {
        super(BoxLayout.y());
        $(getContentPane()).setPaddingMillimeters(3f, 0, 0, 0);
        add(FlowLayout.encloseCenter(new Label(AppleLogin.createAppleLogo(0x0, 15f))));


        Button loginBtn = new Button("Sign in with Apple");
        AppleLogin.decorateLoginButton(loginBtn, 0x0, 0xffffff);

        loginBtn.addActionListener(evt->{
            login.doLogin(new LoginCallback() {
                @Override
                public void loginFailed(String errorMessage) {
                    System.out.println("Login failed");
                    ToastBar.showErrorMessage(errorMessage);
                }

                @Override
                public void loginSuccessful() {
                    new MainForm().show();
                }
            });
        });

        add(FlowLayout.encloseCenter(loginBtn));


    }
}


....

class MainForm extends Form {
    MainForm() {
        super(BoxLayout.y());
        add(new SpanLabel("You are now logged in as "+login.getEmail()));
        Button logout = new Button("Logout from Apple");
        logout.addActionListener(e->{
            login.doLogout();
            new LoginForm().show();
        });
        add(logout);
    }
}

For full working demo, see the Demo app

Some screenshots from the demo:

Sign-in with apple demo

More information

  1. See the Github project for the Apple Sign-in cn1lib.

  2. See the Demo project for an example.

  3. See the set-up instructions detailed instructions on adding support for Apple Sign-in.

  4. See Apple’s documentation for Sign-in with Apple.

]]>
1,026 Sign-in with Apple Support 0 0 0
Picking a Dialog Type blog/picking-dialog-type.html Fri, 10 Jan 2020 00:00:00 +0200 shai https://www.codenameone.com/blog/picking-dialog-type.html

The duality of InteractionDialog and Dialog is often confusing to the Codename One newcomer (and to some degree to veteran developers too). This is in part due to the multiple behavior differences that extend far beyond the "official" functionality difference. This has its roots in history that predated Codename One.
In this post I’ll try to clarify the process of picking the "right one" and the tradeoffs involved.

A dialog can be modal or non-modal but it isn’t interactive like an InteractionDialog. The modal dialog blocks the EDT internally using InvokeAndBlock so the current thread stops until there’s a response from the dialog. This is convenient but has some edge case issues. E.g. the event that launched the dialog might trigger other events that would happen after the dialog was dismissed and cause odd behavior.

But that’s not the big thing in modality. Modality effectively means the form behind you "doesn’t exist". Everything that matters is the content of the dialog and until that is finished we don’t care about the form behind. This core idea meant that a Dialog effectively derives Form and as such it behaves exactly like showing another Form. In other words a Dialog IS A FORM. This effectively disables the current Form. What you see behind the dialog is a drawing of the previous Form, not the actual Form.

Text fields can pose a problem in this case. Because the way the dialog is positioned (effectively padded into place within its form using margin) the UI can’t be scrolled as text field requires when the virtual keyboard rises. Since people use dialogs in such scenarios we try to workaround most of these problems but sometimes it’s very hard e.g. if the dialog has a lot of top margin, the virtual keyboard is open and covering it. Or if the user rotates the screen at which point the margin positioning the dialog becomes invalid.

In InteractionDialog Some of these issues such as the margin to position also apply so it’s also a bit problematic for text input

InteractionDialog is a completely different beast that sprung out of a completely different use case. What if we want a Dialog such as a "color palette" that floats on top of the ui?

We can move it from one place to another but still interact with the underlying form. That’s the core use case for InteractionDialog. As such modality is no longer something we need so it was never baked into InteractionDialog although it technically could have been (but it doesn’t make sense to the core use case).

It’s implemented as a Container placed into the layered pane of the current Form so the Form around it is "real". Because the Form is "live", layout works better. The removal of modality makes some edge cases related to editing slightly better. There are still some inherent problems with Dialog positioning and rotation though. It also allows you to click outside of the Dialog while input is ongoing which might be a desirable/undesirable effect for your use case.

Overall I try to use dialogs only for very simple cases and avoid input in any Dialog when possible. If I use input I never use more than one field (e.g. no-username and password fields) so I won’t need to scroll. These things work badly for native UIs as well e.g. with the virtual keyboard obscuring the submit button etc. Since those behaviors are very hard to get right for all resolution/virtual keyboard scenarios.

]]>
1,027 Picking a Dialog Type 0 0 0
Live Streaming with Codename One and Wowza blog/live-streaming-wowza.html Tue, 7 Jan 2020 00:00:00 +0200 https://www.codenameone.com/blog/live-streaming-wowza.html

Two months ago I published the CN1Lib "Wowza Live Streaming Events", as usual you can install that by the Extension Manager. The purpose of this CN1Lib is to add live streaming capabilities to iOS and Android Codename One apps, hiding all the complexities and reducing the effort.

The Wowza cn1lib has been deprecated since the publication of this post

However, live events are not trivial. That’s why you should read this README carefully: https://github.com/jsfan3/CN1Libs-WowzaLiveStreaming/blob/master/README.md

This CN1Lib lets you to broadcast a live video streaming event from a mobile app. The streaming is identified by a unique id which is automatically assigned. You can then play the live video stream with a given id and an adaptive bitrate;

You can also record live streams to watch later.

All streaming operations (storage, processing, adapting to multi-bitrate, broadcasting to multiple devices, etc.) are automatically backed by the Wowza Cloud Service. This includes accurate logging and monitoring.

More specifically, this CN1Lib integrates and makes use of GoCoder SDK for iOS, GoCoder SDK for Android, and it performs RESTful requests to the Wowza Streaming Cloud service (live event plan).

Please consider this CN1Lib in alpha stage, not ready yet for production environments. I’d appreciate contributions/pull requests to improve this CN1Lib.

]]>
1,028 Live Streaming with Codename One and Wowza 0 0 0
New Low-level Microphone API blog/new-low-level-microphone-api.html Fri, 3 Jan 2020 00:00:00 +0200 steve https://www.codenameone.com/blog/new-low-level-microphone-api.html

Today’s blog post will delve further into our new media features. We’ve recently added an API to access raw PCM data from the device’s microphone. Previously, the media recording API could only be configured to save audio to a file. This is fine for most use cases, but sometimes it is necessary to access the the raw PCM stream directly. For example for voice recognition, or audio processing, or audio visualization.

How it works

In order to access an audio PCM stream, you need to create an AudioBuffer object, which will be used as a destination for microphone input.

AudioBuffer buffer = MediaManager.getAudioBuffer("mybuffer.pcm", true, 4096);

A couple of points here:

  1. The "mybuffer.pcm" is the virtual path to the buffer. Think of it like a file path that doesn’t correspond to an actual file. This can be any arbitrary string. We will be referencing it later when we construct the media recorder, to redirect its output to this audio buffer.

  2. The 2nd parameter (true) says to "create" the audio buffer object if it doesn’t already exist in the central registry.

  3. The 3rd parameter, is the buffer size. You can put anything here, and the API will adapt. I’m using 4096 here, but that was chosen rather arbitrarily.

Next, you add a callback which will be executed whenever the buffer’s contents are changed. This happens when a new chunk of PCM data is available from the microphone.

final float[] sampleData = new float[buffer.getMaxSize()];
buffer.addCallback(buf->{
	buf.copyTo(sampleData);
    int sampleRate = buf.getSampleRate();
    int numChannels = buf.getNumChannels();
    int len = buf.getSize();

   sendDataToServerForProcessing(sampleData, 0, len, sampleRate, numChannels);

});

Some key points here:

  1. The callback does NOT run on the EDT. It runs on its own thread.

  2. The buf.copyTo() method will copy all new data from the buffer into our own float[] array. It will only write values in the range [0, buf.getSize()). Each entry will be a float between -1 and 1.

  3. buf.getSize() may return a different value in each invocation, as the “size” of the buffer reflects the current data in the buffer. Not to be confused with the maxSize of the buffer, which is the original size of the buffer, as it was created in the getAudioBuffer() method.

  4. If you are processing the data in any way, you’ll need to know both the sampleRate, and the number of channels of the input. It is important to collect this data from the audioBuffer inside this callback, and not depend on the settings you provided originally to createMediaRecorder().

We’ll use MediaRecorderBuilder to construct our media recorder now as follows:

MediaRecorderBuilder mrb = new MediaRecorderBuilder()
    .path("mybuffer.pcm")
    .redirectToAudioBuffer(true);

Media microphone = MediaManager.createMediaRecorder(mrb);

Notice that, for the path() parameter of the builder, we use the same value we used in getAudioBuffer(). This is extremely important, otherwise the media recorder won’t run the callback in your AudioBuffer instance.

We can start recording now using microphone.play(), and pause using microphone.pause(). Or use the new async APIs, playAsync() and pauseAsync() to gain more clarity about the recording state.

Saving PCM Stream to a WAV File

In order to test the AudioBuffer class, we needed to be able to play the PCM stream that we capture to make sure that it is working correctly, and that it hasn’t been corrupted in any way. We added a class, WAVWriter, for writing a PCM stream to a WAV file to facilitate this testing. A WAV file, after all, just contains a raw stream of PCM data, with some headers to declare the data format, so this class is pretty minimal.

The following example records directly from the PCM stream to a WAV file in file system storage.

WAVWriter wavWriter;
AudioBuffer audioBuffer;
private void record() throws IOException {
     audioBuffer = MediaManager.getAudioBuffer(bufferPath, true, 4096);
     final float[] floatSamples = new float[audioBuffer.getMaxSize()];
     audioBuffer.addCallback(buf->{
        synchronized(clipLock) {
            if (wavWriter == null) {
                try {
                    wavWriter = new WAVWriter(
                        new File(fileName),
                        buf.getSampleRate(),
                        buf.getNumChannels(),
                        16
					);
                } catch (IOException ex) {
                    Log.e(ex);
                    return;
                }
            }

            buf.copyTo(floatSamples);
            try {
                wavWriter.write(floatSamples, 0, buf.getSize());
            } catch (IOException ex) {
                Log.e(ex);
            }
        }
    });
    }
    MediaRecorderBuilder builder = new MediaRecorderBuilder()
        .audioChannels(1)
        .path(bufferPath)
        .redirectToAudioBuffer(true);

    MediaManager.createMediaRecorder(builder));


    synchronized(clipLock) {
        wavWriter = null;
    }
}

// … And when you’re finished recording, just close the WAVWriter
// for the file to be written.
wavWriter.close();

The key parts of this example are:

  1. We don’t necessarily need to instantiate the WAVWriter object inside the AudioBuffer callback, but we do need some information that the callback provides: the sample rate, and number of channels. This information is supplied in the audiobuffer callback, and won’t change, so you could also just fetch this information in the first callback, and store it for when and where you do instantiate the WAVWriter object.

  2. The WAVWriter.write(float[] samples, int offset, int len) method is where you can pass the PCM samples directly to WAV file.

  3. Remember to call close() on the WAVWriter to ensure that the file is written.

You can find some examples using the AudioBuffer and WAVWriter classes to write PCM streams to a WAV File in the Samples project. Specifically, the AsyncMediaSample and the AudioBufferSample.

Sample Rates and Downsampling

A PCM data stream is a digital approximation of a sound wave form. The sample rate, usually expressed in Hz (hertz) is the number of samples we extract per second. A sample rate of 16000 Hz indicates that we are extracting 16000 floating point values (per channel) per second. The higher the sample rate, the better wave approximation will be, and therefore the better quality the sound will be. But higher sample rates also correspond to larger file sizes.

When you construct a media recorder, you can request a specific sample rate, but there is no guarantee that the underlying platform will comply with your request. Some platforms only support the native sample rate of the audio hardware, so you’re at the mercy of the audio chip to a certain extent. You can find out the actual sample rate by calling audioBuffer.getSampleRate(), any time after the first callback is executed - as this is where the platform informs the audio buffer about the underlying sample rate.

Some common sample rates you’ll see are 16000, 22050, 44100, and 48000. If you are passing the PCM data stream to service that only accepts a certain sample rate, then you may need to downsample the data. The AudioBuffer class includes a downsample() method with a rudimentary algorithm that may be sufficient for some cases. It is lacking some of the features of high-end down-sampling algorithms, such as low pass filtering, and it does noticeably lower the audio quality, but if your application doesn’t need “perfect sound”, then it might be appropriate for your needs. If you do need perfect sound, you should either perform the downsampling server-side, or use a 3rd-party sound library.

The downSample() method works as follows:

audioBuffer.downSample(16000);  // downsample to 16000Hz)

You should call this method inside your callback, before copying the data to your float samples buffer. This is because it will modify the data in the audio buffer, and update both the “size” property, and the “sampleRate” property of the buffer, to accurately reflect the new sample rate.

The AudioBufferSample includes an example usage of this method.

]]>
1,029 New Low-level Microphone API 0 0 0
Preliminary course for mobile cross-platform development with Java and Codename One blog/preliminary-course-mobile-cross-platform-development.html Thu, 2 Jan 2020 00:00:00 +0200 https://www.codenameone.com/blog/preliminary-course-mobile-cross-platform-development.html

I’m Francesco Galgani, a developer and a Codename One enthusiast.

Few days ago I published the first version of an Italian free preliminary course for mobile cross-platform development with Java + Codename One. I’ll probably write other articles, however the first module is complete. This course is intended for people interested in app development, but have no prior programming experience. That’s why my first article has no code, it’s preparatory to understand the magic and challenges of development.

In this course I tried to share my experience. The reader is gradually introduced to the first lines of Java code, that solve a simple math problem. After that, the same problem is used to see how to write the first app with Codename One. Finally, there is a lot of “homework” to learn Codename One.

Currently, there are seven articles.

The first is about Codename One, it’s a short introduction: I describe why I consider it the best tool for cross-platform development. I invite to spread the word about it.

In the second I discuss why it’s so difficult to find a good learning path in the chaotic and unstable world of software development. I explain why the real challenge is to find good indications that will last over time: about that, I report concrete examples of wrong paths. I indicate JAVA + Codename One + Spring Boot as a good direction.

The third is an introduction to the “algorithm” concept. I discuss what we need to know about the hardware and what are the programmable machines that we use every day (smart-phone, smart-tv, smart-car, etc.). I included an introduction to programming languages, written by me, as downloadable pdf slides. After that, I describe why software debugging is the longest, most difficult and most expensive part of software development. I conclude with the concepts of “device-agnostic”, “IDE-agnostic” and “language-agnostic”.

In the fourth I introduce a simple math problem (the linear motion of a car) that I use to describe the first lines of Java code and to show a good way to decompose a problem in algorithms. I suggest to use a Java online compiler to test the code examples. Then I describe why algorithms depend on the tools we have and on the logic of the chosen programming language. I suggest what we can try to do when the chosen programming language doesn’t have the functionality what we need, with examples.

In the fifth I deeply describe what we need about software, hardware and cloud services to do cross-platform development. After that, I suggest which chapters of a specific Java 8 learning book should be studied. I also include a Java introduction written by me, as downloadable pdf. Then I list several links about Codename One learning tools, documentation, technical support.

The sixth revisits the same math problem of the car in an Objected-Oriented Programming perspective. I guide the reader to write the first Codename One app to solve that problem. This article ends with a list of coding exercises to learn Codename One.

In the seventh I summarize sixteen points what I think are the best software engineering practices for managing complexity in a software project.

I hope you or your friends will find this guide helpful and useful.

]]>
1,030 Preliminary course for mobile cross-platform development with Java and Codename One 0 0 0
Javascript Media Restrictions to be Aware Of blog/javascript-media-restrictions.html Thu, 19 Dec 2019 00:00:00 +0200 steve https://www.codenameone.com/blog/javascript-media-restrictions.html

Codename One provides play() and pause() methods as part of the Media interface, which you can use to start and stop media clips programmatically. If you are deploying your app to the browser using the Javascript port, then you should be aware of some restrictions imposed by modern web browsers on media playback, and how to work around them.

Modern browsers will only allow audio playback that is triggered by direct user interaction. This basically means that the user needs to press a button to trigger audio playback.

If your app fits into this model (playing sounds only in response to user button presses), then you don’t have to worry about this restriction. But if you want to play sounds in other contexts, you might bump into this restriction. For example, you might want to play a sound in a chat app, in response to someone else’s message being posted (i.e. in response to a server event). Or you might want to play a sound in response to voice input, or location changes, or when a timer goes off.

So what happens if you call mySoundClip.play(), and the browser disallows it on the grounds that it wasn’t in direct response to user interaction? Our media API catches this failure, and displays a popup-dialog saying “Media ready” with a “Play” button as shown here:

MediaPlayNow

When the user presses the “Play” button, then the media will start playing because this time it should "meet" the browser’s user-interaction requirements due to the user’s click on the 'play' button.

Make Sure to Call cleanup()

The “media ready” popup dialog shouldn’t present a major problem for your app’s user experience. However, if you’re playing several audio clips, one after another, it could become annoying for the user to have to keep pressing “Play” for each clip. Luckily, the browser will “remember” if the user has authorized audio playback on a particular media element, so if you play your cards right, you may only need to prompt the user to play the first sound clip, and the subsequent ones will just be allowed by the browser. In the Javascript port, each Media object is mapped to its own HTMLMediaElement (an <audio> or <video> DOM element). It uses a pool of media elements to try and reuse the underlying media elements if it can. A Media object’s underlying element is only returned to the pool when its cleanup() method is called. If you’re playing a series of audio clips in a row, you should make sure to call the cleanup() method on each sound clip, before the next one plays, to avoid being forced by the browser to display “Media Ready/Play” dialog.

For example, suppose you’re need to play a series of sound clips, one after another. You might do something like:

Media media;
List<String> clipsUrls = ...;

void play(int index) {
    If (media != null) {
        media.cleanup();
    }
    media = createMedia(clipUrls.get(index), false, () ->{
        callSerially(()->{
            play(index+1);
        });
    });
}

play(0);

In this example, I’m using a single property, media, which we will reuse for each sound clip, and I have a list of URLs for sound clips I want to play sequentially. The play(index) method plays the clip at the specified index - but first, it cleans up the previous sound clip. This is extra important in the Javascript port since this will return the sound clip’s <audio> element to the pool, so that it (and it’s granted permissions) can be reused for the next sound clip.

We use the completion handler in createMedia() to be notified when the sound clip has finished playing, at which point it calls play(index+1) - i.e. it plays the next clip. Notice that I place the play() call inside callSerially() so that it is deferred to the next dispatch. This may not be strictly necessary, but it avoids any problems that might occur trying to call cleanup() from inside the media’s completed callback.

Finally we start the chain off by calling play(0) to play the first sound clip. If play(0) is called in response to a user interaction (e.g. inside a button’s action listener), then the sound clip should play unimpeded. But if it is called outside of this context, then a dialog may be displayed to prompt the user to play the sound clip.

Check out the AsyncMediaSample in the Samples project for a complete working example that follows this workflow. That example just runs a continuous cycle of recording audio from the microphone, then playing back what you recorded. If you run this example on an iOS device (which seems to be the most strict about its media playback restrictions), you’ll be prompted to play the media at the beginning - but won’t be asked again for subsequent clips.

]]>
1,031 Javascript Media Restrictions to be Aware Of 0 0 0
Async Play and Pause Support blog/media-async-play-and-pause-support.html Fri, 13 Dec 2019 00:00:00 +0200 steve https://www.codenameone.com/blog/media-async-play-and-pause-support.html

We’ve recently released a few updates to our Media APIs in order to support a wider variety of use cases. One of these upgrades is the new AsyncMedia interface, which includes async play and pause support.

The Media interface has always had play() and pause() methods, but it didn’t provide an easy way to detect when they had taken effect. On most platforms, this hasn’t been a major issue because the delay between when play() is called, and when the media actually starts playing is small, or negligible. But our Javascript port presented some new challenges due to strict browser permissions surrounding media playback. In some cases, which I’ll discuss in detail in my next blog post, the user will be prompted to provide permission to play audio, or access the microphone, when play() is called. This means that the delay between play() and when the media actually starts playing may be significant.

New AsyncMedia interface

The new AsyncMedia interface extends Media and adds some new methods. Among these new methods, you’ll find playAsync() and pauseAsync() which return PlayRequest and PauseRequest objects respectively.

Since MediaManager returns media as Media objects, you’ll need to convert them into AsyncMedia objects in order to access the new functionality. You can use the new MediaManager.getAsyncMedia(Media) method for this purpose. Almost all Media objects in Codename One already implement AsyncMedia, so the getAsyncMedia() will usually just return the same object, but casted to AsyncMedia. In the rare case where the Media object, doesn’t already implement AsyncMedia, getAsyncMedia() will return a proxy wrapper around the original media object, that includes support for the new Async APIs.

Let’s look at an example:

import static com.codename1.media.MediaManager.*;
import com.codename1.media.*;

//...

boolean playPending, playing;
// ...

AsyncMedia media = getAsyncMedia(createMedia(mediaUrl, false));
playPending = true;
media.playAsync().ready(m->{
    playPending = false;
    Playing = true;
}).except(ex->{
    playPending = false;
    Dialog.show("Failed to play", "Failed to play media: "+ex.getMessage(), "OK", null);
});
media.addMediaStateChangeListener(evt->{
    Playing = (evt.getNewState() == State.Playing);
});

This code is a complete example of how you can keep track of the playing state of your media. It keeps track of both whether there is a play request pending, and whether it is currently playing. Technically you don’t need to keep your own variable for keeping track of the the “playing” state, as you can just call media.isPlaying(), or media.getState() at any time. I use a separate playing variable here just to illustrate how to synchronize your application state with the state of your media.

How it works

playAsync() returns a PlayRequest object, which is a subclass of AsyncResource<Media>. Its “ready” callback will be executed once playing has begun. The “except” callback will be called if playback fails due to an error.

You can also use the mediaStateChangeListeners of the AsyncMedia object to keep track of changes to state. Whenever the media starts playing, or stops playing, it will fire one of these events.

The new pauseAsync() method works similarly.

Check out the AsyncMediaSample sample in the Samples project for a full working example that makes use of playAsync().

]]>
1,032 Async Play and Pause Support 0 0 0
Using Component Placeholders While Loading Data blog/data-loading-placeholders.html Fri, 6 Dec 2019 00:00:00 +0200 steve https://www.codenameone.com/blog/data-loading-placeholders.html

In my last post I introduced the new CN.invokeWithoutBlocking() method as a means of ensuring that your UI construction code doesn’t run into any "blocking" that could negatively affect user experience. In cases where you need to perform a network request to help build your UI, I offered some recommendations for moving blocking code out of the critical paths. One recommended pattern was to insert placeholder content into your components, which you replace with the actual data once it has been received from the server. That pattern goes something like:

Form f = new Form("Hello", BoxLayout.y());
Label nameLabel = new Label();
nameLabel.setText("placeholder");
AsyncResource<MyData> request = fetchDataAsync();
request.ready(data -> {
    nameLabel.setText(data.getName());
    f.revalidateWithAnimationSafety();
});
f.add(nameLabel);
f.show();

The concept here is that, the fetchDataAsync() method is performing an asynchronous network request in the background but returns an AsyncResource object immediately which will be notified when the response if received. Therefore, the contents of the ready( data → {…​}) block are executed some time after the form has already been built and shown.

This solved a significant user experience problem, in that the form can be shown to the user immediately without waiting for the network request to complete. However, it introduced a new problem, which is that our nameLabel is displaying placeholder text for some period of time before it is replaced with the actual data.

To help solve this problem, I have added some new progress animations that are specifically designed to be used as placeholders while a component’s data is being loaded. These animations include methods for easily swapping themselves with other components while they are loading, and then seamlessly swapping back once the component is finished loading. Currently there are two ProgressAnimation classes:

  1. CircleProgress - A circle that progressively draws itself then erases itself. This is a variation on the classic infinite progress animation, but it is more flexible than the built-in InfiniteProgress class.

  2. LoadingTextAnimation - This animation appears to be a paragraph of text that is being typed progressively one word at a time - except instead of words, it just renders filled rectangles. This is useful for indicating that a block of text is loading.

Example using CircleProgress

Form f = new Form("Hello", new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE));
Form prev = CN.getCurrentForm();
Toolbar tb = new Toolbar();
f.setToolbar(tb);
tb.addCommandToLeftBar("Back", null, evt->{
    prev.showBack();
});
Label nameLabel = new Label();
$(nameLabel).setAlignment(CENTER);
nameLabel.setText("placeholder");
f.add(BorderLayout.CENTER, nameLabel);
// Replace the label by a CircleProgress to indicate that it is loading.
CircleProgress.markComponentLoading(nameLabel)
        .getStyle().setFgColor(0xff0000);

AsyncResource<MyData> request = fetchDataAsync();
request.ready(data -> {
    nameLabel.setText(data.getName());

    // Replace the progress with the nameLabel now that
    // it is ready, using a fade transition
    CircleProgress.markComponentReady(nameLabel, CommonTransitions.createFade(300));
});

f.show();

And the result looks like:

Example Using LoadingTextAnimation

Form f = new Form("Hello", new BorderLayout(BorderLayout.CENTER_BEHAVIOR_SCALE));
Form prev = CN.getCurrentForm();
Toolbar tb = new Toolbar();
f.setToolbar(tb);
tb.addCommandToLeftBar("Back", null, evt->{
    prev.showBack();
});
SpanLabel profileText = new SpanLabel();

profileText.setText("placeholder");
f.add(BorderLayout.CENTER, profileText);
// Replace the label by a CircleProgress to indicate that it is loading.
LoadingTextAnimation.markComponentLoading(profileText);

AsyncResource<MyData> request = fetchDataAsync();
request.ready(data -> {
    profileText.setText(data.getProfileText());

    // Replace the progress with the nameLabel now that
    // it is ready, using a fade transition
    LoadingTextAnimation.markComponentReady(profileText, CommonTransitions.createFade(300));
});

f.show();

And the result looks like:

]]>
1,033 Using Component Placeholders While Loading Data 0 0 0
When You Cannot Afford to Block the EDT blog/invoke-without-blocking.html Fri, 29 Nov 2019 00:00:00 +0200 steve https://www.codenameone.com/blog/invoke-without-blocking.html

We recently added support for disabling invokeAndBlock() for certain sections of code. The syntax is:

CN.invokeWithoutBlocking(()->{
      // This code is not allowed to call invokeAndBlock()
});

If any attempt is made to execute invokeAndBlock() inside that block of code, it will throw a BlockingDisallowedException.

This can be useful for ensuring that your UI will be responsive, especially in cases where you’re building a new form to show to the user, and any delay will be noticed by the user.

For example:

Button btn = new Button("Show Details");
btn.addActionListener(evt->{
    DetailsForm form = new DetailsForm();
    form.show();
});

The intention is that the user presses the “Show Details” button, and they are shown some sort of details form. But what if, somewhere in the construction of the DetailsForm, there is a call to invokeAndBlock()? A common reason for this is if a network call is performed during the building of the form. For example:

DetailsModel model = loadDetailsFromServer();  // A synchronous call to the server
nameField.setText(model.getName());
...

That loadDetailsFromTheServer() is a synchronous call to the server, so it uses invokeAndBlock() to be able to “block” control flow without actually blocking the EDT. It’s great that it doesn’t block the EDT, but it still blocks our ability to deliver the details form that the user is expecting. The form won’t be built until after the network request complete, then the user will experience a short delay before transitioning to the new form.

A better way is to build and show the DetailsForm, perform an asynchronous network request, and then update the details form with the data received when the network request completes. E.g.

loadDetailsFromServerAsync().ready(model->{
    nameField.setText(model.getName());
    revalidateWithAnimationSafety();
});

An easy fix in this simple case. But in the real world apps are much more complex. Calls to invokeAndBlock() may be nested deep within your app, or its libraries, so it may not be easy to ensure that the DetailsForm can be created without blocking.

This is where invokeWithoutBlocking() comes in handy. Let’s wrap our form creation to make sure that it doesn’t get bogged down by a slow network request.

Button btn = new Button("Show Details");
btn.addActionListener(evt->{
    CN.invokeWithoutBlocking(()->{
        DetailsForm form = new DetailsForm();
        form.show();
    });
});

This is guaranteed to show the form instantly, or it will throw a BlockingDisallowedException. This may be useful, especially during development, to help you hunt down those pesky network requests to get them out of your way. You can also catch these exceptions in order to provide an alternate path in the case that you can’t build the form without blocking.

Button btn = new Button("Show Details");
btn.addActionListener(evt->{
    try {
        CN.invokeWithoutBlocking(()->{
            DetailsForm form = new DetailsForm();
            form.show();
        });
    } catch (BlockingDisallowedException ex) {

         showAlternateForm();
    }
});

Background: When to Block the EDT

Synchronous programming is easier to write and understand than asynchronous programming. You can follow the flow of the code line by line, knowing that line n is run after line n-1, and before line n+1. The problem with synchronous programming is that it causes problems for long-running tasks on threads that can never block. Threads like the event dispatch thread (EDT), which is where most application code runs since it is the only thread that is authorized to interact with the UI. Codename One’s invokeAndBlock() method is a nifty tool which allows you to “block” the current event dispatch, without blocking any other event dispatches. Other events and callSerially blocks will continue to be processed while this event is blocked.

This allows us to do cool things like:

MyData data = loadSomeDataFromTheServer();
updateUIWithMyData(data);

This can be run directly on the EDT without blocking it because events will continue to be delivered and processed while the single event dispatch that is running this code is waiting for the data to be retrieved from the server. In most cases, this is much more elegant than callbacks, promises, or workers. It is easier to write, and easier to understand.

However, there is a cost to using invokeAndBlock(). It still blocks the current dispatch, which can be problematic in certain contexts. E.g. What if we block the pointer-press dispatch. This might cause components to receive the corresponding pointer-release before the pointer-press - which is counter-intuitive.

I generally try to avoid using invokeAndBlock() directly inside event handlers like action events or pointer events, because you could be getting in the way of other event handlers that are relying on the order events. E.g., Instead of:

button.addActionListener(evt->{
    doSomethingWithBlockingCode();
});

Prefer:

button.addActionListener(evt->{
    CN.callSerially(evt->{
        doSomethingWithBlockingCode();
    });
});

This way it will only block the contents of that particular callSerially() dispatch. The action event dispatch (which is probably running inside a pointer pressed or pointer release dispatch) can continue to be delivered to other listeners unimpeded.

I also try to avoid blocking while constructing user interface components to show the user. Usually when I create a form, I want to create it now to show the user instantly.

You can also use callSerially() in this case to prevent the blocking.

E.g.

class MyForm extends Form {

   public MyForm() {
        ...
        myLabel.setText("Some placeholder");
        callSerially(()->{
            MyData data = loadMyDataFromServer();
            myLabel.setText(data.getSomeString());
            revalidateWithAnimationSafefy();
        }
    }
}

Conclusion

invokeAndBlock() is a great tool for simplifying control flow, but in some circumstances, using it can negatively impact the user experience. Two places where invokeAndBlock() should be avoided is during the construction of UI forms to display to the user, and directly inside an event handler. In some cases you can mitigate the cost of invokeAndBlock() by simply wrapping it in a callSerially() (e.g. inside an event handler) so that it doesn’t block the current event. When constructing a UI component like a form, you can use invokeWithoutBlocking() to provide guarantees that your code doesn’t block, and be sure that the user will be shown your form without delay.

]]>
1,034 When You Cannot Afford to Block the EDT 0 0 0
Updates and Expansion blog/updates-expansion.html Sun, 24 Nov 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/updates-expansion.html

I haven’t blogged in a while. I’ve been busy working with a couple of startups, some enterprise customers and bringing new people on-board. Steve has been great in picking up some of the slack but his plate is too full to blog with the same frequency I had so the blog slowed down a bit during this time. I hope to pick it back up to a weekly post regiment but my schedule is just so tight I barely have time to breath.

The good news is that we’re expanding a bit and recently hired a much needed graphics designer to overhaul everything around here with a new coat of paint. Steve and I spent a lot of time with him and we’re super excited about this!

We’ve also made some hirings in the development area which I’m pretty bullish about, hopefully they will turn out nicely too!

Location Regression

Over the weekend we had a hard time with the location API on Android. In fact we had that last weekend too and we eventually reverted last weeks release to the one from the prior week. This week we pushed through and hopefully we resolved the issues. However, if you’re experiencing issues with the location API please let us know ASAP.

The reason behind this is the usual flurry with Google. They keep changing the behavior of devices with every API update. This is very problematic for applications that rely on background location, as such it forced us to update to play services version 12.0.0 which caused a cascade of issues across the board. Unfortunately this is inevitable as Google requires updates to newer versions for app submissions.

Version 7.0

We postponed version 7.0 multiple times by now and I still don’t see how we’ll make the current deadline which is set to Christmas eve of all dates…​ I’m afraid we’ll need to push that back again with our current rate of progress, hopefully our additional developer resources will help us align more closely to these schedules in the future.

]]>
1,035 Updates and Expansion 0 0 0
Runtime Debugging with Groovy Console blog/runtime-debugging-with-groovy-console.html Tue, 5 Nov 2019 00:00:00 +0200 steve https://www.codenameone.com/blog/runtime-debugging-with-groovy-console.html

We recently added a new tool to the Codename One simulator that will allow you to run arbitrary code snippets in your app’s runtime environment.   You can access it from “File” > “Groovy Console”, while your app is running in the simulator.

This will open the following dialog that will allow you to execute arbitrary Groovy code:

As you may suspect, you should use the Groovy programming language in this console.  If you’re not familiar with Groovy, don’t worry.  The language is basically just Java with some nice short-cuts.  In fact, in many cases, you can probably just write Java code, and it will still work.  Personally, I like the way that Groovy lets you refer to object properties via their property name, so you don’t need to use the getter or setter methods explicitly.  E.g. You can do “form.title”, instead of “form.getTitle()”, to get the form’s title.   The former is converted into the latter automatically for you.

First Example: Getting the Current form’s title

As a simple first example, let’s get the current form’s title and print the value in the console.   The default content in the console already has a reference to the current form via the “form=CN.currentForm” line.  So all we need to do is add:

Then press “Command-Enter” (or Ctrl-Enter on Windows/Linux”), to see the output.  You should see something like the following in the bottom-half of the console:

Second Example: Showing a Dialog:

You can also interact with the UI.  Create forms, components, dialogs - or essentially create entire apps.  Below, you can see us displaying a simple dialog directly in the console.

Using the ComponentSelector

My most frequent uses of the console is to find out the properties on some component in the UI.  For this sort of thing, the ComponentSelector class is an invaluable tool.  It makes it easy to find the components that you’re interested in, and to inspect its state.

For example, we might want to look at the font height of all instances of the Label class on the current form:

The above example demonstrates some fundamentals of both Groovy, and of the ComponentSelector class.

  1. `$(‘*’, form)` - This creates a set of all components in the current form.  “*” matches all.
  2. `.filter{ ..}` - This method filters the set so that only the components for which the closure returns `true` are included.    Notice the convenient notation for a closure in Groovy:  `{ .. }`.  And it provides you with the implicit variable “it” which refers to the parameter in a single-parameter method closure.
  3. `.each{...}` - Allow you to execute code on “each” element in the set.  Again, we use the convenient closure notation and implicit “it” object provided by Groovy.

Useful for Troubleshooting Requests

One potential area where the console may have some powerful uses, is in the area of trouble-shooting and bug reporting.  If a developer asks the community for help in debugging a problem in their app it is now possible for community members to share snippets that might help to diagnose the problem.  It was already possible to share snippets, but the console makes it easier to provide advice like:  “When you get to the part of your app that is having the problem, try running this snippet of code inside the console, and let me know what the output is”.

Versus Debugging in the IDE

It is worth noting that you could already inspect the runtime state of your app using your IDE debugger.  You can pause the app, then evaluate arbitrary Java expressions to see their output.  You can Add break-points, and even make source code changes that are applied seamlessly at runtime.  The console is not meant to replace this functionality.  It is meant to provide a slightly lighter-weight approach to achieve many of the same things.  

You’ll have to find your own usage patterns, but I have found that it is handy to be able to just open up a console, whether or not my app is being run in “Debug” mode, and start tinkering with some code to get a clearer picture of what is going on inside my app.

Share Your Snippets

I’ve only posted a couple of simple examples of how you might use the console, but the sky is the limit.  Please share your own snippets and tips if you discover any tricks that might be helpful to other users.  You can share them below in the comments.

]]>
1,036 Runtime Debugging with Groovy Console 0 0 0
WKWebView and PRs blog/wkwebview-prs.html Sat, 5 Oct 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/wkwebview-prs.html

We still have features to cover from our summer vacation but we need to make a short de-tour through newer things that landed recently. One of the big highlights is the switch to WKWebView. We effectively changed the default iOS browser component to WKWebView instead of UIWebView. This resolved warnings Apple started sending out to developers about using the out of date UIWebView.

This mostly went unnoticed by most developers as it should. But if your browser starts acting up this is the reason. There isn’t much we can do here as we knew that day would come where Apple will demand a switch.

Video Library

Francesco Galgani created an impressive cn1lib for low level video access named "VideoOptimizer". It supports compressing video files for distribution, grabbing video frame screenshots, getting the duration of a video etc.

This is pretty cool and also a pretty difficult task as it involved integrating the ffmpeg native library in an Android build with an AAR to package the whole thing.

ReleasableComponent

ramsestom implemented a generic interface for releasable components in PR #2910. Before this Codename One had special cases for Button so if a user pressed a button and didn’t release it we made sure to let the button know about this at some point…​

This is now generic via the new ReleasableComponent (named IReleasable in the PR which was renamed in the following commit).

Popup Direction and Mime Guessing

Francesco Galgani submitted two PRs. First #2914 which lets you explicitly set the auto complete popup direction e.g.:

autoComplete.setPopupPosition(AutoCompleteTextField.POPUP_POSITION_OVER);

Valid values are: POPUP_POSITION_AUTO, POPUP_POSITION_OVER and POPUP_POSITION_UNDER.

The second PR #2925 includes an API to guess common file mime types from the first few bytes of a file. Using Util.guessMimeType e.g.:

String mimeType = Util.guessMimeType(fileOrStorageFile);
]]>
1,037 WKWebView and PRs 0 0 0
Push Cheatsheet blog/push-cheatsheet.html Sat, 28 Sep 2019 00:00:00 +0300 steve https://www.codenameone.com/blog/push-cheatsheet.html

Push support is one of the most complicated features to set up, due to all of the red tape you have to cut through on each platform. Each platform has its own series of hurdles you have to jump through. Apple (iOS) requires you to generate push certificates. Google (Android) requries you to set up a project in Firebase console. Microsoft requires you to register your app for the Windows store. And that’s just the beginning.

Our developer’s guide provides detailed instructions on how to perform each of these steps to get up and running, but it is quite verbose. If you’ve already gone through the guide to setup push for your first app, you may not require quite such a detailed guide to help you with your 2nd app. If you’re like me, you just want a birds-eye view of what is required to get push working, and not necessarily a step-by-step tutorial on how to meet those requirements.

So…​ I have created a Push Cheatsheet, to serve as a quick reference for setting up push notifications in your mobile apps.

Image 160919 061502.600

As with all CN1 docs, this cheatsheet is a living document that will evolve based on your feedback. You can always download the latest PDF at https://www.codenameone.com/files/push-cheatsheet.pdf .

I have tried to include all of the pertinent details required to get Push working on all platforms. Each platform has its own "box" with setup instructions. It also includes a minimal code snippet to show how to implement Push support in your codename one app - in particular how to register the device for push, and receive push messages. There is a box for "Sending a Push", which shows the format of an HTTP request that you can use to send a push message to your app’s users.

GET parameters in this snippet are color-coded and cross-referenced with other parts of the cheatsheet so you can easily see where to find this information. E.g. The FCM_SERVER_API_KEY in the HTTP request is purple and bold, corresponding to its mention in the "Android Client Setup" box, so you can see that this value comes from the google firebase console.

Finally, there is a section documenting the different push message type values, so you can quickly decide which type of push is appropriate for a given situation.

Please let us know if you can think of anything that would improve the utility of this cheatsheet.

Happy pushing!

]]>
1,038 Push Cheatsheet 0 0 0
Terse Table, Radar Chart and Networking Enhancements blog/terse-table-radar-chart-networking.html Sat, 14 Sep 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/terse-table-radar-chart-networking.html

This is the 3rd installment of the updates we did over the summer and so far it isn’t the last one. We have at least one more part coming in next week…​

Terse Table Layout

TableLayout is pretty darn verbose e.g. this snippet from the TableLayout JavaDoc:

cnt.add(tl.createConstraint().
        horizontalSpan(2).
        heightPercentage(80).
        verticalAlign(Component.CENTER).
        horizontalAlign(Component.CENTER),
            new Label("Span H")).

    add(new Label("BBB")).

    add(tl.createConstraint().
        widthPercentage(60).
        heightPercentage(20),
            new Label("CCC"));

This is pretty verbose and a bit painful to write so we added shorthand methods:

  • vs() = verticalSpan()

  • hs() = horizontalSpan()

  • wp() = widthPercentage()

  • hp() = heightPercentage()

  • ha() = horizontalAlign()

  • va() = verticalAlign()

  • cc() = constraint()

As such we can rewrite the snippet above as such:

cnt.add(tl.cc().
        hs(2).
        hp(80).
        va(Component.CENTER).
        ha(Component.CENTER),
            new Label("Span H")).

    add(new Label("BBB")).

    add(tl.cc().
        wp(60).
        hp(20),
            new Label("CCC"));

RadarChart

David Day contributed new support for RadarChart in this pull request. You can check out a sample of the radar chart in the samples. Specifically:

public void showChart() {
    Form f = new Form("RadarChartSample", new BorderLayout());
    f.add(BorderLayout.CENTER, getSampleChart());
    f.show();
}

ChartComponent getSampleChart(){
    // Create dataset
    AreaSeries dataset = new AreaSeries();

    CategorySeries series1 = new CategorySeries("May");
    series1.add("Health", 0.8);
    series1.add("Attack", 0.6);
    series1.add("Defense", 0.4);
    series1.add("Critical", 0.2);
    series1.add("Speed", 1.0);
    dataset.addSeries(series1);

    CategorySeries series2 = new CategorySeries("Chang");
    series2.add("Health", 0.3);
    series2.add("Attack", 0.7);
    series2.add("Defense", 0.5);
    series2.add("Critical", 0.1);
    series2.add("Speed", 0.3);
    dataset.addSeries(series2);

    // Setup renderer
    DefaultRenderer renderer = new DefaultRenderer();
    renderer.setLegendTextSize(32);
    renderer.setLabelsTextSize(24);
    renderer.setLabelsColor(ColorUtil.BLACK);
    renderer.setShowLabels(true);

    SimpleSeriesRenderer r1 = new SimpleSeriesRenderer();
    r1.setColor(ColorUtil.MAGENTA);
    renderer.addSeriesRenderer(r1);

    SimpleSeriesRenderer r2 = new SimpleSeriesRenderer();
    r2.setColor(ColorUtil.CYAN);
    renderer.addSeriesRenderer(r2);

    // Create chart
    return new ChartComponent(new RadarChart(dataset,renderer));
}

Networking Enhancements

Better Error Code Handling

Up until recently if we got an error response code it wasn’t sent through the global error handler and was handled via the local error handling chain first. This is no longer the case and these errors are now handled correctly.

However, if you relied on that misbehavior of older versions we have setHandleErrorCodesInGlobalErrorHandler(boolean). This defaults to true, you can set it to false to change the default behavior.

Network Monitor Stats

Network monitor now has stats for time duration etc. You can see these by opening the network monitor and inspecting the values:

Network Monitor Enhancements
Figure 1. Network Monitor Enhancements

Data Interface

The Data interface can be used to abstract data that has size and can be appended to an OutputStream. This is very handy for providing large amounts of data for processing, so that the data itself doesn’t need to be passed around. This interface should be used in a few places in the API to facilitate working with large data objects, such as for file uploads.

E.g. For multi-part file uploads the best way to deal with large objects right now is to store them in a file, and then provide the file path to the connection request, so that the platform’s native implementation can chunk or stream it appropriately.

This interface provides potentially a cleaner more generic way to pass large amounts of data to a connection request.

The main entry point for this functionality is ConnectionRequest.setRequestBody(Data data). The interface itself is pretty trivial and contains two methods:

public interface Data {

    /**
     * Appends the data's content to an output stream.
     * @param output The output stream to append to.
     * @throws IOException
     */
    public void appendTo(OutputStream output) throws IOException;

    /**
     * Gets the size of the data content.
     * @return Size of content in bytes.
     * @throws IOException
     */
    public long getSize() throws IOException;
}

Within the interface there are several concrete implementations specifically StringData, FileData, StorageData and ByteData. E.g.:

public static class StringData implements Data {
    private byte[] bytes;

    public StringData(String str) {
        this(str, "UTF-8");
    }

    public StringData(String str, String charset) {
        try {
            bytes = str.getBytes(charset);
        } catch (UnsupportedEncodingException ex) {
            Log.e(ex);
            throw new RuntimeException("Failed to create StringData with encoding "+charset);
        }
    }
    @Override
    public void appendTo(OutputStream output) throws IOException {
        output.write(bytes);
    }

    @Override
    public long getSize() throws IOException {
        return bytes.length;
    }
}

This makes things such as working with files in Storage (as opposed to FileSystemStorage) easier.

]]>
1,039 Terse Table, Radar Chart and Networking Enhancements 0 0 0
Icon Fonts, Popups and Infinite Scroll blog/icon-fonts-popups-infinite-scroll.html Sat, 7 Sep 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/icon-fonts-popups-infinite-scroll.html

As I mentioned in the last post there are a lot of new features we need to go over and it will take time to cover everything. In fact this is still a partial list and there’s a lot more on the way…​

Easier Icon Font Integration

First off Thomas submitted a PR for a few new font icon methods:

Specifically in Label he added:

public void setFontIcon(Font font, char c);
public void setFontIcon(Font font, char c, float size);

And in FontImage he added:

public static void setFontIcon(Label l, Font font, char icon, float size);
public static void setFontIcon(Label l, Font font, char icon);
public static void setFontIcon(Command c, Font font, char icon, String uiid, float size);

These new methods work in a similar way to the set material icon methods. However, they can work with an arbitrary icon font. That was possible to do before this change but this change makes that easier and makes the code more fluid. It also works similarly to the material API where icons are implicitly applied to all the states of the buttons. That means the pressed/selected/disabled styles will apply to the icon as well as the text.

Additional Icon Fonts

Francesco Galgani came up with a solution for #2421 which finally allowed us to update the material font and font constants.

This has been a long time RFE which came up every few months. When we launched the material font support. Unfortunately, Google stopped updating that font a few years ago and everyone who relied on it was stuck. Thankfully Francesco found an up to date version of the font as well as the diffs between the old/new font.

As a result we now have a lot more constants you can use when setting an icon in a default Codename One app. You can see the full list here.

Lightweight Popup Dialog

When Codename One was young we needed a popup arrow implementation but our low level graphics API was pretty basic. As a workaround we created a version of the 9-piece image border that supported pointing arrows at a component.

Since we added a proper graphics pipeline we wanted to rewrite that logic to use proper graphics. This allows for better customization of the border (color, shape etc.) and it looks better on newer displays. It also works on all OSs. Right now only the iOS theme has the old image border approach.

To solve this we added new support for arrows into the RoundRectBorder API. If you style a popup dialog with a round rect border this should "just work" and use this API. By default popup dialogs are styled that way with the exception of iOS where they still have the image border styling for compatibility (although we might change that).

This works by setting the track component property on border. When that’s done the border implicitly points to the right location.

Continue the Infinite

InfiniteContainer and InfiniteAdapter work great for most use cases but they have a bit of an "undefined" behavior when it comes to failure. E.g. if we have a network error and don’t have anything to fetch as a result.

to solve this we added this method to InfiniteContainer:

public void continueFetching();

And these to InfiniteScrollAdapter:

public void continueFetching();
public static void continueFetching(Container cnt);

So when you get an error you can just add this error button and return null:

SpanButton errorButton = new SpanButton("Networking Error, Press to retry");
errorButton.addActionListener(e -> {
     errorButton.remove();
     continueFetching();
});
add(errorButton);
return null;

The error button will let the user retry the operation and continue fetching the content even though it was previously "stopped".

More Coming

As I mentioned at the top of this post, this is still the tip of the iceberg of new features we’ve worked on over the summer. More is coming!

]]>
1,040 Icon Fonts, Popups and Infinite Scroll 0 0 0
We're Back from Vacation blog/back-vacation.html Tue, 3 Sep 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/back-vacation.html

Summer is finally over and the kids are going back to school/kindergarten so it’s time to go back to our regularly scheduled posts. I won’t be posting as often as before as I’ll dedicate more time for support/development activities but still there’s a lot to write about…​

During our time off we had a lot of changes, I’ll repeat a few big ones which you might have run into already and cover a few that might have gone under the radar.

GCM Removal and FCM as Default

During the month of August Google finally removed their old GCM servers. We’ve prepared for this ages ago but this still took us a bit off guard. We were ready for the switch itself but there were still a couple of things we weren’t prepared for.

Users who still used the old style of push notifications (prior to the google-services.json file approach) had push messages blocked. That was expected.

You can read about the modern approach to push here

Because that no longer works anyway we switched the default build mode to FCM. This solves an issue for developers who neglected to define the android.messagingService=fcm build hint (which you no longer need). However, this causes a build error if you don’t have that JSON file in place. You can get this to compile for now by explicitly stating the build hint android.messagingService=gcm. However, push won’t work if you do that since the Google run GCM push servers are no longer there. But it will compile which is a start.

To migrate to the new FCM approach check out the developer guide section on push.

API Level 28 and HTTPS Requirement

We migrated the build servers to Android API level 28 as required by Google. This migration was a bit painful because Google changed the way clipping works under Android so we had to make some extensive changes to our rendering pipeline.

However, one thing we can’t mitigate is that Google now blocks HTTP connections (not HTTPS).This is generally a good practice and a requirement on iOS as well. However, if you have an HTTP URL you need to use you can do so with the build hint:

android.xapplication_attr=android:usesCleartextTraffic="true"

Component Inspector Enhancements

We implemented a couple of RFEs in the venerable component inspector, specifically 2695 and 1476.

You can now refresh the component tree and the selection would remain in place but more importantly you can select a component and it will be highlighted in the UI. This is very helpful as a debugging tool.

Component inspector highlights current selection
Figure 1. Component inspector highlights current selection

AutoCompleteTextComponent and TextComponentPassword

Francesco Galgani contributed an implementation of AutoCompleteTextComponent which allows you to use an auto-complete with the text component framework for a more fluid input UI.

He also contributed TextComponentPassword which is a password field with the same convention. It carries the more modern "show password" icon convention which is far more convenient than the old "double type" approach.

Error Callbacks for URLImage

It’s hard to handle errors in URLImage objects. Because they are so "seamless" the point for exception handling is deep withing the class. To solve issue 2703 we had to do something different.

You can now use the static method setExceptionHandler on URLImage. It accepts the inner interface ErrorCallback which has a single method:

public static interface ErrorCallback {
    public void onError(URLImage source, Exception err);
}

So effectively you can do something like this:

URLImage.setExceptionHandler((img, err) -> handleError());

So Much More

I’ll write about other things in the coming weeks as this post is getting a bit long. There’s a lot to cover.

]]>
1,041 We're Back from Vacation 0 0 0
Summer Vacation blog/summer-vacation.html Wed, 26 Jun 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/summer-vacation.html

You might have noticed we’ve been a bit slow with updates over the past couple of weeks as we’re dealing with a bit of a backlog. With the kids on holiday from school it’s harder to keep up not to mention our travel plans for the summer. As such we’re officially in summer vacation until September.

During this time we won’t update the blog as that takes a bit too much. We will probably have Friday releases if warranted but this depends on commits. It will take us longer to evaluate some issues but we’ll still try to respond to everyone in a reasonable timeframe. Pro and enterprise support might be impacted as well due to personnel availability but should still provide fast response turnaround.

]]>
1,042 Summer Vacation 0 0 0
Post Message blog/post-message.html Tue, 18 Jun 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/post-message.html

BrowserComponent is a pretty powerful tool when you just want to integrate HTML into your application. We use it a lot in native apps, but surprisingly it’s just as useful when we compile an app as a web application. It lets us embed HTML into the web application. But there’s a big caveat known as SOP when we do that.

SOP and CORS

SOP is the "Same Origin Policy" enforced by browsers. It’s prevents CSRF (Cross Site Request Forgery) which essentially lets a site pretend it’s a different site.

SOP makes sure all requests to the site come from the same origin, it’s enforced by the browser and essentially blocks manipulations across domains. This sounds great but if you need to communicate between two sites it might be a problem. That’s where CORS comes in.

CORS (Cross Origin Resource Sharing) is essentially the SOP compliant way for a website to communicate with a different website. There are HTTP headers you need to add etc. I won’t go into it as there are better resources on all of this.

Post Message

So lets say we have a browser component from a different domain embedded in our app. Normally in a native app that’s no problem. We can inject JavaScript into it and do whatever we want.

However with the JavaScript port this becomes a bit of an issue because of SOP. postMessage makes the communication between the two origins possible but to get it to work you need to have code that handles sends the messages on both sides.

For this we now have the following API’s in BrowserComponent:

public void postMessage(String message, String targetOrigin);
This API will work correctly in native apps as well so you can still enjoy portability

This method Calls the postMessage() method on the webpage’s window object. To receive a message, the web page should register a "message" event listener, just as it would to receive messages from other windows in the browser. See MDN docs for postMessage() for more information.

]]>
1,043 Post Message 0 0 0
Flamingo SVG Transcoder Revisited blog/flamingo-svg-transcoder-revisited.html Wed, 12 Jun 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/flamingo-svg-transcoder-revisited.html

A couple of years ago I wrote about our support for Flamingo which translates static SVG files to Java source files that you can treat as images within Codename One while getting pixel perfect resolution independent results. There were a few minor changes since until a month ago when Steve committed some work to address this RFE.

What’s SVG and Why Should I Care?

If you’re new to SVG here’s a quick primer. There are two types of images: Vector and Raster. Raster is what most of us work with every day. They are our JPEG, GIF, BMP and PNG images. They store the pixels of the image. There are many nuances here but I’ll leave it at that…​

Vector images are something completely different. Instead of storing the pixels they store the primitive drawing operations. If we have an image of a triangle, a raster image will store a lot of pixel values representing the triangle whereas a vector image would just store the fact that a triangle is drawn at a specific size in a given coordinate.

Vector images work great for images that were designed to be vector images e.g. logos, drawings etc. They are useless for things such as photos. They have a couple of big advantages:

  • They are smaller than raster images both on disk and more importantly in RAM

  • They can scale to any size with no degradation in image quality, this is very helpful for mobile apps

But surprisingly they might not perform as well as a raster image for all cases. This might be offset by the RAM savings so I’d still recommend SVG for most cases.

Flamingo works by translating SVG’s to Java source code that you can compile in Codename One. This means more elaborate features such as SVG animations are discarded as part of the process. It also means some SVG’s just won’t work. With the new update we added support for a lot of SVG’s and also updated the flamingo binary distribution which we didn’t have in the original project.

The recent changes include both improvement to the translation process and to the Codename One API. Our updated API now supports features such as paint and gradient drawing which is commonly used in SVG. Specifically Steve added a Paint interface, with concrete subclass LinearGradientPaint that can be used to fill a shape with a gradient via Graphics.setColor(Paint) then Graphics.fillShape(shape). There are quite a few other related changes such as MultipleGradientPaint and AffineTransform…​

With these in place you can convert a lot of typical SVG’s and use them in your app. The big question is whether we’ll integrate this deeper into Codname One by automating this process. Right now we’re not sure about that or how such integration will look. Maybe something similar to the CSS integration.

]]>
1,044 Flamingo SVG Transcoder Revisited 0 0 0
Sheets and Samples blog/sheets-samples.html Tue, 4 Jun 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/sheets-samples.html

Over the years we wrote a lot of demos for Codename One as well as a lot of test cases. This can get painful after a few dozen projects each of which with their own version of the JARs and build script. To solve this never ending need to organize samples Steve introduced a new samples folder into the open source project.

This makes it easy to build/run samples if you’re command line inclined. All you need is an install of Java and Ant to be in business. Steve introduced this quite a while back but it took a while to reach a critical mass of samples which it recently passed. By now the samples cover a lot of the functionality of Codename One. There’s a lot of documentation for the samples folder here so I won’t go too much into details. I will however refine a few steps in the getting started process…​

To run the samples you need to checkout the git cn1 project then do the following in the project root directory:

ant samples
JAVA_HOME must point at a valid JDK for Codename One and ant must by in the system PATH variable. Otherwise this won’t work.
The Sample Runner App
Figure 1. The Sample Runner App

It doesn’t look pretty but you can run a sample for almost any Codename One component and see the applicable code. You can send device builds etc. This is a very convenient system for test cases.

Sheet

As such the samples contain a sample for the new Sheet API which you can see here. This is it, there is no need for resources or anything, the file is self contained.

Sheet is a new API that lets you show the property sheet UI common in iOS and recently on Android as well. It was easy to do this in the past but there was no dedicated class to address it…​ Now there is.

The Sheet Sample
Figure 2. The Sheet Sample
]]>
1,045 Sheets and Samples 0 0 0
Photo Cropping Wizard blog/photo-cropping-wizard.html Wed, 29 May 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/photo-cropping-wizard.html

A month ago Francesco asked how to implement the common image cropping UI we see in many applications where the user can pan and zoom to decide the cropping area. We implemented that feature by effectively enhancing the ImageViewer class with two new methods:

public Image getCroppedImage(int backgroundColor);
public Image getCroppedImage(int width, int height, int backgroundColor);

But that’s just the first step, implementing the whole UI is still not trivial. We’d like to add a more general purpose component that does that but doing this with an approach that’s sufficiently generic is hard…​

Francesco wrote some code that used the API but it was still non-trivial. So I took that code as a starting point and implemented something that’s slightly more generic, it still isn’t perfect but it should be a good starting point for a more general purpose component.

Since this uses the ImageViewer you can use pinch to zoom and drag to position the image the way you want:

private void cropImage(Image img, int destWidth, int destHeight,
        OnComplete<Image> s) {
    Form previous = getCurrentForm();
    Form cropForm = new Form("Crop your avatar", new LayeredLayout());

    Label moveAndZoom = new Label("Move and zoom the photo to crop it");
    moveAndZoom.getUnselectedStyle().setFgColor(0xffffff); (1)
    moveAndZoom.getUnselectedStyle().setAlignment(CENTER);
    moveAndZoom.setCellRenderer(true);
    cropForm.setGlassPane((Graphics g, Rectangle rect) -> {
        g.setColor(0x0000ff);
        g.setAlpha(150); (2)
        Container cropCp = cropForm.getContentPane();
        int posY = cropForm.getContentPane().getAbsoluteY();

        GeneralPath p = new GeneralPath(); (3)
        p.setRect(new Rectangle(0, posY, cropCp.getWidth(), cropCp.getHeight()), null);
        if(isPortrait()) {
            p.arc(0, posY + cropCp.getHeight() / 2 - cropCp.getWidth() / 2,
                cropCp.getWidth() - 1, cropCp.getWidth() - 1, 0, Math.PI*2);
        } else {
            p.arc(cropCp.getWidth() / 2 - cropCp.getHeight() / 2, posY,
                cropCp.getHeight() - 1, cropCp.getHeight() - 1, 0, Math.PI*2);
        }
        g.fillShape(p);
        g.setAlpha(255);
        g.setColor(0xffffff);
        moveAndZoom.setX(0);
        moveAndZoom.setY(posY);
        moveAndZoom.setWidth(cropCp.getWidth());
        moveAndZoom.setHeight(moveAndZoom.getPreferredH());
        moveAndZoom.paint(g);
    });

    final ImageViewer viewer = new ImageViewer();
    viewer.setImage(img);

    cropForm.add(viewer);
    cropForm.getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_CROP, e -> {
        previous.showBack();
        s.completed(viewer.getCroppedImage(0). (4)
                fill(destWidth, destHeight));
    });
    cropForm.getToolbar().addMaterialCommandToLeftBar("", FontImage.MATERIAL_CANCEL, e -> previous.showBack());
    cropForm.show();
}
1 This label is drawn manually on top of the glass pane overlay
2 The glass pane highlights invisible portions although the cropped image in our case is still square, this is pretty common behavior as a UI will sometimes show the image rounded
3 This shape represents the blue tinted overlay on top

The UI results in an image like this:

The Crop UI on the Simulator
Figure 1. The Crop UI on the Simulator

To get to that point we need the UI that invokes this code which looks like this:

Form hi = new Form("Cropped Image", BoxLayout.y());
hi.getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_CAMERA, e -> {
    Capture.capturePhoto(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent evt) {
            if (evt == null || evt.getSource() == null) {
                // the user cancelled the image capturing
                return;
            }
            String file = (String) evt.getSource();
            try {
                Image img = Image.createImage(file);
                cropImage(img, 256, 256, i -> {
                    hi.removeAll();
                    hi.add(new ScaleImageLabel(i));
                    hi.revalidate();
                });
            } catch (IOException ex) {
                Log.p("Error loading captured image from camera", Log.ERROR);
                Log.e(ex);
            }
        }
    });
});

hi.show();
]]>
1,046 Photo Cropping Wizard 0 0 0
Asynchronous Media blog/asynchronous-media.html Thu, 23 May 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/asynchronous-media.html

There are a lot of fixes and new features that I don’t get to cover enough as I’ve been busy on several fronts. One of the new features is support for asynchronous media API’s. These let us create a media object without waiting for it to complete. This is very useful if you have a complex UI and want to play a media file while doing other things.

E.g. if you’re scrolling in a social network feed and want to play a media preview. You might create a media object but don’t want it to block the current call. You can do this using code such as:

AsyncResource<Media> async = Display.getInstance().createMediaAsync(URL_TO_MEDIA, isVideo, null);
async.ready(mediaInstance -> playMedia(mediaInstance));

You will notice the usage of AsyncResource which is similar to a future or a promise in other platforms. It lets you monitor the status of an asynchronous approach. This block would execute quickly but the playMedia call would happen when loading is completed.

Rendering Hints

One of the API’s I dislike in JavaSE is the Graphics2D rendering hints. It’s a bit opaque in the choices it exposes. I want fast and good looking graphics but the tradeoff isn’t always clear. How much would I "pay" for good looking in this case in terms of speed and visa versa.

Now we also have one rendering hint in our graphics:

graphics.setRenderingHints(Graphics.RENDERING_HINT_FAST);

I’m not too crazy about the name as it’s a bit misleading. I’m sure developers would just turn it on to make everything "go fast" then complain when it has no impact…​ It doesn’t do that.

Only iOS uses this and even then only when rendering images. Since copying images to textures is expensive, we keep the last generated texture cached. This works well if we are always rendering the image at the same size. If we are constantly rendering the same image at different sizes, then we’ll constantly be invalidating the cache, this results in artifacts. This affected pinch zoom in the image viewer causing it to be choppy as it had to regenerate a texture for every change in size. When Fast rendering is enabled, we now only invalidate the texture cache if the image is larger or smaller than the existing texture by more than a factor of 2.

This change also fixed a bug that caused images to be rendered as black if they are larger than the max OGL texture size. Now it will cap the size of the texture at the max OGL size, and it will use the GPU to scale the texture to the desired size.

Uppercase

TextArea now supports a new UPPERCASE constraint which lets you request uppercase input. Generally it will just popup the keyboard with the capslock on. You can use it as such:

textFieldOrArea.setConstrainer(TextArea.UPPERCASE);
]]>
1,047 Asynchronous Media 0 0 0
TIP: Reordering Tabs blog/tip-reordering-tabs.html Tue, 14 May 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-reordering-tabs.html

The Tabs class is pretty powerful and flexible as I mentioned before. One thing it doesn’t support is drag and drop to re-order the tabs. Here the flexibility of Codename One takes over and allows us to accomplish it.

We can use the builtin drag and drop support for components within the tabs container. This is exactly how the code below works. In the drop listener we block the the default processing of drop behavior so we can move the tab manually to the new location:

Form hi = new Form("Tabs", new BorderLayout());

Tabs t = new Tabs();

t.addTab("T1", new Label("Tab 1"));
t.addTab("T2", new Label("Tab 2"));
t.addTab("T3", new Label("Tab 3"));
t.addTab("T4", new Label("Tab 4"));

Container tabsC = t.getTabsContainer();
tabsC.setDropTarget(true);
for(Component c : tabsC) {
    c.setDraggable(true);
    c.addDropListener(e -> {
        e.consume();
        Component dragged = c;
        int x = e.getX();
        int y = e.getY();
        int i = tabsC.getComponentIndex(dragged);
        if(i > -1) {
            Component dest = tabsC.getComponentAt(x, y);
            if(dest != dragged) {
                Component source = t.getTabComponentAt(i);
                int destIndex = tabsC.getComponentIndex(dest);
                if(destIndex > -1 && destIndex != i) {
                    String title = t.getTabTitle(i);
                    t.removeTabAt(i);
                    t.insertTab(title, null, source, destIndex);
                }
            }
            tabsC.animateLayout(400);
        }
    });
}

hi.add(CENTER, t);
hi.show();
]]>
1,048 TIP: Reordering Tabs 0 0 0
Lightweight Text Selection blog/lightweight-text-selection.html Tue, 7 May 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/lightweight-text-selection.html

Text editing is implemented natively in Codename One. This provides a huge benefit as it allows us to ignore a lot of complex topics such as virtual keyboard localization etc. If we had to implement all that logic the overhead of Codename One would have been huge…​

One free benefit is seamless support for copy & paste. It "just works" thanks to the usage of the native widget we don’t need to worry about that UI. However, this breaks down when we want to provide the ability to select & copy without the ability to edit. E.g. in a web app we can usually select any bit of text and copy it…​ That’s convenient for some cases.

A good example would be listing user details within the app that the user might want to copy e.g. his user ID

As part of an RFE we added support for that through a lightweight copy mechanism, this works for uneditable TextAreas and TextFields. It will also work for Labels and SpanLabels. Notice that this is off by default you need to enable this using parentForm.getTextSelection().setEnabled(true). Even then it will only work for TextAreas and TextFields by default. You will need to explicitly enable it on other components using setTextSelectionEnabled(true).

Form hi = new Form("Tabs", BoxLayout.y());
hi.getTextSelection().setEnabled(true);

SpanLabel s = new SpanLabel("Long label text that would still be selectable in this case");
s.setTextSelectionEnabled(true);
hi.add(s);

hi.show();
Lightweight Text Selection
Figure 1. Lightweight Text Selection
]]>
1,049 Lightweight Text Selection 0 0 0
Camera Kit Rewrite blog/camerakit-rewrite.html Tue, 30 Apr 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/camerakit-rewrite.html

The native low level camera API on Android is a disaster. It’s often cited as one of the worst API’s on Android. To make matters worse there are two separate API’s Camera and Camera2 (yes really). You need to use Camera2 where it’s available but that means no support for older devices. To solve this we picked the Android Camera Kit library when we started building our low level camera support. This proved to be a mistake.

Camera Kit was supposed to reach 1.0 status for about a year now. It constantly moved the goal posts and eventually moved the entire implementation to Android X which meant breaking compatibility on a large scale with existing code. After waiting endlessly for 1.0 we eventually tried to move to the latest beta but this proved unworkable due to the usage of Android X. So we decided to solve this by moving to Golden Eye. It’s a far simpler solution and as a result a stabler one.

As I write this it looks like Camera Kit is moving towards 1.0 but it’s still unclear

We still kept the name for the cn1lib which might breed some confusion so we’re open to suggestions here and might update it in the future.

As part of this overhaul we the library now supports the JavaScript port. It doesn’t support all the features but you should be able to get low level camera access in your web apps as well.

The nice thing is that the API is mostly unchanged. It can still be used with roughly the same API as we had before: https://github.com/codenameone/CameraKitDemo/

]]>
1,050 Camera Kit Rewrite 0 0 0
TIP: Rich View Revisited blog/tip-rich-view-revisited.html Tue, 23 Apr 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-rich-view-revisited.html

A couple of years ago I posted a tip about rich text view which worked out reasonably well. But it’s a bit outdated by now and it’s worth revisiting. During these two years we published the Facebook Clone which used this component.

In the facebook clone I did a lot of work for the rich text. This work added support for alignment and clickable hyperlinks as well as a couple of bug fixes. Following is the code I used in the Facebook Clone for the rich text.

public class RichTextView extends Container {
    private String text;
    private float fontSize = 2.6f;
    private EventDispatcher listeners = new EventDispatcher();

    private Font currentFont;
    private int currentColor = 0;
    private String currentLink;
    private Style lastCmp;
    private Font defaultFont;
    private Font boldFont;
    private Font italicFont;
    private int sizeOfSpace;

    public RichTextView() {
        init(null);
    }

    public RichTextView(String text, String uiid) {
        init(uiid);
        setText(text);
    }

    public RichTextView(String text) {
        init(null);
        setText(text);
    }

    private void init(String uiid) {
        boldFont = Font.createTrueTypeFont(NATIVE_MAIN_BOLD, fontSize);
        italicFont = Font.createTrueTypeFont(NATIVE_ITALIC_LIGHT, fontSize);
        if(uiid == null) {
            defaultFont = Font.createTrueTypeFont(NATIVE_MAIN_LIGHT,
                fontSize);
        } else {
            Style s = UIManager.getInstance().getComponentStyle(uiid);
            defaultFont = s.getFont();
            boldFont = boldFont.derive(defaultFont.getHeight(),
                Font.STYLE_BOLD);
            italicFont = italicFont.derive(defaultFont.getHeight(),
                Font.STYLE_ITALIC);
        }
        sizeOfSpace = defaultFont.charWidth(' ');
        currentFont = defaultFont;
    }

    public void setAlignment(int align) {
        ((FlowLayout)getLayout()).setAlign(align);
    }

    private void createComponent(String t) {
        if(t.indexOf(' ') > -1) {
            for(String s : StringUtil.tokenize(t, ' ')) {
                createComponent(s);
            }
            return;
        }
        Label l;
        if(currentLink != null) {
            Button b = new Button(t, "Label");
            final String currentLinkValue = currentLink;
            b.addActionListener(e -> listeners.fireActionEvent(
                    new ActionEvent(currentLinkValue)));
            l = b;
        } else {
            l = new Label(t);
        }
        Style s = l.getAllStyles();
        s.setFont(currentFont);
        s.setFgColor(currentColor);
        s.setPaddingUnit(Style.UNIT_TYPE_PIXELS);
        s.setPadding(0, 0, 0, sizeOfSpace);
        s.setMargin(0, 0, 0, 0);
        lastCmp = s;
        add(l);
    }

    public final void setText(String text) {
        this.text = text;
        removeAll();
        try {
            char[] chrs = ("<body>" + text + "</body>").toCharArray();
            new Parser().eventParser(new CharArrayReader(chrs));
        } catch(IOException err) {
            log(err);
        }
    }

    public String getText() {
        return text;
    }

    public void addLinkListener(ActionListener al) {
        listeners.addListener(al);
    }

    public void removeLinkListener(ActionListener al) {
        listeners.removeListener(al);
    }

    class Parser extends XMLParser {
        @Override
        protected void textElement(String text) {
            if(text.length() > 0) {
                if(lastCmp != null && text.startsWith(" ")) {
                    lastCmp.setPadding(0, 0, 0, sizeOfSpace);
                }
                createComponent(text);
                if(!text.endsWith(" ")) {
                    lastCmp.setPadding(0, 0, 0, 0);
                }
            }
        }

        @Override
        protected boolean startTag(String tag) {
            switch(tag.toLowerCase()) {
                case "a":
                    currentColor = 0x4267B2;
                    break;
                case "b":
                    currentFont = boldFont;
                    break;
                case "i":
                    currentFont = italicFont;
                    break;
            }
            return true;
        }

        @Override
        protected void endTag(String tag) {
            currentColor = 0;
            currentLink = null;
            currentFont = defaultFont;
        }

        @Override
        protected void attribute(
                String tag, String attributeName, String value) {
            if(tag.toLowerCase().equals("a") &&
                    attributeName.toLowerCase().equals("href")) {
                currentLink = value;
            }
        }

        @Override
        protected void notifyError(int errorId, String tag,
                String attribute, String value, String description) {
            log("Error during parsing: " + tag);
        }
    }
}
]]>
1,051 TIP: Rich View Revisited 0 0 0
Better Error Logging blog/better-error-logging.html Tue, 16 Apr 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/better-error-logging.html

A common pain point in most GUI frameworks is the hidden stack traces. When we have an app in production we get occasional emails from crash protection which are cryptic and hard to figure out. They usually start with the EDT loop and make no sense.

The reason for that is callSerially(). When we have code that invokes callSerially we essentially lose the previous stack trace. So your stack trace would look roughly like this:

java.lang.RuntimeException:
        at com.mycompany.MyClass.myMethod(MyClass.java:400)
        at com.codename1.ui.Display.edtLoopImpl(Display.java:1166)
        at com.codename1.ui.Display.mainEDTLoop(Display.java:1070)
        at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120)
        at com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)

For most cases you can just fix line 400 of MyClass so it won’t throw an exception but you might be hiding a worse bug. Lets say that line fails because it expects a specific condition to exist in the app and that condition isn’t met. A good example for this would be logged in users. Lets say your app expects the user to be logged in before myMethod is invoked but for some reason he isn’t.

That means the real bug occurred elsewhere probably in the area of code where callSerially() → myClass.myMethod(; was called. Lets say you looked over the entire body of code and you have suspects but can’t tell which part is at fault. Narrowing this down would help…​

That’s where Display.setEnableAsyncStackTraces() comes in. When set to true it creates a "fake" exception for every callSerially if there’s a "real" exception thrown within the callSerially it uses this "fake" one as the cause. That means you will be able to see the cause for a specific bug when this is enabled.

Notice that this API is potentially prohibitive in terms of performance. As such we recommend that people don’t turn this on by default. You can include this as a user configuration or use it in debug builds.

]]>
1,052 Better Error Logging 0 0 0
Zulu Desktop Builds blog/zulu-desktop-builds.html Thu, 11 Apr 2019 00:00:00 +0300 shai https://www.codenameone.com/blog/zulu-desktop-builds.html

Sometimes you pick up a task and know in advance it’s going to be hell to implement it. Sometimes, the hell you were expecting turns out to be WAY worse than you anticipated. This is the case for modern Java desktop deployment. Amazingly, Java was able to take one of the worse deployment processes possible and make it MUCH worse than before.

If you haven’t used the Codename One desktop port I’ll sum it up to you. We use javapackager (formerly javafxpackager) to essentially wrap the jar file as a DMG/pkg on Mac OS and as an EXE/MSI on Windows. This "worked" in the broad sense of the word. It had a lot of issues and we had to create a lot of workarounds.

However, Oracle effectively killed Java 8. This left us in a bind with an out of date VM that we need to maintain. Some Java advocates came out a while back with an open document claiming that "Java is still free"…​

If you think that a 23 page document explaining that something is "free" sounds problematic…​ Well, you have a point.

ZuluFX

Unfortunately we need JavaFX. My opinion of JavaFX hasn’t changed, if anything I think it’s worse off than ever before. But in the Java world there is no other option. We need video, HTML and other capabilities for our simulator and desktop port so we need JavaFX support.

Luckily, Azul releases a packaged OpenJDK distribution called ZuluFX which includes JavaFX within sparing us the need to package it ourselves. Unfortunately, basic things in javapackager just don’t work properly with Zulu. It seems it picks the full JDK instead of the JRE causing it to produce a 200mb hello world application. It doesn’t work at all on Windows and on Mac OS it’s remarkably flaky, it just fails on random.

After struggling with this approach for a couple of weeks we gave up. Using javapackager is no longer a tenable approach so we’re using a manual approach of generating installers using 3rd party tools. Technically this is the same approach used by javapackager internally so this shouldn’t be too different, just a whole lot of more work for us.

New Build Hints

This should land on the build servers tomorrow but this is highly experimental so I would suggest caution for at least a few weeks. It’s very likely compilation will fail for some builds right now as we’re ironing out the kinks. Still if you run into a problem let us know as we might not be aware of it.

You can use the build hints win.desktop-vm=zuluFx8 or mac.desktop-vm=zuluFx8 to hint that you want to use the Zulu VM instead of the default Oracle JRE. Once this becomes stable we might flip the switch and make this the default target moving forward as it gives us more control over the end result.

At the moment we only support unsigned EXE/DMG targets for this. Hopefully we’ll be able to address the full range of supported targets as we migrate to this approach.

]]>
1,053 Zulu Desktop Builds 0 0 0
The Native Version of Build isn't Coming to iOS blog/build-app-not-coming-ios.html Wed, 27 Mar 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/build-app-not-coming-ios.html

When we released Codename One 6.0 we mentioned that Codename One build is going through the approval process on iOS. We didn’t mention that this was a process where Apple repeatedly rejected us and we had to appeal over and over again. 

We wanted to have a native app because they look/feel slightly better. We also wanted native in-app-purchase as an alternative to PayPal. But it seems Apple won’t allow a native app to do basic things that a web app can easily pull off on its platform. 

For iOS we had to hide the fact that builds can exist for other OS’s. So we can’t mention Android support or even show the logo. That’s prohibited by Apples terms. But the sticking point was the ability to install the app you built. A pretty basic feature for an app building service. It works for web apps without a problem, we just launch a link and the app installed. 

It seems that Apple guideline 2.5.2 prohibits 3rd party app installations. This makes a lot of sense on the surface. It’s meant to stop spam applications from constantly pushing you to install additional apps or stop a hacker from leveraging a loophole. But this is a legitimate use that works on the web and it’s crucial for this type of app. In a sense these guidelines make native apps less powerful than their web based equivalents.

After spending ages back and forth with Apples bureaucrats over this requirement it seems that this just won’t happen. But I also had an epiphany of sort…

Web is Sometimes Better

I’m strongly in the "native first" camp as this is our core business: we sell a cross platform native development tool. While it supports targeting web UI’s as well, that’s a secondary function and not its primary value.

However, in this specific case, Apples restrictions make no sense. Amazingly, web provides the same level of functionality as the native app. It also has a couple of other advantages:

  • Fast/instant deployment

  • No restrictions about mentioning other platforms

  • Subscriptions -- Without the "Apple tax"

The disadvantages for our case are:

  • No appstore discoverability - This isn’t a big deal as there are so many apps in the stores today

  • No in-app-subscriptions - This is a benefit and a curse. In app purchase is a "low touch" solution for subscriptions but the cost and restrictions are prohibitive

  • No push -- This is the most painful downside of this, it seems all browsers on iOS don’t support push at this time

Another disadvantage is the seamless login support. We invoke the app with a token from the web in order to login without a password. That’s both secure and convenient as one doesn’t need to type a password on the device.

This is problematic for the web app and as a result we need to show a typical email/password box. That’s not a deal breaker but it’s still a slight annoyance.

Do We Still Need Native?

Not as much as we used to and this gap is eroding fast. There are still annoyances with small things that are trivial to accomplish in native but require multiple steps in the web version. But for the most part they are easy to circumvent. 

One of the nice things about cross platform development is the fact that we don’t rely on Apples whims. We can just release the web version of the app and keep working. That’s a huge advantage. It’s still not perfect but it looks as close as possible to a native app even though it’s technically a web app. So for this particular case this can work.

For the Android version we still have the native app that works just fine. It also looks slightly better than the web version on the same device (mostly due to fonts). It supports push and in-app-purchase so it provides that full app experience.

]]>
1,054 The Native Version of Build isn't Coming to iOS 0 0 0
Introduction to UIFragment blog/introduction-to-uifragment.html Tue, 19 Mar 2019 00:00:00 +0200 steve https://www.codenameone.com/blog/introduction-to-uifragment.html

I recently became frustrated by the amount of work I had to put into recreating a Component hierarchy programmatically. For a test case, I needed to create a Form with a text field in the bottom, and a button just above it on the right.

I ended up with code that looked like:

Form f = new Form("Test Form", new BorderLayout());

Container south = new Container(new BorderLayout());
south.add(BorderLayout.SOUTH, new TextField());
south.add(BorderLayout.NORTH, FlowLayout.encloseRight(new Button("Submit")));

f.add(BorderLayout.SOUTH, south);

f.show();

The result looks something like:

Image 110319 095240.611

This isn’t all that bad, but it feels like it could be made simpler. It isn’t entirely obvious what the finished result will look like by looking at the Java code. This is largely due to the imperative notation. A declarative notation would be easier to read.

Codename One uses XML, a declarative language, as the underlying format for its GUI builder but this isn’t designed to be written or read by a human. It would be nice if I could express this UI using a notation that is both optimally succinct, and easy to read.

So, I created such a notation, and wrapped it in a class named UIFragment.

A New Declarative UI Notation

UIFragment supports both an XML and a JSON notation. Both are compiled down to the same underlying structure, which is based on the XML notation. I.e. If you use the JSON notation, it will just be converted to the XML version under the hood.

Let’s take a look at the above example, using XML:

<border>
  <border constraint="south">
    <$myTextField constraint="south"/>
    <flow constraint="north" align="right">
      <$submitButton/>
    </flow>
  </border>
</border>

This XML can be transformed into a Component using

Component cmp = UIFragment.parseXML(xmlString)
  .set("myTextField", new TextField())
  .set("submitButton", new Button("Submit"))
  .getView();

While this notation is slightly easier to read than it’s pure-java equivalent, it is still quite lengthy. For a UI like this, I’d like a notation that I can easily stick into a single-line string. So let’s take a look at the JSON-ish UI notation.

{south:{north:{flow:[$myTextField], align:right}, south:$submitButton}}

And this can be further shorthened using the single-character layout constraint aliases to:

{s:{n:{flow:[$myTextField], align:right}, s:$submitButton}}

Notice that this JSON is not quite valid JSON. The property keys aren’t quoted, and neither are some of the values. This is OK, as the JSON parser for UIFragment is using non-strict mode, so as to eliminate all extra syntax that would only make our notation more difficult to write.

Using JSON, the entire Java source for the above example becomes:

Form f = new Form("Test Form", new BorderLayout());
f.add(
    BorderLayout.CENTER,
    UIFragment.parseJSON("{s:{n:{flow:[$myTextField], align:right}, s:$submitButton}}")
        .set("myTextField", new TextField())
        .set("submitButton", new Button("Submit"))
        .getView()
);
f.show();

While the total lines of code hasn’t changed, the portion related to constructing the UI is certainly shorter, and it is now easy to understand what the structure will be.

Syntax

The syntax is built around containers. Rather that have a container tag, I opted to use a separate tag for each layout type. This makes the syntax both easier to read and easier to write. Instead of <container layout="flow"> we simply have <flow>.

The supported layouts are shown in the following table.

Table 1. Container notation
Layout XML JSON

TableLayout

<table><tr><td>…​</td>…​</tr>…​</table>

{table:[[],…​]}

BorderLayout

<border>…​</border>

{c:[], n:[], e:[], w:[], s:[], o:[]}

FlowLayout

<flow>…​</flow>

{flow:[], align:center, valign:top}

BoxLayout X

<x>…​</x>

{x:[]}

BoxLayout Y

<y>…​</y>

{y:[]}

GridLayout

<grid>…​</grid>

{grid:[], rows:3, cols:2}

LayeredLayout

<layered>…​</layered>

{layered:[]}

Placeholders

Rather than creating tags for every Component type, I chose to leave it simple. Only Containers can be expressed using the XML/JSON notation. Components should be represented using placeholders, and a corresponding call to set(String,Component) must be made on the UIFragment to bind a Component to the placeholder.

E.g.

UIFragment.parseJSON("{s:$myButton}")
    .set("myButton", new Button("Hello"))

The above example creates a Container with a button placed in its south position. The $myButton placeholder will be replaced by the button that we passed to set(). Notice that the placeholder is prefixed with a $.

The XML equivalent of the above is:

UIFragment.parseXML("<border><$myButton constraint='south'/></border>")
    .set("myButton", new Button("Hello"))

Attributes

There are a small number of attributes which can be added to elements of a UIFragment. These include:

  • uiid - The UIID to set on the element.

  • id - An ID that can be used to obtain a direct reference to the Container via the UIFragment.findById(String) method. This is useful if you need to provide further customizations on the Container in Java that aren’t possible using XML or JSON.

  • class - This works like the HTML class attribute works. It assigns tags to the container that can be used by ComponentSelector to select the resulting component. Multiple tags are separated by spaces.

There are also some attributes that are only applicable to certain container types. E.g. <flow> supports align and valign attributes which accept "left", "right", "center", and "top", "bottom", "center" respectively for values.

<grid> and <table> both support rows and cols attributes which specify the number of rows and columns in their layouts respectively. On <table> these are optional attributes. If they are omitted it will use the actual data in the table to figure out the correct number of rows and columns.

Labels

Labels are a special case which are supported in UIFragments without having to use a placeholder. The XML notation supports a <label> tag, and the JSON notation will treat a string literal as a Label.

Examples

FlowLayout

The following are all equivalent

JSON notation using short array syntax for flow layout.
Container cnt = UIFragment.parseJSON("['Name', $name, $button]")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
JSON notation using verbose Object syntax for flow layout.
Container cnt = UIFragment.parseJSON("{flow:['Name', $name, $button]}")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
XML notation
Container cnt = UIFragment.parseXML("<flow><label>Name</label><$name/><$button/></flow>")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
Pure Java Imperative Code
Container cnt = new Container(new FlowLayout());
cnt.add(new Label("Name"));
cnt.add(new TextField());
cnt.add(new Button("Submit"));

And the resulting UI:

Image 110319 110455.993

BorderLayout

JSON notation using single-char constraints.
Container cnt = UIFragment.parseJSON("{n:'Name', c:$name, s:$button}")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
JSON notation using verbose constraints.
Container cnt = UIFragment.parseJSON("{north:'Name', center:$name, south:$button}")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
XML notation
Container cnt = UIFragment.parseXML("<border><label constraint='north'>Name</label>" +
    "<$name constraint='center'/><$button constraint='south'/></bortder")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
Pure Java Imperative Code
Container cnt = new Container(new BorderLayout());
cnt.add(BorderLayout.NORTH, new Label("Name"));
cnt.add(BorderLayout.CENTER, new TextField());
cnt.add(BorderLayout.SOUTH, new Button("Submit"));

And the result is:

Image 110319 111107.819

Box Layout Y

JSON notation
Container cnt = UIFragment.parseJSON("{y:['Name', $name, $button]}")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
XML notation
Container cnt = UIFragment.parseXML("<y><label>Name</label><$name/><$button/></y>")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
Pure Java Imperative Code
Container cnt = new Container(BoxLayout.y());
cnt.add(new Label("Name"));
cnt.add(new TextField());
cnt.add(new Button("Submit"));

And the result:

Image 110319 111915.662

Box Layout X

JSON notation
Container cnt = UIFragment.parseJSON("{x:['Name', $name, $button]}")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
XML notation
Container cnt = UIFragment.parseXML("<x><label>Name</label><$name/><$button/></x>")
    .set("name", new TextField())
    .set("button", new Button("Submit"))
    .getView();
Pure Java Imperative Code
Container cnt = new Container(BoxLayout.x());
cnt.add(new Label("Name"));
cnt.add(new TextField());
cnt.add(new Button("Submit"));

And the result:

Image 110319 112031.709

GridLayout

JSON notation
Container cnt = UIFragment.parseJSON("{grid:[$settings, $info, $account, $logout], cols:2}")
    .set("settings", new Button(FontImage.MATERIAL_SETTINGS))
    .set("info", new Button(FontImage.MATERIAL_INFO))
    .set("account", new Button(FontImage.MATERIAL_ACCOUNT_CIRCLE))
    .set("logout", new Button(FontImage.MATERIAL_EXIT_TO_APP))
    .getView();
XML notation
Container cnt = UIFragment.parseXML("<grid cols='2'><$settings/><$info/><$account/><$logout/></grid>")
    .set("settings", new Button(FontImage.MATERIAL_SETTINGS))
    .set("info", new Button(FontImage.MATERIAL_INFO))
    .set("account", new Button(FontImage.MATERIAL_ACCOUNT_CIRCLE))
    .set("logout", new Button(FontImage.MATERIAL_EXIT_TO_APP))
    .getView();

The result is:

Image 110319 113512.657

TableLayout

JSON notation
Container cnt = UIFragment.parseJSON("{table:[['Name', $name], ['Age', $age], ['Active', $active]]}")
    .set("name", new TextField())
    .set("age", new TextField())
    .set("active", new CheckBox())
    .getView();
XML notation
Container cnt = UIFragment.parseXML("<table><tr><td><label>Name</label></td><td><$name/></td></tr>"+
    "<tr><td><label>Age</label></td><td><$age/></td></tr>"+
    "<tr><td><label>Active</label></td><td><$active/></td></tr></table>")
    .set("name", new TextField())
    .set("age", new TextField())
    .set("active", new CheckBox())
    .getView();

The result is:

Image 110319 114149.837

Reusing a Fragment

Suppose you have a UIFragment that defines a single row of a list. When this list grows to hundreds or thousands of rows, it may become expensive to re-parse the JSON or XML for each row fragment. Luckily, you don’t need to do that. You can create the fragment once, and then use it to generate a different view for each row.

Consider this example using the Contacts API.

Contact[] contacts = Display.getInstance().getAllContacts(true, true, true, true, true, true);
UIFragment fragment = UIFragment.parseJSON("{w:$icon, c:{n:$name, s:[$phone, '/', $email]}}");
Container cnt = new Container(BoxLayout.y());
for (Contact contact : contacts) {
    Component row = fragment.set("icon", (contact.getPhoto() == null) ?
            new Button(FontImage.MATERIAL_ADD_A_PHOTO) : new Button(contact.getPhoto().scaledHeight(20)))
            .set("name", new Label(contact.getDisplayName()))
            .set("phone", new Label(contact.getPrimaryPhoneNumber()))
            .set("email", new Label(contact.getPrimaryEmail()))
            .getView();
    $(row).selectAllStyles().setBorder(Border.createUnderlineBorder(1))
            .setBgColor(0xffffff)
            .setBgTransparency(0xff);
    cnt.add(row);
}

f.add(BorderLayout.CENTER, cnt);

In this example, we parse the JSON only once, when UIFragment.parseJSON() is called. Then we iterate through each contact, setting the placeholders for each row. This works because once getView() has been called, the next subsequent call to set() will result in the fragment’s `view' getting reset. This works similar to the way a prepared database query works in JDBC. The query is compiled (prepared) once, and changing the placeholders results in a different effective query.

And the result:

Image 110319 120333.100

Accessing Components in the Fragment

The JSON and XML notations offer only a limited amount of customization options for elements. The uiid attribute allows you to set the UIID for a component, but that’s about it. If you want to customize it further, you’ll need to obtain a reference to the actual component, and customize it directly using its Java API. There are two ways to obtain references to the components in a fragment.

  1. Add the id attribute to the component, then use the findById() method of the fragment to access it.

  2. Add the class attribute to the component to add "tags" that can be selected by the ComponentSelector class.

Example using findById()
UIFragment fragment = UIFragment.parseJSON("{c:{flow:'Hello World', id:'MyContainer'}}");
Container myContainer = (Container)fragment.findById("MyContainer");
$(myContainer).setPaddingMillimeters(10);
Container cnt = fragment.getView();

In the above example we set the 'id' attribute on the nested FlowLayout Container, so that we can fetch it via findById(). Then we set the padding on that container using the Java API. The result:

Image 110319 123838.820
Example using class Attribute and ComponentSelector
Container view = UIFragment.parseJSON("{c:{flow:'Hello World', class:'tag1 tag2'}, s:{flow:'South', class:'tag2'}")
    .getView();
$(".tag1", view).selectAllStyles().setBgColor(0xff0000).setBgTransparency(0xff);
$(".tag2", view).selectAllStyles().setBorder(Border.createLineBorder(1));

This example demonstrates the use of the class attribute for setting tags that can be used by ComponentSelector for selecting nested components in the fragment. We have two nested containers. One in the center with tags "tag1" and "tag2"; and a second component in the south with tag "tag2" only.

We’re able to select on "tag1" to set the background color of the first component to red. Then we selet on "tag2" (which selects both of the components), to set the border of both components to use a line border.

The result:

Image 110319 124528.887

Summary

UIFragment provides simple way to user interfaces using a declarative syntax. It supports both an XML and a JSON syntax. The JSON notation is almost always more succinct and easier to read than the XML equivalent, and both provide advantages over directly defining the UI in Java code. You can embed placeholders inside your fragment’s JSON/XML and have them replaced by custom components when the fragment is compiled. Additionally, you can access nested components within fragments for further customization by tagging them either with an "id" or a "class" attribute. Finally you can reuse the same fragment to generate entire sets of components by setting placeholders with different values.

]]>
1,055 Introduction to UIFragment 0 0 0
Video Capture Constraints blog/video-capture-constraints.html Wed, 13 Mar 2019 00:00:00 +0200 steve https://www.codenameone.com/blog/video-capture-constraints.html

The new video capture constraints API allows you to specify "constraints" when capturing videos. Constraints include:

  1. Video Quality (High or Low)

  2. Maximum duration

  3. Maximum file size

  4. Video Resolution (i.e. width and height).

Support for these constraints vary by platform and device, but the API allows you to check if your constraints are supported at runtime. Essentially, you set your "preferred" constraints, and the API will give you its best attempt at meeting those constraints. This is similar to setting a visual Component’s preferred width and height. The layout manager takes these preferred dimensions under advisement, but ultimately sets the size on its own.

Example 1: Capturing a Low-Quality 5-Second Clip

Suppose we want to allow the user to capture a short (5 second) clip, in a low resolution, appropriate for sharing on a social media platform. We create our VideoCaptureConstraints object as follows:

VideoCaptureConstraints cnst = new VideoCaptureConstraint()
    .preferredQuality(VideoCaptureConstraints.QUALITY_LOW)
    .preferredMaxLength(5);

This constraint can then be passed to Capture.captureVideo() to obtain the captured file.

String videoPath = Capture.captureVideo(cnst);

Not all platforms support all constraints

So how do we know if our constraints will be obeyed? If the platform doesn’t support the max length, constraint, we may want to do something different. We can find out if a constraint is supported by simply asking out constraint object.

E.g.

if (cnst.isMaxLengthSupported()) {
    // The max length constraint that we specified is supported on this platform.
} else{
    // The max length constraint is NOT supported.
    // Check the effective max length constraint value to see if it may be partially
    // supported
    int effectiveMaxLength = cnst.getMaxLength();
    if (effectiveMaxLength == 0) {
        // Max length is not supported at all... the user will be able
        // to capture a video without duration restrictions
    } else {
        // Max length was set to some different value than we set in our
        // preferredMaxLength, but the platform is at least trying to accommodate us.
    }
}

You can probe a constraint to see whether the entire constraint is supported (i.e will be obeyed), or whether any particular aspect of it will be supported using the following methods:

  • isSupported() - True if all preferred constraints are supported.

  • isQualitySupported() - True if the preferred quality setting is supported.

  • isMaxLengthSupported() - True if the max length setting is supported.

  • isMaxFileSizeSupported() - True if the max file size setting is supported.

  • isSizeSupported() - True if the specified preferred width and height constraints are supported.

Example 2: Specifying Explicit Width and Height

Suppose we want to capture a video with resolution 320x240. We would begin with this constraint:

VideoCaptureConstraints cnst = new VideoCaptureConstraints()
    .preferredWidth(320)
    .preferredHeight(240);

Explicit width and height constraints currently aren’t well supported across platforms. Android doesn’t support them at all. iOS supports only 3 specific sizes. Javascript supports it when running on a desktop browser or on Android - but not on iOS. Etc..

So let’s find out if this constraint will be obeyed.

if (cnst.isSizeSupported()) {
   // Yay! This platform supports our constraint, so the captured video will
   // be exactly 320x240.
} else {
   // Not supported... let's see if the platform will at least try to accommodate us
   int effectiveWidth = cnst.getWidth();
   int effectiveHeight = cnst.getHeight();
   int quality = cnst.getQuality();
   if (effectiveWidth == 0 && effectiveHeight == 0) {
       // This platform has no control over width and height
       // In many cases it will try to at least set the quality approximate
       if (quality != 0) {
          //  The platform set the quality for us to try to comply.
          // Since 320x240 is pretty small, the quality would probably
          // be set to QUALITY_LOW
       }
   } else {
       // The platform couldn't capture at 320x240, but it has provided an
       // alternate size that is as close to that as possible.
   }
}

Constraint Support By Platform

Platform Size Quality Max Length Max File Size

Javascript (iOS)

Yes

Yes

Yes

No

iOS

Limited

Yes

Yes

No

Android

No*

Yes

Yes

Yes

Simulator/JavaSE

No

No

No

No

UWP

No*

Yes

Yes

No

Javascript (Desktop)

Yes

Yes

Yes

No

Javascript (Android)

Yes

Yes

Yes

No

* If size is specified, the platform will attempt to translate to the appropriate quality constraint.

]]>
1,056 Video Capture Constraints 0 0 0
TIP: Adapting to Tablets blog/tip-adapting-to-tablets.html Tue, 5 Mar 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-adapting-to-tablets.html

A while back someone asked on stackoverflow how to adapt a Codename One app to tablets. I provided quite a few references in the answer and following discussion but I think a better approach is to explain what we did with the recent Codename One Build app because that’s what I’ve been doing in all recent apps I worked on.

I call this approach the "phone first" approach for universal app development. It starts by forgetting about the tablet and focusing on building a good looking phone app. In this app I usually subclass Form for all the classes which instantly creates an app that’s very suitable for phones.

When this is done I give some thought to how I would like the app to work as a tablet app. In general I want the app to have a permanent side menu and one Form where the content is replaced.

To accomplish this I change all the existing subclasses of Form so they will derive from my private class BaseForm which is basically something like this:

public class BaseForm extends Container {
    private String title;
    private static Form tabletForm;

    public BaseForm(String title, Layout l) {
        super(l);
        this.title = title;
        if(!(l instanceof BorderLayout)) {
            setScrollableY(true);
        }
    }

// rest of code
}

Since there is only one form in the tablet I can keep it as a static global, that’s obvious.

But why is the title a variable?

Where are the rest of the methods we expect in Form?

Simple, we create every method thats missing and make it work in such a way that makes sense for our applications tablet design. E.g. this is show() which is one of the bigger methods here:

public void show() {
    if(isTablet()) {
        if(tabletForm == null) { (1)
            tabletForm = new Form(title, new BorderLayout()); (2)
            getUnselectedStyle().setBgTransparency(255); (3)
            UIUtil.createSideMenu(tabletForm);
            tabletForm.add(CENTER, this);
            tabletForm.show();
        } else {
            replaceTabletForm(true); (4)
        }
    } else {
        if(getParent() != null) { (5)
            getComponentForm().show();
        } else {
            Form f = new Form(title, new BorderLayout());
            UIUtil.createSideMenu(f);
            f.add(CENTER, this); (6)
            f.show();
        }
    }
}
private void replaceTabletForm(boolean dir) {
    getUnselectedStyle().setBgTransparency(255);
    Container c = tabletForm.getContentPane();
    c.replaceAndWait(c.getComponentAt(0), this, CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL, dir, 300));
    tabletForm.setTitle(title); (7)
}
1 If this is the first form shown we create a new Form for the case of a tablet
2 The BaseForm is in the center of a border layout which means it will take the available space, we can use more creative layouts e.g. with 3 panes
3 By default Container is transparent but this looks bad in replace animation so for tablets we make the containers opaque
4 If this isn’t the first form we just use the replace method below
5 For a phone we wrap a BaseForm in a Form so they are effectively identical, we can reuse the Form instances
6 This code is almost identical to the code in the tablet mode but happens multiple times
7 Since we reuse the same form in the tablet mode we need to update the title value

Once we do this there would be compilation errors for various methods of Form that we relied on. These are mostly easy to fix as they just mean implementing the logic for every Form method you need e.g.:

public void showBack() {
    if(isTablet()) {
        replaceTabletForm(false);
    } else {
        getComponentForm().showBack();
    }
}

Generalization via API

I tried to generalize this process through a standardized API multiple times in the past and failed. It’s easy to solve the simple use cases but converting a "best practice" into an API is more challenging.

If you have thoughts or ideas on how this approach can be adapted to create a more versatile API I’m open to suggestions.

]]>
1,057 TIP: Adapting to Tablets 0 0 0
Codename One 6.0 "Chat" is now Live blog/codename-one-6-0-chat-live.html Wed, 27 Feb 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/codename-one-6-0-chat-live.html

We are thrilled to announce the release of Codename One 6.0 - Chat. Codename One is an open source "Write Once Run Anywhere" mobile platform for Java and Kotlin developers!
With this release we introduced Codename One Build which is one of the biggest overhauls to the Codename One workflow since its inception. We also refined and updated many underlying technologies e.g. the xcode 10.1 migration, WKWebView support, push replies, badges on Android and much more.

You can check out the details below for the full review but first if you are new to Codename One here’s a short primer. Codename One is the only platform that:

  • Has Write Once Run Anywhere with no special hardware requirements and 100% code reuse

  • Compiles Java or Kotlin into native code for iOS, UWP (Universal Windows Platform), Android & even JavaScript

  • Is Open Source & Free for commercial use with an enterprise grade commercial support

  • Is Easy to use with 100% portable Drag & Drop GUI builder

  • Has Full access to underlying native OS capabilities using the native OS programming language (e.g. Objective-C) without compromising portability

  • Has full control over every pixel on the screen! Just override paint and draw or use a glass pane to draw anywhere…​

  • Lets you use native widgets (views) and mix them with Codename One components within the same hierarchy (heavyweight/lightweight mixing)

  • Supports seamless Continuous Integration out of the box

To learn more about Codename One check out the about page you can download it for free right now.

Version 6.0 is nicknamed Chat because of the WhatsApp Clone application that was developed with it for the online course in the Codename One Academy.

Highlights of this Release

The top 5 features of this release are covered in this short video, check out further details below…​

  • Codename One Build — we can now monitor builds from Android and iOS. The app is also available for every device through web PWA. It works with push notification and is built with Codename One!

Currently the iOS version is still in beta due to the tedious appstore approval process
  • xcode 10.1 Migration — builds on the Codename One cloud implicitly use xcode 10.1. We migrated from xcode 9.2 to satisfy Apples requirements, this has been seamless for the most part

  • VM Changes — we now support java.util.Objects and some additional methods from Class

  • New Switch API — Switch replaces the old OnOffSwitch API which is pretty old by now

  • Reply Push Notifications — the final piece of RFE 2208 Rich Push Notifications is now implemented. You can now prompt a user for a reply via a push message

  • Support for Badges on Android — we can now mark an Android icon with a numeric badge

  • Material Design Infinite Progress — InfiniteProgress now has a material design mode that includes the custom circle animation we see in material design

  • WKWebView Support — Apple includes two implementations of a "WebView". We now support both

  • CSS Improvements — underline border is now supported natively. Round rectangle is also supported natively and lets you activate the angle only on specific corners as per this RFE

  • Picker Improvements — Picker now lets you define start/end date

  • FontImage rotateAnimation — FontImage lets you animate an icon so it rotates infinitely effectively making every component into an InfiniteProgress

  • Added Ownership to Component Hierarchy — ownership allows us to create a relationship between components other than ComponentContainer

  • Added Animation Safe Revalidate — revalidte() is a powerful tool but if it’s invoked when an animation is in progress it might produce unpredictable behavior. This method solves that problem

  • Button Lists — List is discouraged but we still want lists that use a model to represent buttons, radio buttons and checkboxes button lists can fit in that niche

  • XML Mapping in Properties — this is still an experimental feature but XML parsing/generating is now supported for PropertyBusinessObject

  • PWA Install Prompt — a new API lets us install PWA’s directly

  • New Full Screen API — the Desktop and JavaScript targets allow running the app in full screen mode by leveraging this new API

  • Facebook SDK Updated — we updated the Facebook SDK to use the latest version

There are many other features both big and small. Check out our blog and the github project history.

Onwards to 7.0 - Video

We took a lot of time for 6.0 but I’m not sure if that’s enough. We might take longer to deliver 7.0. Currently the timeline is unchanged but we’ll have to see.

We will have a Netflix clone tutorial in the Codename One Academy. Hence the moniker of the next release.

We Need your Help

If you think we are doing a good job and appreciate our help please help us by:

Thanks for reading this far and if you have any thoughts/suggestions of any kind please let us know!

]]>
1,058 Codename One 6.0 "Chat" is now Live 0 0 0
VM Enhancements, Full Screen and XML blog/vm-enhancments-full-screen-xml.html Thu, 21 Feb 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/vm-enhancments-full-screen-xml.html

We are in code freeze…​ As part of the release process I’m gathering the changes we implemented over the past few months. Quite a few didn’t get a blog post during this time. So here is a list of the important things we didn’t document.

VM Changes

We did two big things for ParparVM (our iOS VM) which allowed us to expose this functionality in other platforms as well. First is support for most of the small methods in Class which can be helpful when we write some generic code. Specifically the methods: isEnum(), isSynthetic(), isInterface(), isAnonymousClass(), isPrimitive() and isAnnotation().

We can’t guarantee that these will work perfectly for all cases as there are some complexities. E.g. our process for supporting Java 8 might trigger true for isAnonymousClass() due to a lambda. But this is a start.

Another important piece is java.util.Objects. Technically we implemented this class via com.codename1.compat.java.util.Objects but if you use the java.util.Objects it will work just fine. The processor will translate the calls to the Codename One equivalents.

Objects includes some common useful utility methods. Hopefully we’ll add a few more classes like that as needed.

XML Properties

We supported JSON in properties from day one. XML is a bit harded but not by much. We added highly experimental support for XML into properties which can be useful when working with XML data from the server or storage.

This feature is experimental and the API might change/break

PropertyIndex includes several new API’s to enable that. First we have:

public Element asElement();

This returns the PropertyBusinessObject as an XML Element object which is the parsed form of XML in Codename One. You can use XMLWriter to convert this to a String or use XML processing code to work with the Element.

The toXML() method in PropertyIndex takes the next step of converting that XML Element to a String and returning it:

public String toXML();

A property in the object can appear as the text element e.g. <tag>Text Element</tag>. You can explicitly define which by using these methods in PropertyIndex:

public void setXmlTextElement(PropertyBase p, boolean t);
public boolean isXmlTextElement(PropertyBase p);

Finally, we can parse XML by using:

public void fromXml(Element e);

This will walk the parsed XML and convert it to the object we’re using.

Full Screen

The Desktop and JavaScript ports support running in full screen mode. This is useful for games and media applications. It’s also useful for PoS and multiple other purposes. Historically, we had a build hint that allowed you to set full screen mode for the desktop port. That isn’t ideal as full screen might be something you wish to toggle dynamically within the app. It might require user permission too as is the case for JavaScript.

We now have a new full screen API. The following code assumes you have an import of CN specifically import static com.codename1.ui.CN.*;:

if(isFullScreenSupported() && !isInFullScreenMode()) {
    requestFullScreen();
}

You can also use exitFullScreen() to perform the inverse operation.

]]>
1,059 VM Enhancements, Full Screen and XML 0 0 0
Codefreeze for Chat (Codename One 6.0) blog/codefreeze-for-chat.html Wed, 20 Feb 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/codefreeze-for-chat.html

We’re entering code freeze later tonight which means no further commits will be made. After the code freeze only reviewed commits can be cherry picked. Only critical bugs will be fixed at that point.
We will push out a new plugin update and tools tomorrow morning. They will be labeled 6.0 and serve as release candidates. If there are issues we’ll push out further updates during the week.

Once the release is made we’ll skip the next Friday release as is our practice with the first Friday after a release.

We closed 96 issues as part of this release so far. Some things proved problematic and were postponed to version 7.0 hopefully we’ll catch up to most of them by then.

]]>
1,060 Codefreeze for Chat (Codename One 6.0) 0 0 0
Preparing for Codename One 6.0 - Chat blog/preparing-for-chat.html Wed, 13 Feb 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/preparing-for-chat.html

Codename One 6.0 AKA Chat will launch on February 27th. We’re pushing this release a week back to finish the open issues. We will enter code freeze on the 20th exactly one week from today.
After the code freeze only reviewed commits can be cherry picked. Only critical bugs will be fixed at that point.

Chat

We named the releases of Codename One based on the signature app we built on top of that given release. 5.0 was named social and 4.0 was named Taxi representing the Facebook/Uber clones respectively. Version 6.0 is named Chat as we built a WhatsApp Clone during the release cycle.

For Version 7.0 we plan to build a Netflix clone. The planned name for version 7.0 is "Video".

]]>
1,061 Preparing for Codename One 6.0 - Chat 0 0 0
File Chooser on Xcode 10.1 blog/file-chooser-xcode-10.html Tue, 12 Feb 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/file-chooser-xcode-10.html

The recent migration to xcode 10.1 broke builds for apps using the file chooser API. In order to use that API we need to make changes to the provisioning profile to include iCloud support. With the new version you must have a container associated with iCloud for this to work.

To fix this follow these steps:

iCloud Settings
Figure 1. iCloud Settings
  • Create a new iCloud container and give it a unique name/package

  • Go back to the icloud settings edit mode and select the new container in the list of containers as such

Select the Container
Figure 2. Select the Container
  • Next regenerate and download the provisioning profiles, replace the ones in your app with the new provisioning profiles

Notice that this will only work with the default xcode 10.1 mode. It seems that the application loader now requires this as Apple no longer accepts binaries with xcode 9.2 that use the older approach (without containers).

]]>
1,062 File Chooser on Xcode 10.1 0 0 0
WhatsApp Clone GA blog/whatsapp-clone-ga.html Thu, 7 Feb 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/whatsapp-clone-ga.html

The WhatsApp clone app is finally up in the online course. With that finally out of the way we can turn our attention to Codename One 6.0 which is due dangerously soon. We might even slightly postpone the release so we can finish crucial RFE’s/issues.

Getting the WhatsApp clone out has been an ordeal, not so much because of complexity (the app is much simpler than Uber or Facebook)…​ It was due to the amount of concurrent things I had to deal with during this time.

There are still plenty of issues for version 6.0 and a lot of them just won’t make it in time so this is the point where we need to step it up for the 6.0 release. Right now I’m considering pushing it back one week to February 26th instead of the 19th. This should give us a bit more time to get more of these issues/features in.

]]>
1,063 WhatsApp Clone GA 0 0 0
Rich Push Notifications Improved blog/rich-push-notification-improved.html Tue, 5 Feb 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/rich-push-notification-improved.html

Last week Steve committed the final piece of the rich push notification support RFE. This commit introduces support for replies in push messages. This came too late for the whatsapp clone but if you want to build an app of this type you would need this feature.

The app main class should implement PushActionProvider. This defines a method that returns a set of categories. E.g.

public PushActionCategory[] getPushActionCategories() {
    return new PushActionCategory[]{
        new PushActionCategory("fo", new PushAction[]{
            new PushAction("yes", "Yes"),
            new PushAction("no", "No"),
            new PushAction("maybe", "Maybe", null, "Enter reason", "Reply")
        })

    };
}

Then, when sending a push notification, you can specify the "category" of the message. If the category corresponds with a defined category in your getPushActionCategories() method, then the user will be presented with a set of buttons corresponding to the PushActions in that category.

In the above example, we would send a push type 99 and a body of

<push type="0" body="Hello" category="fo"/>

This would trigger the "fo" category that we defined, which has 3 actions: Yes, No, and Maybe. And the "Maybe" action will provide a text input because of the extra parameters provided:

new PushAction("maybe", "Maybe", null, "Enter reason", "Reply")

The last 2 parameters are the "hint" text and the reply button label. On android, the notification will look like this.

Push Reply
Figure 1. Push Reply

If you click on "Maybe" (with Android API level 27 or higher which is the default), then you’ll get a text field to enter a reply directly.

You can retrieve both which action was pressed, and what the user text input was using the PushContent class.

An example push callback method to retrieve this data:

public void push(String value) {
    PushContent data = PushContent.get();
    if (data != null) {
        Log.p("Image URL: "+data.getImageUrl());
        Log.p("Category: "+data.getCategory());
        Log.p("Action: "+data.getActionId());
        Log.p("Text Response: "+data.getTextResponse());
    } else {
        Log.p("PushContent is null");
    }
    Log.p("Push "+value);
    Display.getInstance().callSerially(()->{
        Dialog.show("Push received", value, "OK", null);
    });
}
]]>
1,064 Rich Push Notifications Improved 0 0 0
Native Controls blog/native-controls.html Tue, 29 Jan 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/native-controls.html

We supported native widgets within the native interface feature since the first public beta. However, this requires native coding and isn’t always trivial. Normally you don’t really need to do that.

E.g. for text fields we implicitly convert the field to a native field as necessary.

However, if you wish to use the password manager of the device this won’t work. To solve this Steve introduced a new cn1lib: cn1-native-controls. This library wraps some native widgets and lets us use some features that might be tricky without them. Good use cases for this include the password managers which need a native text widget constantly in that sport. Another use case would be iOS’s SMS interception text field that can automatically intercept the next incoming SMS and set it to a special native text field.

There are currently two widgets in this library:

  • NSelect — which acts like a combo box

  • NTextField — which is practically a drop-in replacement for TextField

You can use the NTextField like this:

hi.add("Text fields");
hi.add("Username:");
NTextField tf1 = new NTextField(TextField.USERNAME);
System.out.println("Setting font to main light 15mm");
tf1.getAllStyles().setFont(Font.createTrueTypeFont(Font.NATIVE_MAIN_LIGHT, 15f));
System.out.println("Finished setting font");
tf1.getAllStyles().setFgColor(0x003300);
tf1.getAllStyles().setBgTransparency(255);
tf1.getAllStyles().setBgColor(0xcccccc);
tf1.getAllStyles().setAlignment(CENTER);
hi.add(tf1);
hi.add("Password:");
NTextField tf2 = new NTextField(TextField.PASSWORD);
hi.add(tf2);
hi.add("Email:");
NTextField emailField = new NTextField(TextField.EMAILADDR);
hi.add(emailField);

tf1.addActionListener(e->{
    //tf2.setText(tf1.getText());
});
tf1.addChangeListener(e->{
   result.setText(tf1.getText());
   hi.revalidateWithAnimationSafety();
});
tf2.addActionListener(e->{
    Log.p("Action listener fired on password field");
    result.setText(tf2.getText());
    hi.revalidateWithAnimationSafety();
});
tf2.addDoneListener(e->{
    Log.p("Done was clicked!!!");
});
]]>
1,065 Native Controls 0 0 0
WhatsApp Clone blog/whatsapp-clone.html Thu, 24 Jan 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/whatsapp-clone.html

I’ve been pretty busy this new year with a lot of unpredictable detours. As such I completely missed the deadline of releasing the whatsapp clone application I promissed for the end of year. I’m now in the process of uploading modules into the online course for the WhatsApp clone.

Unlike previous clones I plan to make this far simpler as a lot of the work was already covered in the Facebook Clone module. This mostly focuses on features that weren’t covered there and fixes some things e.g. SMS activation is handled in the server side.

There is still a lot of work to do for Codename One 6 which is slated for February so I hope I’ll be able to push out the WhatsAppClone ASAP.

Reminder xcode 10.1 Goes Live

As a reminder, xcode 10.1 will be the default with builds starting tomorrow. If you experience sudden regressions please check out the previous blog post!

]]>
1,066 WhatsApp Clone 0 0 0
Xcode 10.1 Migration Phase 2 blog/xcode-10-1-migration-phase-2.html Mon, 21 Jan 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/xcode-10-1-migration-phase-2.html

As I mentioned recently, we’re migrating to xcode 10.1. This weekend we entered phase 2 of the migration which allows you to test your builds with xcode 10.1. To do this just set the build hint: ios.xcode_version=10.1.

If you run into issues with xcode 10.1 builds let us know ASAP. This is very urgent as phased 3 will kick in this coming Friday. In phase 3 we will flip 10.1 as the default which means that builds sent on Friday will implicitly target xcode 10.1.

If this produces an unforseen regression you can explicitly force xcode 9.2 for compatibility by using the build hint: ios.xcode_version=9.2.

This means you can use the capabilities of the latest xcode/iOS SDK within your native code starting with this update.

]]>
1,067 Xcode 10.1 Migration Phase 2 0 0 0
Xcode 10.1 Migration blog/xcode-10-1-migration.html Wed, 9 Jan 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/xcode-10-1-migration.html

Over the past month Apple started sending out warnings that they will no longer accept apps built with older SDK’s starting this March. To preempt that we will update our servers to use xcode 10.1 over the next couple of weeks. This change should be seamless for the most part but some odd behaviors or bugs usually rise as a result of such migrations.

To test if a sudden regression is caused by this migration you can explicitly force xcode 9.2 for compatibility by using the build hint: ios.xcode_version=9.2.

We also considered updating the Android target SDK from 27 to 28 but since that would be a requirement only around August it might make better sense to postpone it to the 7.0 release cycle.

]]>
1,068 Xcode 10.1 Migration 0 0 0
TIP: Global Settings blog/tip-global-settings.html Tue, 8 Jan 2019 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-global-settings.html

A somewhat hidden feature of Codename One Settings is the ability to define global settings. Global settings are useful when you have multiple projects and would like to use common defaults. E.g. we have standard certificate definitions for iOS/Android that allow us to just send a device build on a new project without configuring anything.

For iOS this can only work for debug builds and won’t allow features such as push etc. It requires some work…​
The global settings toggle button
Figure 1. The global settings toggle button

A good use for this is in the Android settings where you can define one certificate for use with all your apps. For iOS you can define the same certificate for all applications as they don’t change between apps. However, you would need a different provisioning profile so for distribution you will need to re-run the wizard and generate only the provisioning.

There is a trick for iOS where you can create a "generic" provisioning profile to the * package/identifier. This can be done via the web UI on apples developer site here: https://developer.apple.com/account/ios/profile/

You can generate an "AllApps" provisioning profile and save it to your disk, then point the debug provisioning to that. Once this is defined, new projects will build on iOS without a single change.

]]>
1,069 TIP: Global Settings 0 0 0
See You in 2019 blog/see-you-2019.html Tue, 18 Dec 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/see-you-2019.html

Happy holidays, Merry Christmas, happy new year to all. All of us here at Codename One hope you have a lovely vacation if you are taking one. Since half of our readership is from countries that celebrate these holidays it seems like a good time to take a short blogging vacation as well.

During this time we won’t blog and won’t make releases unless there are crucial issues. We’re still here for support but some things will be pushed into January.

]]>
1,070 See You in 2019 0 0 0
TIP: Undo Delete blog/tip-undo-delete.html Tue, 11 Dec 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-undo-delete.html

One of my pet peeves is the "Are you sure?" dialog. I’ve used it a lot myself because it’s the "easy way out", but when possible I try to avoid it. This is especially important in mobile where constant prompts really slow down the workflow.

The trick that lets us avoid the "Are you sure?" dialog is the undo option. Once you can undo an operation you can move fast and let users reconsider later. With file deletion this is a bit harder. Most OS’s provide a trash can abstraction. This doesn’t exist in mobile devices where apps are effectively segregated from one another. However, implementing our own trash can is pretty trivial.

These are methods I use in one of our util classes, they aren’t public API’s because they are too high level for the API. But they might be useful for you. First we need copy and move operations. Notice, I didn’t use file system rename since different file systems might be isolated on the device and might not allow a rename:

public static void copyFile(String sourceFile, String destFile) throws IOException {
    try (InputStream source = openFileInputStream(sourceFile);
        OutputStream dest = openFileOutputStream(destFile)) {
        Util.copy(source, dest);
    }
}

public static void moveFile(String sourceFile, String destFile) throws IOException {
    copyFile(sourceFile, destFile);
    delete(sourceFile);
}

The next stage is a trash abstraction. We add a Trash directory which we create dynamically. We then move the file to a dummy name in the trash to delete it. Empty trash might be something you can expose to the user or just invoke on startup/exit.

public static String moveToTrash(String file) {
    try {
        String trash = getAppHomePath() + "Trash/";
        mkdir(trash);
        String trashName = trash + System.currentTimeMillis();
        moveFile(file, trashName);
        return trashName;
    } catch(IOException err) {
        Log.e(err);
        return null;
    }
}

public static void emptyTrash() {
    try {
        String trash = getAppHomePath() + "Trash/";
        mkdir(trash);
        String[] arr = listFiles(trash);
        for(String f : arr) {
            delete(trash + f);
        }
    } catch(IOException err) {
        Log.e(err);
    }
}

Last but not least we have the undoable delete. The autoFlash option allows us to automatically delete the file after 10 seconds if it wasn’t restored. This is accomplished by launching a thread to delete the file later. We also send a callback in the case of an undelete so a user can update the UI.

public static void undoableDelete(String file, boolean autoFlush, Runnable onUndelete) {
    String trashFile = moveToTrash(file);
    ToastBar.showMessage("Deleted " +
        file.substring(file.lastIndexOf('/') + 1) +
        "\nTouch to undo", FontImage.MATERIAL_UNDO, e -> {
        try {
            moveFile(trashFile, file);
            onUndelete.run();
        } catch(IOException err) {
            Log.e(err);
            ToastBar.showErrorMessage("An error occurred on file restore");
        }
    });
    if(autoFlush) {
        startThread(() -> {
            Util.sleep(10000);
            if(existsInFileSystem(trashFile)) {
                delete(trashFile);
            }
        }, "Delete Trash");
    }
}

I used a toastbar to show the undo message, you might want to provide more ways to undo a delete depending on the case.

]]>
1,071 TIP: Undo Delete 0 0 0
Button Lists blog/button-lists.html Wed, 5 Dec 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/button-lists.html

I wrote in the past about the problems in the List class, so I won’t rehash them here. However, the ideas behind list are still valuable. One such idea is the list model which allows us to separate the state from the view. Unfortunately the renderer architecture makes this hard to implement for most developers and limits the flexibility of the API.

To leverage this idea with the easier to use layout/container hierarchy we introduced ButtonList and its subclasses: CheckBoxList, RadioButtonList & SwitchList.

We also added a new MultipleSelectionListModel which extends the ListModel with multi-selection capabilities. This allows us to use a set of buttons as we would use a list and also generate them from data more easily.

In the code below we show two lists that use the same model. Changes to the checkboxes reflect instantly in the switch and vice versa:

Form hi = new Form("Button Lists", new BorderLayout());
SwitchList switchList = new SwitchList(new DefaultListModel("Red", "Green", "Blue", "Indigo"));
switchList.addActionListener(e->{
    Log.p("Selected indices: "+Arrays.toString(switchList.getMultiListModel().getSelectedIndices()));
}); (1)
switchList.setScrollableY(true);
Button clearSelections = new Button("Clear");
clearSelections.addActionListener(e ->
    switchList.getMultiListModel().setSelectedIndices());

Button addOption = new Button("Add Option");
addOption.addActionListener(e -> { (2)
    callSerially(()->{
        TextField val = new TextField();
        Command res = Dialog.show("Enter label", val, new Command("OK"));
        switchList.getMultiListModel().addItem(val.getText());

    });
});
RadioButtonList layoutSelector = new RadioButtonList(new DefaultListModel("Flow", "X", "Y", "2-Col Table", "3-Col Table", "2 Col Grid", "3 Col Grid"));
layoutSelector.addActionListener(e->{ (3)
    boolean yScroll = true;
    switch (layoutSelector.getModel().getSelectedIndex()) {
        case 0:
            switchList.setLayout(new FlowLayout());
            break;
        case 1:
            switchList.setLayout(BoxLayout.x());
            yScroll = false;
            break;
        case 2:
            switchList.setLayout(BoxLayout.y());
            break;
        case 3:
            switchList.setLayout(new TableLayout(switchList.getComponentCount()/2+1, 2));
            break;
        case 4:
            switchList.setLayout(new TableLayout(switchList.getComponentCount()/3+1, 3));
            break;
        case 5:
            switchList.setLayout(new GridLayout(2));
            break;
        case 6:
            switchList.setLayout(new GridLayout(3));
    }
    switchList.setScrollableX(!yScroll);
    switchList.setScrollableY(yScroll);
    switchList.animateLayout(300);
});
CheckBoxList checkBoxList = new CheckBoxList(switchList.getMultiListModel()); (4)
checkBoxList.addActionListener(e->
    System.out.println("CheckBox actionEvent.  "+Arrays.toString(checkBoxList.getMultiListModel().getSelectedIndices())));
hi.add(BorderLayout.NORTH, layoutSelector);
hi.add(BorderLayout.CENTER, BoxLayout.encloseY(checkBoxList, switchList));
hi.add(BorderLayout.SOUTH, GridLayout.encloseIn(2, addOption, clearSelections));
hi.show();
1 Instead of binding multiple listeners we can bind a listener to the list itself and get the selections
2 It’s easy to add options like clear selection and add option thanks to the structure of the list model
3 This list uses a layout as it’s in-effect just a Container
4 The CheckBoxList uses the same model as the SwitchList
Demo of the button list
Figure 1. Demo of the button list classes
]]>
1,072 Button Lists 0 0 0
Install on Home Screen blog/install-home-screen.html Tue, 4 Dec 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/install-home-screen.html

We talked about our support for Progressive Web Apps before. We added quite a few enhancements since that support was introduced and it’s a pretty powerful feature. Personally I consider it a killer feature, even if Google decides to ban your account you can still distribute your app.

One of the cool features is the seamlessness. Most of the functionality "just works". However, there are some cases where we need explicit hints due to the different behavior of desktop/mobile and web.

One such case is installation. PWA’s support an icon on the device home screen, but you need to explicitly ask the browser to install that icon. That’s unique to PWA’s and requires a new API to support that behavior. That’s why we introduced onCanInstallOnHomescreen, canInstallOnHomescreen() and promptInstallOnHomescreen() to help with that process. You can use them as:

onCanInstallOnHomescreen(()->{
    if (canInstallOnHomescreen()) {
        if (promptInstallOnHomescreen()) {
            // User accepted installation
        } else {
            // user rejected installation
        }
    }
});
This code expects import static com.codename1.ui.CN.*;

The code would prompt the user to install on the home screen in OS’s where this is appropriate. Notice that this will prompt the user once to install on the home screen so you don’t need additional guards against duplicate prompts.

]]>
1,073 Install on Home Screen 0 0 0
TIP: Canceling Subscriptions blog/tip-canceling-subscriptions.html Tue, 27 Nov 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-canceling-subscriptions.html

One of the things I like most about our subscription base is its solid nature. We still have a lot of subscribers in the $9 per month plan which we discontinued several years ago (it was so long ago I can’t find the relevant blog post anymore). That’s wonderful, it means people like our product and are with us for the long run.

However, flexibility is important too. The fact that subscriptions can be canceled easily is important. Canceling subscriptions is easy although we don’t have any control over that.
When you sign up for a subscription you effectively define a PayPal recurring payment process associated with your account. When we get a payment we keep the account at that level.

To cancel the subscription you need to login to paypal and cancel the recurring payment there. If this isn’t obvious to you we can do that (although we can’t change anything about the subscription, only cancel it). Just use the chat and give us the account email and paypal email addresses (assuming they differ).

Problematic Upgrades

We chose this approach for simplicity and security. This way we have no valuable credit card or billing information on our servers. This reduces their appeal to would be hackers. It also lets us focus on our business instead of billing etc.

One of the problems with this system is that we don’t have control over payment. We can’t fix mistakes and some things aren’t as seamless as they should be. One such thing is upgrades. If you upgrade your account or resubscribe on billing failure you can end up with two payments on the same account. These things are hard to detect due to some complex assumptions made in the system. So it’s crucial you cancel old payments when upgrading.

If you run into billing issues be sure to contact our support, using the chat button.

]]>
1,074 TIP: Canceling Subscriptions 0 0 0
Bottom Align blog/bottom-align.html Thu, 22 Nov 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/bottom-align.html

Box layout Y is one of the most used layouts in Codename One. It’s a wonderful, easy to use layout that makes vertical scrollable layout trivial. I love its simplicity, but sometimes that simplicity goes too far. A good example of that is the a common layout where we have a button at the bottom of the screen.

Historically we solved this by nesting box into a border layout:

Form f = new Form("Border Layout", new BorderLayout()); (1)

Container box = new Container(BoxLayout.y());
box.setScrollableY(true); (2)

Button b = new Button("Add New Button");

b.addActionListener(e -> {
    MultiButton mb = new MultiButton("Added Button");
    box.addComponent(0, mb);
    mb.setWidth(f.getWidth());
    mb.setY(f.getHeight());
    box.animateLayout(150);
});

f.add(SOUTH, b);
f.add(CENTER, box);

f.show();
1 Border layout implicitly disables the default scrolling of the Form
2 Because of that we need to scroll the box layout

When launched the UI looks like this:

Newly launched UI
Figure 1. Newly launched UI
After adding a couple of elements it looks like this
Figure 2. After adding a couple of elements it looks like this
After adding a lot of elements it looks like this
Figure 3. After adding a lot of elements it looks like this

Now this might be what you want. The add button is always clearly visible and easily accessible. However, in some cases this doesn’t work.

Lets say you want this exact behavior like we see in the first two images. But once we reach the edge of the form you want the button to act as if this was a regular box layout. Effectively the button would either align to the bottom of the form or the edge of the layout.

To accomplish this we are adding a new yLast mode in the BoxLayout which can be created using BoxLayout.yLast() or new BoxLayout(BoxLayout.Y_AXIS_BOTTOM_LAST). E.g the code below will produce the exact same result for the first two images:

Form f = new Form("Border Layout", BoxLayout.yLast()); (1)
Button b = new Button("Add New Button");
b.addActionListener(e -> {
    MultiButton mb = new MultiButton("Added Button");
    f.addComponent(0, mb);
    mb.setWidth(f.getWidth());
    mb.setY(f.getHeight());
    f.getContentPane().animateLayout(150);
});

f.add(b);

f.show();
1 Box layout doesn’t disable the default scrollability of form

When it’s completely filled the button is pushed down out of the view area:

The button scrolls down when there is no more space
Figure 4. The button scrolls down when there is no more space

I like this approach as it reduces clutter for the UI and leaves more space available. It doesn’t fit for all cases but it’s a valuable addition to the API. These changes will be available with the update we’ll release this Friday.

]]>
1,075 Bottom Align 0 0 0
Introducing Cloud Connect blog/introducing-cloud-connect.html Tue, 20 Nov 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/introducing-cloud-connect.html

A few years back we had Codename One LIVE!
It allowed developers to preview designs built with the old GUI builder on devices. Today we’re introducing the equivalent functionality for the new GUI builder and the Codename One Build app. Cloud Connect allows you to instantly see changes from the GUI builder in the app.

You can activate Cloud Connect using the new Cloud Connect button in the latest version of the GUI builder. Once it’s activated launch Codename One Build and open the side menu, you should see a new Cloud Connect option in the side menu.

You might need to kill and relaunch the app the first time around to make that menu appear

Check out the new How Do I video that covers the whole process.

This is a remarkably useful feature as it allows us to instantly see the nuanced impact of choices we make within the GUI builder. Small alignment or boundary choices within the tool can have a significant impact when you’re running on the device. Fonts and styles can look very different on the physical device than they do within the GUI builder.

But even more importantly, scrolling behavior and editing is impacted by design choices. E.g. in the video I made the login form scrollable. This isn’t something I thought I needed initially, but as I played with the UI on smaller devices it became apparent that this form must be scrollable. Using cloud connect saved me from the frustration of "compile → deploy → repeat".

]]>
1,076 Introducing Cloud Connect 0 0 0
Validate, Owner, Badges, ImageViewer and Picker Range blog/validate-owner-badges-imageviewer-picker-range.html Thu, 15 Nov 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/validate-owner-badges-imageviewer-picker-range.html

I’ve been pretty busy over the past few weeks and didn’t get a chance to write a "what’s new" post. This caused a huge backlog of new features which I’ll try to cut down in this post.

UI Validation

This is a feature that’s coming in the Friday update. Component inspector has a new "Validate" button that checks the UI for common mistakes. Right now it only checks for nested scrollables on the Y axis but ideally we should include additional checks.

This could be a pretty useful tool to diagnose common programming errors in the UI.

Material Commands

Command has a new factory method:

public static Command createMaterial(String name, char icon, ActionListener ev);

Using this API you can create a Command object with the given material icon.

Fixing Revalidate

This is a big one: revalidateWithAnimationSafety().

Codename One was inspired by Swing which didn’t have animations. We added them but our model for animations was too simplistic when we launched. We later refined it with the animation manager which was a huge overhaul.

Here’s the problem. Let’s say you have an animation running and you invoke component.remove() during the animation. It’s very likely remove() will disrupt the animation. So we serialized all calls to methods that mutate the UI. This means that if an animation is in progress it will wait for it to complete before doing the operation.

While this broke some edge case behaviors this was relatively compatible and worked reasonably well.

The problem is we didn’t change revalidate(). We left it "as is" and didn’t think it was a problem. It’s a complex method which impacts a lot of core functionality so leaving it in place was probably a good call.

However, for most cases you might want to use the new revalidateWithAnimationSafety() which makes sure animations aren’t disrupted by revalidate().

Component Ownership

Component.setOwner(), Component.isOwnedBy() and Component.containsOrOwns(x,y) allow components to denote an ownership hierarchy. This allows you to more easily track relationships hierarchical relationships between components that don’t happen to descendents of each other - e.g. for popup dialogs that are actually contained in a layered pane but are logically owned by components in the content pane. This is used by Dialog and InteractionDialog to better test for pointer events that occur outside their bounds. Now Dialog.setDisposeWhenPointerOutOfBounds() regards an event to be in bounds if it occurs on a component that is owned by the Dialog (or by a component in the dialog). The Picker, AutocompleteTextField and ComboBox popup dialogs have been updated to track their owner so that they behave appropriately. If you develop your own popups that are placed in the layered pane, it is up to you to set their owner appropriately using Component.setOwner() so that dialogs can deal with their pointer events properly.

There is no special house-keeping for the owner hierarchy. E.g. If you remove an owner from the form, it doesn’t do anything like remove its owned components also. It is up to you (the developer) to manage these relationships.

Badges on Android

We added limited support for badges in the Android port. Now Push type 101 is supported on Android as well. Also LocalNotification.setBadgeNumber(int) is supported. Notable omissions are push type 100 and Display.setBadgeNumber(int) which are still not supported. Android, while supporting badges on API 22 and above still can only set it in conjunction with a notification, so APIs that just set the badge number still can’t be implemented.

rotateRadian

Rotate works with absolute coordinates which in retrospect might have been a poor choice. We can’t change that as due to backward compatibility. So we added a new method rotateRadian() that rotate graphics context about the context’s current origin, taking into account translation.

Zoom Enhancements on ImageViewer

Carlos Verdier submitted a a pull request that adds a method to zoom to a specific location panning the image. As part of that change zoom is now animated by default.

You can disable zoom animations on ImageViewer using setAnimateZoom(false).

FontImage Rotation

Multiple icon fonts include rotating glyphs to indicate progress. While it’s trivial to rotate a FontImage we wanted to make it even simpler. As a result we added the method FontImage rotateAnimation() to FontImage. It returns a new FontImage instance using the same font/icon as the current font image. This new image is an animation that rotates to the right constantly.

You can use that in any Label subclass and it will animate the rotation constantly.

Efficient getRGB

Image.getRGB() isn’t very efficient as it might trigger the creation of a new array when it’s invoked. Dave Dyer submitted a new pull request that adds a new getRGB variant. This variant accepts an existing int[] array and fills it up instead of returning the array.

This reduces RAM allocation and is ultimately more efficient as a result.

Picker Rage Limits

One of the biggest RFE’s for the old Picker was the ability to determine ranges. Now that we have a lightweight picker implementation this is possible in a cross platform way:

public void setHourRange(int min, int max);
public void setStartDate(Date start);
public void setEndDate(Date end);

This lets you limit the range of hours or dates in a lightweight Picker so a user won’t be able to pick outside of that range.

]]>
1,077 Validate, Owner, Badges, ImageViewer and Picker Range 0 0 0
TIP: Don't Use Push as a Communication Protocol blog/tip-dont-use-push-as-communication-protocol.html Tue, 13 Nov 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-dont-use-push-as-communication-protocol.html

Apple introduced push notification at a time when iOS apps didn’t support multi-tasking. It was used as an intrusive notification system that allowed an app to communicate it had something important to tell you. Back then push messages would trigger a dialog box as it predated the pull down notification tray pioneered by Android.

The purpose of push on iOS is visual notification. You can send non-visual meta-data but that’s almost an afterthought in iOS.

On Android push was designed as an overarching general purpose communication protocol. It’s far more powerful and Google wanted developers to use it as the actual communication protocol. In fact the visual notification of push in Android is almost an afterthought and is handled by background code within the activity.

Developers coming from the Android ecosystem tend to think of push as a communication protocol and want to use that. That’s a bad idea for iOS and not a great idea for Android either.
We suggest using a proper communication protocol e.g. WebSockets. You should use push only for marketing related notifications and application notices in the background. That is far more portable, powerful and doesn’t suffer from the limitations of push (e.g. permissions).

E.g. in Codename One Build we use networking as such:

  • Most logic is handled through WebServices - these are use easy to build/maintain and debug

  • Events such as a new build are sent thorough a WebSocket connection - that removes the need for polling and is very fast

  • When a build is completed we send a push notification and a WebSocket event - if the app is running the WebSocket event will work. If it isn’t you will get push notification notice. If you disable push notifications everything would still work

This is the best approach for networking infrastructure and we recommend most apps follow this approach. Here are a few problems in push:

  • Push needs to go thru the Apple/Google servers which are unreliable complex and incompatible

  • Push doesn’t work everywhere e.g. kindle or all ports e.g. JavaScript push doesn’t work for all browsers

  • Push can be disabled by the user which means you literally can’t rely on it working even on supported platforms

  • You need your own servers to handle the push sending and batching anyway so you won’t be able to go "serverless" with push

  • Push enforces size limits on messages

  • Push doesn’t "really" work when an app is in the background in iOS. In iOS a push notification that includes a visual payload will show that e.g. an icon, badge, message, sound etc. even in a background app. However, the non-visual payload won’t be delivered to your app if it isn’t running

All things considered push makes sense only for visual notifications and marketing as an addition to your communication protocol not as a replacement.

]]>
1,078 TIP: Don't Use Push as a Communication Protocol 0 0 0
Subscription Pitfall blog/subscription-pitfall.html Tue, 6 Nov 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/subscription-pitfall.html

A while back Steve wrote about auto-renewing subscriptions and I recently got a chance to implement such a subscription in an app. However, it seems that all the changes in the world of in-app purchase created a situation where API’s work in some cases and don’t work for all of them.

After publishing this post we walked back on this, you now need to use subscribe for subscriptions again!

In the blog post, Steve used the purchase(sku) API to subscribe. This worked correctly as subscriptions are determined by the respective app store. As I implemented this code I chose to use the subscribe(sku) method which seems to make more sense. Unfortunately it doesn’t work and would be deprecated with the update this Friday.

It seems to work and even works on Android/iOS however, it doesn’t work with the receipt API which is an important part of the IAP workflow.

Despite a lot of the work we did for IAP it’s still one of the more painful API’s we need to work with. Dealing with the server side API is a nightmare. I hope we’ll come up with a better implementation for that moving forward.

]]>
1,079 Subscription Pitfall 0 0 0
Crisp cn1lib blog/crisp-cn1lib.html Tue, 23 Oct 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/crisp-cn1lib.html

Crisp powers the chat button in the bottom right portion of our site. It also handles emails and a host of other great features. One feature we didn’t take advantage of is the mobile app support. To solve that we just issued a new Crisp cn1lib which we integrated into the new versions of our Android and iOS apps.

You can install it yourself using the extension manager and use it with the instructions here.

There is some implementation detail related to the library which I think would be interesting to developers building similar solutions.

Why HTML/JS and Not Native?

When I started the work of porting the library I looked at the Crisp native SDKs for iOS/Android. I even got some code working but as I looked through the actual SDK source code it became apparent that Crisps native SDKs for iOS and Android don’t leverage native functionality. This is perfectly OK as HTML/JS can deliver a fine experience for this type of app.

However, its silly to wrap the native libraries when I can use HTML directly and get greater portability. As a result of that I threw away the code I wrote for the original integration and used the HTML approach. This highlights the importants of reviewing the implementation before we start implementing a cn1lib.

]]>
1,080 Crisp cn1lib 0 0 0
Build App on iOS blog/build-app-on-ios.html Wed, 17 Oct 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/build-app-on-ios.html

We launched the Codename One Build App beta on Android last week and now we have a public beta for iOS as well. You can sign up to join the public beta through this link. Notice that you will need testflight on your device to join the public beta test.

As is the case with the Android version please let us know if there are issues you experience in the issue tracker. The app mostly "just worked" on iOS but getting through the approval and submission process is always a pain. As part of that work we also improved the tablet UI which makes the app very usable on the iPad.

We’re refining the app further and hopefully we’ll bring it to production grade soon.

]]>
1,081 Build App on iOS 0 0 0
TIP: Obfuscation Mapping File blog/tip-obfuscation-mapping-file.html Tue, 16 Oct 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-obfuscation-mapping-file.html

Proguard is one of the most disliked aspects of Android programming. Developers attack it left and right because there are so many nuances to it. That’s a huge mistake, proguard is one of the most important tools in our development toolchain. It makes our apps slightly more secure, much smaller and even slightly faster. Codename One apps use proguard by default for Android. This is a huge benefit in our case because the limits related to obfuscation are very similar to the limits related to portability.

However, one of the side effects of obfuscation is jumbled stack traces. Normally this isn’t a "big deal" since line numbers are still correct. But when we have multiple releases it might be harder to track some of these line numbers through tags and history.

That’s where the mapping file you get when sending an Android build becomes useful.

Mapping file in Build Results
Figure 1. Mapping file in Build Results

You can use proguard to de-obfuscate mappings manually using the retrace command but that’s not necessarily helpful.

Google Play includes a section for crashes and ANR’s (Application Not Responding) reported by users. These include stack traces but they might come from a wide range of versions and might be confusing to track.

The Crashes & ANR's Section
Figure 2. The Crashes & ANR’s Section

The problem is that you can get crashes that are very unreadable due to obfuscation. That’s where the mapping.txt file becomes useful. After you upload the APK file and submit it the next step is the Deobfuscation files section. Here you can upload the mapping file matching your version, this will create slightly more readable stack traces for you.

Notice that the mapping.txt file differs between builds even if you didn’t change anything so make sure to use the file matching the APK you uploaded.

]]>
1,082 TIP: Obfuscation Mapping File 0 0 0
Switch, Progress and Pull to Refresh blog/switch-progress-pull.html Thu, 11 Oct 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/switch-progress-pull.html

Some of our older components were developed years ago. As Android and iOS slowly converged their UI paradigms we got stuck supporting odd/outdated functionality as designs shifted. Three great examples are pull to refresh, OnOffSwitch and the InfiniteProgress features.

OnOffSwitch contained labels both in iOS & Android. On Android it was literally a button that was moved back and forth. Today both OS’s use a simple switch graphic. When we developed the original component we didn’t have the same level of graphic drawing capability that we have today, that made the design of the iOS version even harder.

Pull to refresh was a feature twitter introduced. It became a hit among developers and slowly made its way into OS’s each of which implemented it differently. The original approach was very iOS centric and relied on the way drag works on that platform. Newer approaches such as the one in material design use an overlay approach. This also brings us to the InfiniteProgress class which is technically a trivial class, however in material design it has a very distinct special effect that’s hard to replicate in the current design.

Switch

People have been complaining about the OnOffSwitch for a while. Recently ramsestom contributed some Android code to improve this behavior and we decided to take the plunge.

Fixing the old class would have been hard. It was designed to support two very different component types and includes a lot of kludges necessary for the limited graphics capabilities of Codename One 1.0. So we needed a clean break with the new Switch class.

The New Switch Class in Action
Figure 1. The New Switch Class in Action

The new class is trivial and works as a drop-in replacement for OnOffSwitch. The great thing about it is the newfound ability to customize everything through the theme/css. You can learn more about that in the class JavaDoc.

InfiniteProgress

Despite its huge legacy there was no need to rewrite InfiniteProgress since the class is much simpler. However, since the new material design progress behavior might not be to everyones liking its off by default. We’ll switch this on for Android when we feel it’s stable enough.

You can activate the experimental mode either through code using:

InfiniteProgress.setDefaultMaterialDesignMode(true);

Or by setting the theme constant infiniteProgressMaterialModeBool to true. Both of these will impact the pull to refresh and progress animation.

Notice that the implementation of pull to refresh would be completely different when this is invoked so it’s very possible that quite a few things would break or misbehave. Proceed with caution and let us know!

]]>
1,083 Switch, Progress and Pull to Refresh 0 0 0
Build App Beta blog/build-app-beta.html Wed, 10 Oct 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/build-app-beta.html

One of the big "behind the scenes" motivations for our big build cloud migration was new server API’s. We now have a completely new backend and this made it easier to build a completely new Codename One App christened as "Codename One Build".

This app is currently in public beta on Android which you can opt-into here. Once we feel good with the Android version we’ll push out versions for iOS and maybe UWP if there’s demand for that.

To keep things simple we are currently focusing on the bare minimum so we can build on top of that. Furthermore, it’s at version 0.13 so it’s far from production grade. But it works really well and should be instantly useful for all of us. It includes several interesting features:

  • Push notifications on builds

  • Essentially eliminates the need of using the website for build tracking

  • Ability to subscribe using In-App-Purchase - this is highly experimental so if you run into a problem let us know!

We made the app beta to get it out as soon as possible but for all intents and purposes this is Alpha level software

We have big plans for this app and have some pretty interesting ideas for it as we move forward. Right now we want to focus on the core functionality so it runs as smoothly as possible. As it matures we might use this app as the basis for a new web UI as well.

We plan to push frequent updates to this app and refine it as we go. So we need your help with issues which you can file in the usual place.

]]>
1,084 Build App Beta 0 0 0
TIP: Fix Issue with Missing Builds blog/tip-fix-issue-missing-builds.html Tue, 2 Oct 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-fix-issue-missing-builds.html

A while back we announced the migration to the new build cloud. The migration worked very smoothly and mostly seamlessly but there was one caveat: client libraries must be up to date. This is a confusing point so hopefully this long overdue post will clarify it.

The core of the problem is CodeNameOneBuildClient.jar. It’s a relatively simple jar with a few ant tasks that performs a lot of "under the hood" services such as sending the build to the cloud. It’s shipped within the IDE plugin and old versions of the IDE plugins would replace it automatically. We now update it via the update framework which is better but might cause a few issues.

Generally the issues can be expressed either via a build that doesn’t appear. You might get an error that a build is already in the queue and once we remove app engine entirely you’ll get a connection error.

To fix this you need to do the following:

  • Update your plugin to the latest version, make sure that other team members don’t use an old plugin either

  • Run Update Project Libs which you can do by right clicking the project and selecting: Codename OneCodename One SettingsBasicUpdate Project Libs

You need to Update Project Libs for every project if you have more than one

Notice that new projects should be fine.

If this Didn’t Work

The problem is that these two steps might fail. Here are things you need to look at:

  • Make sure the Versions.properties and the jars in the projects aren’t under source control. They should be excluded from it as we update them dynamically

  • In your home directory there is a directory named .codenameone make sure it doesn’t contain an UpdateStatus.lock file. If it does you can delete it assuming it’s been there for a while

When in doubt you can delete Versions.properties and Update Project Libs again. This should work but if that doesn’t do it you can go with the "nuclear option" and delete the .codenameone directory and Versions.properties. After that do an Update Project Libs.

If this still doesn’t work let us know via the chat. Ideally try to run the update framework from command line to figure out what went wrong.

]]>
1,085 TIP: Fix Issue with Missing Builds 0 0 0
Codename One 5.0 "Social" is now Live blog/codename-one-5-0-social-live.html Wed, 19 Sep 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/codename-one-5-0-social-live.html

We are thrilled to announce the release of Codename One 5.0 - Social. Codename One is an open source "Write Once Run Anywhere" mobile platform for Java and Kotlin developers!
We postponed the release of this version since it’s so packed with big changes. We made CSS a first class citizen in Codename One and made CSS updates live (no recompile necessary). We moved from screenshots in iOS launches to storyboards. Added support for newer JDK’s. Migrated to Android API level 27. Moved our entire build server infrastructure. Redid push notification and so much more…​

There is SO MUCH more, check out the details below.

However if you are new to Codename One here’s a short primer. Codename One is the only platform that…​

  • Has Write Once Run Anywhere with no special hardware requirements and 100% code reuse

  • Compiles Java or Kotlin into native code for iOS, UWP (Universal Windows Platform), Android & even JavaScript

  • Is Open Source & Free for commercial use with an enterprise grade commercial support

  • Is Easy to use with 100% portable Drag & Drop GUI builder

  • Has Full access to underlying native OS capabilities using the native OS programming language (e.g. Objective-C) without compromising portability

  • Has full control over every pixel on the screen! Just override paint and draw or use a glass pane to draw anywhere…​

  • Lets you use native widgets (views) and mix them with Codename One components within the same hierarchy (heavyweight/lightweight mixing)

  • Supports seamless Continuous Integration out of the box

To learn more about Codename One check out the about page you can download it for free right now.

Version 5.0 is nicknamed Social because of the Facebook Clone application that was developed with it for the online course in the Codename One Academy.

Facebook Native App vs. our Clone
Figure 1. Facebook Native App vs. our Clone

Highlights of this Release

The top 5 features of this release are covered in this short video, check out further details below…​

  • Live CSS Update — CSS is now deeply and seamlessly integrated into Codename One. When you change the content of a CSS file and save the Codename One simulator automatically updates on the fly

  • Rich Push Notifications — Push notification was overhauled, we moved the last of the functionality from GCM to FCM. We now support rich push notifications that can include images and complex functionality

  • Launch Screen Storyboards — Historically iOS used screenshots of apps to fake fast application launches. Codename One automated that process in the past, it’s now discoraged by newer iOS features such as side-by-side multi-tasking. As such we now use storyboard launch files. This allows side-by-side multi-tasking and as a bonus speeds up compilation while reducing the app size further

  • New JDK/OpenJDK Support — We now support JDK’s 8 to 11 this includes OpenJDK

  • New Cloud Servers — We migrated the last remaining Codename One servers off of Google App Engine. This allowed us to introduce great new features such as the ability to increase your free build quota

  • Removed Old IDE Preferences UI — The old right click IDE preferences UI was causing a lot of confusion due to lack of maintenance. It’s now gone and replaced completely by Codename One Settings

  • Android API Level 27 — We moved to Android’s API Level 27 by default. Since Google requires API level 26 or higher at this time. We’ll probably update API levels faster due to this policy

  • Lightweight Picker — The Picker component was rewritten as a lightweight component instead of a native one. This allows far more customization and cross platform consistency for one of our most problematic widgets

  • Low Level Camera API — Camera Kit allows developers to access the native camera view to grab photos/videos and overlay graphics on top of the camera

  • Pluggable Spatial SQLite — Spatial support for SQLite lets developers write complex location based applications. This functionality lets developers replace the existing native SQLite implementation with an arbitrary implementation which is very useful for enterprise grade features such as deep encryption, replication etc.

  • Improved Map Layout — The map API now includes a native high performance component layout built in

  • Landscape UIID’s — Components can adapt their UIID to landscape, this enables features such as smaller title font/padding in landscape mode

  • Multiple Smaller Improvements:

There are many other features both big and small. Check out our blog and the github project history.

Onwards to 6.0 - Chat

We took a lot of time for 5.0 and I’d like to take a similar duration for 6.0. I think this made 5.0 a better release.

We will have a whatsapp clone tutorial in the Codename One Academy. Hence the moniker of the next release.

Check out our survey results to see the future apps we’ll release into the academy. Even if you never plan to signup to the academy this should be interesting as it gives you a good notion of what can be built with Codename One.

We Need your Help

If you think we are doing a good job and appreciate our help please help us by:

Thanks for reading this far and if you have any thoughts/suggestions of any kind please let us know!

]]>
1,086 Codename One 5.0 "Social" is now Live 0 0 0
Codefreeze for Codename One 5.0 - Social blog/codefreeze-5-social.html Wed, 12 Sep 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/codefreeze-5-social.html

Codename One 5.0 (Social) will launch next week, to keep the code stable we are entering a week long code freeze. Please update your plugin installs frequently and report bugs immediately so we will have a stable release!

We’ve added a lot of new features to 5.0 but a few of the big things are pretty disruptive:

Because all of these changes have many subtle implications we are relying on feedback from you on what’s working and what isn’t. If you run into any issue please file the issue ASAP so we can move quickly and update the release if necessary.

During this week of code freeze we’ll try to update the documentation and prepare everything for the release.

Upcoming Milestones

We will branch to the 5.0 branch in our repository and every change to the branch will be cherry picked individually.

Here are the coming releases after 5.0:

  • 6.0 (Chat) - Scheduled for February 19 2019

  • 7.0 (Video) - Scheduled for June 12 2019

You can track these milestones and the related tasks in our github project here.

]]>
1,087 Codefreeze for Codename One 5.0 - Social 0 0 0
TIP: Auto Reconnect Web Socket blog/tip-auto-reconnect-web-socket.html Tue, 11 Sep 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-auto-reconnect-web-socket.html

WebSockets changed the way I do networking code. I combine them with WebServices to get the best of both worlds. But they still suffer in terms of reliability. With WebServices we have retries and a mostly transactional model. There is no permanent connection that should be re-established.

With WebSockets a disconnect can be painful, up until recently I used a rather elaborate strategy of error detection and timers. With the latest update to the WebSocket cn1lib we now have a better solution: autoReconnect(int).

It’s exactly as it sounds, once you create a websocket you can invoke autoReconnect(5000) on it to retry the connection ever 5 seconds in case of a disconnect.

If you have an existing WebSocket app you should probably add this call.

]]>
1,088 TIP: Auto Reconnect Web Socket 0 0 0
Preparing for Codename One 5.0 - Social blog/preparing-for-social.html Thu, 6 Sep 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/preparing-for-social.html

Codename One 5.0 AKA Social will launch on September 19th. We pushed this release back to include support for crucial features such as JDK 11/OpenJDK support. With these features in place and a slew of other features we are gearing towards 5.0 GA. Tomorrow (Friday) we will push out plugin version 4.5 for all IDE'. This new version is effectively release candidate 1 for the 5.0 version.

Normally we don’t bother with a release candidate this close to an actual release as the plugins don’t change much. However, we made some extensive changes to the plugins recently and we’d like to make sure we didn’t break too many things before the release.

We will enter the release code freeze for social on September 12th after which only reviewed commits can be cherry picked. Only critical bugs will be fixed at that point.

Currently version 6.0 (AKA Chat) is scheduled for release in February 19th 2019.

Social

We named the releases of Codename One based on the signature app we built on top of that given release. 4.0 was named Taxi as it was used to develop an Uber clone. Version 5.0 is named social as we built a Facebook Clone during the release cycle.

For Version 6.0 we will build a whatsapp clone hence the "Chat" moniker.

]]>
1,089 Preparing for Codename One 5.0 - Social 0 0 0
Removing the Old Preferences blog/removing-old-preferences.html Wed, 5 Sep 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/removing-old-preferences.html

A while back we introduced Codename One Settings which superceded the old approach built in the IDE itself. This allowed us to consolidate code and move faster. That’s how we were able to implement more wizards for things like CSS support etc.

Up until now we just left the old UI in place. People are still used to it. But it has a lot of bugs and causes confusion as developers launch the old UI instead of the new one. So with version 5.0 we’ll remove the old preferences UI and leave Codename One Settings.

We’ll also remove the deprecated build targets from the menu and try to streamline the first usage of Codename One a bit. Most of these changes shouldn’t be noticeable for most developers. However, from experience I’m sure there are quite a few developers out there that didn’t move to Codename One Settings.

]]>
1,090 Removing the Old Preferences 0 0 0
TIP: Activate via URL and Send Arguments blog/tip-activate-via-url-send-arguments.html Tue, 4 Sep 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-activate-via-url-send-arguments.html

The most secure password in the world is the one that doesn’t exist. You remove the user from the equation with a completely random key. To be fair this has some drawbacks and a password still exists somewhere (in your phone/email) but generally this works rather well…​

The trick is simple, if we want to authenticate a user we can email him a single use URL e.g. mycoolapp://act-32548b09-d328-4330-8243-d7d30c322e40. As you can see that’s pretty hard to guess or brute force. Once clicked the URL becomes invalid so even if it’s exposed somehow it would still be irrelevant. To do this we need two parts:

  • The server logic

  • Client URL handling

Both are pretty easy.

The Server

One caveat is that the mycoolapp will work on the device but you can’t click it in an email or in a browser. So we will need an https URL from your server.

The server would look something like this, notice that this is Spring Boot Controller code but you should be able to use any server out there:

public boolean sendSigninEmail(String e) {
    List<UserObj> ul = users.findByEmailIgnoreCase(e);
    if(ul.isEmpty()) {
        return false;
    }
    UserObj u = ul.get(0);
    u.setHashedActivationToken(UUID.randomUUID().toString()); (1)
    users.save(u); (2)
    email.sendEmail(e, "Signin to the Codename One App", "This is a one time link to activate the Codename One App. Click this link on your mobile device: \n\nhttps://ourserverurl.com/app/activateURL?token=act-" + u.getHashedActivationToken()); (3)
    return true;
}
public User activateViaToken(String t) throws ServerAppAPIException {
    List<UserObj> ul = users.findByHashedActivationToken(t); (4)
    if(ul.isEmpty()) {
        throw new ServerAppAPIException(ServerErrorCodes.NOT_FOUND);
    }
    UserObj u = ul.get(0);
    String val = u.getAppToken(); (5)
    u.setHashedActivationToken(null); (6)
    users.save(u);
    User r = u.getUser();
    r.setAppToken(u.getAppToken());
    return r;
}
1 We use UUID to generate the long activation string
2 We save it in the database overwriting an older URL if it exists
3 We can send an email or SMS with the HTTPS URL to activate the app
4 Next we activate the user account with the received token. We find the right account entry
5 An access token is a secure password generated by the server that’s completely random and only visible to the app
6 The activation token used in the URL is removed now making the URL a single use tool

All of that is mostly simple but there is still one missing piece. Our app will expect a mycoolapp URL and an HTTPS URL won’t launch it. The solution is a 302 redirect:

@RequestMapping(value="/activateURL", method=RequestMethod.GET)
public void activateURL(@RequestParam String token, HttpServletResponse httpServletResponse)  {
    httpServletResponse.setHeader("Location", "mycoolapp://" + token);
    httpServletResponse.setStatus(302);
}

This sends the device to the mycoolapp URL automatically and launches your app with the token!

Client Side

On the client we need to intercept the mycoolapp URL and parse it. First we need to add two new build hints:

android.xintent_filter=<intent-filter>   <action android:name="android.intent.action.VIEW" />    <category android:name="android.intent.category.DEFAULT" />    <category android:name="android.intent.category.BROWSABLE" />    <data android:scheme="mycoolapp" />  </intent-filter>
ios.plistInject=<key>CFBundleURLTypes</key>     <array>         <dict>             <key>CFBundleURLName</key>             <string>com.mycompany.myapp.package.name</string>         </dict>         <dict>             <key>CFBundleURLSchemes</key>             <array>                 <string>mycoolapp</string>             </array>         </dict>     </array>
Don’t forget to fix mycoolapp and com.mycompany.myapp.package.name to the appropriate values in your app

Next all we need to do is detect the URL in the start() method. This needs to reside before the code that checks the current Form:

String arg = getProperty("AppArg", null); (1)
if(arg != null) {
    if(arg.contains("//")) { (2)
        List<String> strs = StringUtil.tokenize(arg, "/");
        arg = strs.get(strs.size() - 1);
        while(arg.startsWith("/")) {
            arg = arg.substring(1);
        }
    }
    if(!arg.startsWith("act-")) { (3)
        showLoginForm();
        callSerially(() ->
            Dialog.show("Invalid Key", "The Activation URL is invalid", "OK", null));
        return;
    }
    arg = arg.substring(4);
    Form activating = new Form("Activating", new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER));
    activating.add(CENTER, new InfiniteProgress());
    activating.show();
    sendActivationTokenToServer(arg); (4)
    return;
}
1 This is from the CN class globally imported. The app argument is the URL
2 We remove the URL portion of the argument
3 The act- prefix is there to validate the URL is correct
4 This sends the activation key to the server logic we discussed above

Testing in The Simulator

This will work in iOS and Android. Starting next week you could also test this on the simulator using the new Send App Argument menu option in the simulator.

To integrate this properly into an app you would normally have a login menu that accepts only the email/phone. Or a system in your web based UI to send an invite link to the app.

Whatsapp uses an inverse of this trick to activate their desktop app. They show a QR code to your device and once you scan that QR code with your whatsapp phone install the desktop version is activated. That’s much better than passwords.

]]>
1,091 TIP: Activate via URL and Send Arguments 0 0 0
Status Monitor blog/status-monitor.html Thu, 30 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/status-monitor.html

I wrote before about Crisp and how pleased we are over the migration to their service. Recently they started offering a new service of status page. This service runs on their servers and essentially monitors whether our service is down.

Due to the complexity of our service not all of the pieces are monitored but a few of the more important features are already mapped. Hopefully, if you experience service issues you can look in the status page and you’d know if something is going on. Notice that when it goes red we get alerts and notice that something is broken

You can see the status page at https://status.codenameone.com/

]]>
1,092 Status Monitor 0 0 0
No More iOS Screenshots blog/no-more-ios-screenshots.html Wed, 29 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/no-more-ios-screenshots.html

In February I wrote about a new/improved way to build for iOS without the screenshot process. That was a bit ahead of its time as the xib build didn’t disable the screenshot process yet. This is now fixed and it’s turned on by default now. That means that if you send an iOS build it won’t go through the screenshot generation process.

This means that apps with native peers in the first screen such as maps, browser etc. will start working with this coming update. It also means the splash screen of the application on iOS will be relatively simple by default. You can customize it using xcode to make it more appealing. The upside is things such as multi-tasking etc. will work correctly.

A side benefit of this is a slightly faster build. It would also make the resulting binaries even smaller than they already are. In some cases the screenshots increased the size of the binaries significantly.

If you want to revert to the old behavior you can do so by setting the build hint ios.generateSplashScreens=true. It now defaults to false.

]]>
1,093 No More iOS Screenshots 0 0 0
On Device Web Server blog/on-device-web-server.html Tue, 28 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/on-device-web-server.html

I try to write about every new feature or capability we introduce but this isn’t always possible. Tasks sometimes weigh me down and as they do so I sometimes find something that I totally neglected as it was released. Such is the case with the CN1Webserver library which we launched over 6 months ago.

It got lost and in fact it came out during such a busy period I completely forgot about it.

This cn1lib lets you create a simple web server on the device. You can then connect to this server either from the device itself (the more common use case) or remotely.

This sounds insane but it’s a surprisingly common trick used by developers to get around odd device limitations. I first ran into this use case around 2010. Back then a company I was consulting for needed a way to process media files before playback (to apply DRM). This was impossible in Android at the time and it’s still challenging. Their solution was genius and trivial: they implemented a webserver on the device.

This way they could download and decode the file locally, then playback from a local URL on the device itself. The devices media API was oblivious to the fact that DRM was used in playback.

There are quite a few additional cases such as the ability to embed HTML/JS code that needs server interaction in order to work. For most of us this feature is pretty niche but the fact that you can accomplish it with a simple plugin is huge.

You can install the cn1lib like you can any other cn1lib from the Extension Manager. You will need additional setup so it will work correctly on the devices as explained in the github project.

]]>
1,094 On Device Web Server 0 0 0
Code Ranch Questions blog/code-ranch-questions.html Tue, 21 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/code-ranch-questions.html

I’m answering questions in the Code Ranch this week about Create an Uber Clone in 7 Days. So far I’ve had some pretty great ones, if you have a question join the conversation for a chance to win a free copy. To qualify just ask in the Android forum and make sure to qualify it with "Create an Uber Clone in 7 Days:" so I will notice the question.

So far the book is doing well trending in the #2 to #3 spot for mobile development books in the kindle store. The print version isn’t selling as nicely, probably because of the huge price difference between the kindle and print book. Since the print book took far more effort, I might skip doing a print book if I go through this again.

]]>
1,095 Code Ranch Questions 0 0 0
Uber Book is Out! (And JDK 11 Support) blog/uber-book-is-out-jdk-11.html Thu, 16 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/uber-book-is-out-jdk-11.html

Finally after all this time you can buy the Uber book today in both in Kindle and print versions!
Notice that the kindle pricing is currently very low due to Amazon’s restrictions. Once the Amazon exclusivity expires the price of the ebook will go up.

Notice that the book is also available on all Amazon domains so you can order from UK, DE, FR, ES, IT, NL, JP, BR, CA, MX, AU and IN.

Notice that the print book should also be available in local stores, but you’ll probably need to ask the store to stock it. For now the book won’t launch on other digital stores due to Amazon exclusivity, I’m not sure when/if this will change.

You can check out the first two chapters at the book site. Please spread this far and wide, if the book is a success I’ll turn it into a series featuring other apps as well.

JDK 11

In unrelated news, this weekend we’ll release experimental support for JDK 9, 10 and 11. This support means Codename One will work when running on these JDK’s (and OpenJDK) but it doesn’t mean that Codename One will add features from these JDK’s.

If you have a newer JDK on your machine we’d appreciate issues covering this support. We’re releasing it early to give us time to test it until the 5.0 release.

It’s an important feature as JDK 8 reaches end of life at the end of the year.

]]>
1,096 Uber Book is Out! (And JDK 11 Support) 0 0 0
Table Property Mapping blog/table-property-mapping.html Wed, 15 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/table-property-mapping.html

If you aren’t using properties with Codename One, you probably should. Here’s a post I wrote a while back covering them, it should give you a lot of reasons for this. We are slowly integrating them into API’s such as Rest and as a result the code is simpler. A huge bonus is the type safety and flexibility that comes with this API.

Up until now binding in properties was mostly limited to "simple" classes such as TextField. Complex structures such as Table weren’t supported. This is no longer the case.

With the coming update UIBinding now includes a new API to bind a list of PropertyBusinessObject to a TableModel. This effectively allows the creation of table matching list of objects without writing any code!

Storing Object Lists as JSON

Before we go into the table binding code we needed some API’s to support the following demo. One such API in PropertyIndex is:

public static void storeJSONList(String name, List<? extends PropertyBusinessObject> objs);
public <X extends PropertyBusinessObject> List<X> loadJSONList(String name);

These API’s let you store the data in the list of property objects into JSON. It also lets you load that JSON data into a newly created list. When we implemented this initially it failed for the date object. It turns out that storing Date objects into JSON isn’t standardized. Googling a bit showed that dates are often written like this in JSON: 2018-08-01T18:28:23.292.

So this is now the standard for date objects stored/loaded into JSON.

Sample

Lets look at how this work with a sample based on the old code I wrote a while back. You can check out the full project here. First we’ll start with the properties object itself which isn’t very different:

public class Contact implements PropertyBusinessObject {
    public final IntProperty<Contact> id  = new IntProperty<>("id");
    public final Property<String, Contact> name = new Property<>("name");
    public final Property<String, Contact> email = new Property<>("email");
    public final Property<String, Contact> phone = new Property<>("phone");
    public final Property<Date, Contact> dateOfBirth = new Property<>("dateOfBirth", Date.class);
    public final Property<String, Contact> gender  = new Property<>("gender");
    public final IntProperty<Contact> rank  = new IntProperty<>("rank");
    private final PropertyIndex idx = new PropertyIndex(this, "Contact", id, name, email, phone, dateOfBirth, gender, rank);

    @Override
    public PropertyIndex getPropertyIndex() {
        return idx;
    }

    public Contact() {
        name.setLabel("Name");
        email.setLabel("E-Mail");
        phone.setLabel("Phone");
        dateOfBirth.setLabel("Date Of Birth");
        gender.setLabel("Gender");
        rank.setLabel("Rank");
    }
}

There isn’t much to say about this object it’s pretty standard. So lets initialize it in the start() method:

private List<Contact> listOfContacts;
public void start() {
    if(current != null) {
        current.show();
        return;
    }
    if(!existsInStorage("contacts.json")) {
        listOfContacts = new ArrayList<>(); (1)
        Contact c =new Contact().
            id.set(1).
            dateOfBirth.set(new Date()).
            name.set("Shai").
            gender.set("Male");
        listOfContacts.add(c);
        listOfContacts.add(new Contact().
            id.set(2).
            dateOfBirth.set(new Date()).
            name.set("Steve").
            gender.set("Male"));
        listOfContacts.add(new Contact().
            id.set(3).
            dateOfBirth.set(new Date()).
            name.set("Chen").
            gender.set("Male"));
        PropertyIndex.storeJSONList("contacts.json", listOfContacts); (2)
    } else {
        listOfContacts = new Contact().getPropertyIndex().loadJSONList(
            "contacts.json"); (3)
    }

    // rest of start method ...
}
1 If there are no entries I initialize the list to valid default values
2 I store the list of objects to the Storage as JSON. Notice this is a static method as we already have object instances in the list
3 Loading the JSON does require an object type for context
Notice that this won’t work for JSON that contains more than one type of object. So I can’t store Contact and Person in a single JSON file

The resulting JSON file looks like this:

[{
  "gender": "Male",
  "name": "Shai",
  "dateOfBirth": "2018-08-14T18:27:43.585",
  "id": 1
},
{
  "gender": "Male",
  "name": "Steve",
  "dateOfBirth": "2018-08-14T18:27:43.585",
  "id": 2
},
{
  "gender": "Male",
  "name": "Chen",
  "dateOfBirth": "2018-08-14T18:27:43.585",
  "id": 3
}]

Notice that the date is listed using the new standard format for date strings.

Now that the data is persistent lets create a table to edit this data.

The Table UI

The table binding code from which this post originates produces this output for the Contact object:

Property Table for the Contact Object
Figure 1. Property Table for the Contact Object

This was essentially created using these three lines of code:

Contact prot = new Contact();
UiBinding.BoundTableModel tb = ui.createTableModel(listOfContacts, prot);
Table t = new Table(tb);

The first line uses a prototype object based on which the table structure is determined. Next we create a table model of the type BoundTableModel which includes some special capabilities I’ll cover soon. The next line creates the table…​ That’s it!

The full code doesn’t contain all that much more:

Form hi = new Form("Property Table", BoxLayout.y());
UiBinding ui = new UiBinding();
Contact prot = new Contact();
UiBinding.BoundTableModel tb = ui.createTableModel(listOfContacts, prot);
tb.setMultipleChoiceOptions(prot.gender, "Male", "Female", "Unspecified");
Table t = new Table(tb);
hi.add(t);
hi.getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_ADD, e ->
        tb.addRow(tb.getRowCount(), new Contact().
            name.set("Unnamed")));
hi.getToolbar().addMaterialCommandToRightBar("", FontImage.MATERIAL_REMOVE, e -> {
        if(t.getSelectedRow() > -1)
            tb.removeRow(t.getSelectedRow());
    });
hi.getToolbar().addMaterialCommandToRightBar("",
    FontImage.MATERIAL_SAVE, e ->
        PropertyIndex.storeJSONList("contacts.json", listOfContacts));
hi.show();

You will notice I set multiple choice options for the gender. That means that when someone clicks the gender column they will see this:

The gender option is a picker
Figure 2. The gender option is a picker

That’s really cool but it gets better. The date column implicitly uses a date picker:

The date is implicitly a picker
Figure 3. The date is implicitly a picker

Notice the commands in the source above, they include some familiar code (such as the save method). But the other commands include new methods that aren’t available in the standard TableModel class.

The BoundTableModel includes a few features that aren’t available in TableModel specifically:

public void excludeProperty(PropertyBase b); (1)
public void setColumnOrder(PropertyBase... columnOrder); (2)
public void setEditable(PropertyBase pb, boolean editable); (3)
public void addRow(int index, PropertyBusinessObject b); (4)
public void removeRow(int index);
public void setMultipleChoiceOptions(PropertyBase prop, String... values);
public void setValidationConstraint(PropertyBase prop, Constraint c); (5)
1 Allows us to hide a column from the table
2 Allows us to determine the order of the columns, normally they are in the order they are added to the PropertyIndex
3 We can flag a specific property as non-editable
4 Add/remove a row will also update the origin list or property
5 We can bind validation logic to a specific property

Abstract Table Model

A lot of the power of this class is enabled via the new AbstractTableModel class. Up until now TableModel was the base interface that was implemented by DefaultTableModel. It’s a good abstraction but we needed a way to add new API’s without changing the interface.

Had we migrated the core of Codename One to Java 8 we might have used default methods. Instead we added a new abstract class that implements all the new API’s we need from TableModel. Table now has special cases internally for AbstractTableModel.

Most of the API’s in AbstractTableModel map directly to the functionality you see above. E.g. public Class getCellType(int row, int column) allows the Table to to generate the right cell type by default.

Final Word

This is relatively simple, I could have gone much further. I could have used an auto generated UI to edit individual rows etc. But I wanted to keep things simple and manageable.

As you can see from the blog, this week I’ve been playing a lot of catch up with updates on where we are. There is a lot more coming as we ramp up to Codename One 5.0 in September.

]]>
1,097 Table Property Mapping 0 0 0
SendGrid cn1lib blog/sendgrid-cn1lib.html Tue, 14 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/sendgrid-cn1lib.html

When we announced the migration to the new cloud servers one of the casualties was the cloud email API. This was a well intentioned API for sending an email from an app. Unfortunately we didn’t understand the complexities of modern mail systems well enough when we came up with this API. It turns out that this is pretty problematic. Mail servers get blacklisted and emails fail to deliver.

The problem is that this impacts everyone, if one bad actor sends spam with our cloud servers all of us get blacklisted…​

The solution is simple. We now have a new cn1lib for SendGrid. If you aren’t familiar with SendGrid it’s one of the several leading transactional e-mail providers. They offer 100 free emails per day. They provide a powerful developer REST API which we utilize in this cn1lib.

If you are using the cloud email feature we strongly suggest migrating your code to this cn1lib ASAP. We usually try to give more notice in advance but the app engine backend is something we need to remove in the near future.

Note that this only applies to the send email via cloud API’s and not to the standard send email API’s which are just fine!

]]>
1,098 SendGrid cn1lib 0 0 0
Rest API Error Handling blog/rest-api-error-handling.html Mon, 13 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/rest-api-error-handling.html

We added a lot of features and fixed bugs over the past couple of months and I’ve been a bit lax on blogging. I’ll try to fix that as we approach the revised 5.0 release date. One of the big changes we added over the weekend (it will be in the builds on Friday), is a huge rework of the Rest API.

If you are unfamiliar with the Rest API check out this older post. The Rest API had a few problems, the most pressing of these was the inconsistent error handling behavior. A failure of the Rest call didn’t always trigger error handling correctly and behaved inconsistently on reading the response of the error.

Another inherent problem which we ran into was processing errors differently. E.g. I have a JSON webservice but an error might return a String instead of JSON. With the existing API there was no way of handling this.

This mostly impacted the asynchronous calls but was a problem with the synchronous calls too. Another problem was the SuccessCallback interface used through the API. In itself it’s fine but the callback method is named: onSucess which somehow slipped through…​
Unfortunately fixing interfaces is nearly impossible so we had to add a new interface OnComplete. Like the SuccessCallback the interface is "lambda friendly" by featuring only one method.

A Word About Deprecation

We use deprecation a lot to indicate the better API for usage. As a result we had to deprecate all the existing asynchronous calls in the Rest class and replace them with new API’s.

We won’t remove these deprecated API’s in the foreseeable future. The deprecation is there to show the better/newer API’s.

Better Properties Integration

As part of this work we also squeezed in some work on property business object support. So if I want to make a JSON request to the server with a User PropertyBusinessObject I can do something like:

User userPropertyObject = ...;
ServerResultObject result = Rest.post(url).
   jsonContent().
   body(userPropertyObject).
   getAsProperties(ServerResultObject.class);

Notice that the result can also be parsed from JSON into a property business object seamlessly!

Fetch API’s

The existing synchronous get API’s such as getAsString() didn’t change. We left the signature as is and they should work the same way.

However, the asynchronous API’s relied on the old SuccessCallback interface so we had to change the signature. If this was old Java 5 we could have just overloaded the method but this would have created a conflict for lambda calls so we decided to change the method name to a fetch prefix.

So the existing code:

Rest.get(url).
   getAsString(c -> callback(c));

Would become:

Rest.get(url).
   fetchAsString(c -> callback(c));

Since I used a lambda the fact that we replaced SuccessCallback with OnComplete becomes irrelevant.

All the versions of the methods that received FailureCallback instances are now deprecated as there were serious problems with error handling.

Error Handling

The new error handling code is far more consistent and would work both for the synchronous and asynchronous calls. Notice that if you don’t define an error handler the behavior in the case of an error might be slightly inconsistent but should generally just return the error value in the standard response.

E.g.:

Rest.get(url).
   jsonContent().
   onErrorCodeString(r -> error(r)).
   fetchAsJsonMap(c -> callback(c));

There are five new onError style methods:

public RequestBuilder onErrorCodeBytes(ErrorCodeHandler<byte[]> err);
public RequestBuilder onErrorCodeJSON(ErrorCodeHandler<Map> err);
public RequestBuilder onErrorCode(ErrorCodeHandler<PropertyBusinessObject> err, Class errorClass);
public RequestBuilder onErrorCodeString(ErrorCodeHandler<String> err);
public RequestBuilder onError(ActionListener<NetworkEvent> error);

The first four methods are invoked for an error code from the server (value that isn’t 2xx or 3xx from the HTTP response). Notice that each type parses the result and can use a different parsing system from the default parsing logic. We even support parsing errors into a PropertyBusinessObject which can be of a type different from the one used in the main result. So you can define a ServerError business object and parse error result JSON directly into a type-safe object.

The ErrorCodeHandler interface is a simple lambda friendly interface that returns the Result object.

The last method onError is invoked in case of an exception during the connection. We separate a failure such as exceptions which occur due to connectivity or physical issues from a server error code failure. Normally, I would recommend ignoring onError and focusing on a global error handler using NetworkManager as exists in the boilerplate code in a new project.

Moving Forward

Let us know what you think of these changes and how we can further improve the syntax/utility of this API. I think there is a lot we can do to take it forward.

Two omissions in the API that I’ve run into over the years are:

  • Support for XML — this should be easy to add but there wasn’t much demand for it. If there is we can probably add XML support too

  • Image handling — we already have great Image download/caching API’s so we didn’t add them to this class. I’m not sure if this would be the right API to do image requests but it might

]]>
1,099 Rest API Error Handling 0 0 0
Ending Support for Legacy Cloud blog/ending-support-for-legacy-cloud.html Wed, 1 Aug 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/ending-support-for-legacy-cloud.html

Starting this weekend builds sent using the old plugin or an old project will fail. You will need to update the plugin to 4.3 (or 4.0.3 in NetBeans) or newer. This is an important phase in removing App Engine from our build stack and moving to a new system.

If you are experiencing problems with a project do the following:

  • Right Click the project

  • Select Codename OneCodename One Settings or Codename One Preferences

  • Click Basic

  • Click Update Project Libs in the bottom left side

What’s Next?

After this initial phase we plan to delete the app engine server. This will clean up our legacy system completely. It also means that if you created a Codename One account in the past and haven’t been active your account might vanish.

In that case you would need to create it all over again.

Why are we Doing This?

The old system based on App Engine is unmaintainable and fundamentally broken. Unfortunately, even basic features on app engine just don’t work so we can’t even keep this running.

Using our own servers has been liberating and far more powerful. This is a move we should have completed years ago. Once App Engine is completely out of the way we’ll be able to deliver some services we’ve had on the drawing board for years.

]]>
1,100 Ending Support for Legacy Cloud 0 0 0
TIP: Use the Two Phase Commit Algorithm for Offline Support blog/tip-use-two-phase-commit-for-offline.html Mon, 30 Jul 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-use-two-phase-commit-for-offline.html

I used to do a lot of enterprise consulting in the day and used to lecture a lot on J2EE (as it was known back then). A lot of that knowledge isn’t as applicable even in the server side today, but the algorithms are surprisingly even applicable in mobile.
One of the algorithms I would explain a lot when teaching J2EE was the 2PC AKA Two Phase Commit.

This is builtin to enterprise servers but I won’t go into that here, there is plenty of discussion about that online. What matters to me is how this can be used in a mobile context to build an app that’s friendly for offline/online modes.

What’s 2PC

To understand 2PC you need to understand what’s a transaction. I hope you do but just in case you don’t here’s an oversimplified definition: A transaction contains more than one operation where we want all the operations to succeed or we want all of them to fail.

This is explained with a simple example of a transfer between two bank accounts: If I transfer $5 from my account to my friends account I want the deduction of $5 to be in the same transaction as adding the $5. Otherwise if there is a failure money can evaporate and vanish!

Here’s where 2PC comes in, say my account is in one bank and my friends is in another bank in a different server (obviously this is more complicated, again over simplifying). How would that work?

The two phase commit splits every transaction into two phases:

  • Perform the operations of the transaction but don’t commit

  • Perform the actual commit

That way a system can send all the separate operations to all the servers (yes there can be more than two…​) and when all the servers say they can perform the commit it will notify them all to commit. There are still points of failure in this system but usually when it fails it will be more consistent.

Working Offline in a Mobile Device

Offline synchronization is one of the hardest things to do right in mobile. Especially in cases where your network is unreliable.

Some apps just fail and ask for a valid network connection. But there’s another way.

Assuming you have a cached local database you can let the user keep working locally and then synchronize the data with the server once you get access. This obviously carries several risks:

  • Conflict - user changes might conflict with changes going on in the server while he is working offline

  • Synchronization - you would need to somehow synchronize the changes to the server

  • Connection Reliability - if server synchronization fails the client and server might be left in an illegal state

That’s where the key ideas of two phase commit become applicable.

Command Pattern

Don’t be confused with the Codename One Command class. The GoF Command pattern represents a queue of tasks that allow us to perform the tasks in order. Assuming your server contains a set of REST calls a command would probably map to each call.

So if we proceed with the bank account transfer analogy I’ll have a command that says "transfer $5 from account X to account Y". The implementation of said command could just invoke the REST API. However, when we are offline we can just log the command to run later when we are online.

We can create a queue of offline commands and wait for the moment we get a stable server connection so we can send the commands in queue.

All, Nothing or Conflict

Here’s the tricky part. The server might have changed and the client might have multiple commands.

In an ideal world we can just perform the REST call for each command and be done with it. In practice this can make things worse. If the server changed we can get a conflict. We can also succeed with some operations and fail with others which can create an illegal state in the client.

The solution is to have a similar command abstraction in the server to support the offline mode. So when we go back online instead of sending the regular REST requests we’ll send a special set of commands e.g.:

  • /beginTransaction - would return a transaction id. It might also accept a timestamp which could be useful for conflict detection on the server. The server can use timestamps on database entries to indicate when they were changed. Your timestamp would indicate when you last fetched the database so if an entry was changed since you fetched the database you might have a conflict.

  • Send REST calls with special transaction=id parameter or HTTP header. These operations won’t commit but would return an error in case of a failure. Handling this error is the interesting part. You can do that per-command and let the user decide if this command is crucial or not. You can offer a UI to merge a command or discard it. You can offer to discard all local changes etc.

  • Assuming all went well you can send a commit call.

This sounds a bit difficult but once you divide things correctly and make them modular enough it isn’t very hard.

Summary

You need to think in advance about offline/online in some systems. If you are building a social network it isn’t a big deal if some data is stale or offline behavior isn’t perfect. But if you are building something that needs reliability you need to understand this theory.

Some tools try to abstract these ideas. That sometimes creates a situation of automatic merges (or failures) that don’t give the user enough control. Even if you use such a tool you need to have a decent understanding of the underlying logic.

]]>
1,101 TIP: Use the Two Phase Commit Algorithm for Offline Support 0 0 0
Increase your Build Quotas blog/increase-your-build-quotas.html Wed, 25 Jul 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/increase-your-build-quotas.html

The most common question we get about Codename One is: "Is Codename One Free". The direct answer is "Yes" but we don’t want to mislead. You can work with the open source code, which is just as free as any other project. But it’s not for the faint of heart…​
The build servers have quotas so we won’t go out of business. This is perceived by developers as "not free" but since no one else offers build servers I have an issue with that perception. To battle that perception we’re increasing the build quotas.

Sort of…​

To keep us sustainable paying users essentially pay for the free users. Worse, it’s only the pro/enterprise accounts that cover these costs.

So in order to increase the quotas we need your help. We’ll give you increased quotas for friends of yours that join e.g. you bring your friends and we’ll increase your build quotas respectively.

For every friend we’ll add 512kb to your jar size limit and 50 build credits. This is a permanent addition that will come into effect during the quota reset (it isn’t immediate). We’ll also count that friend as if he referred one person already so he’ll have more than the default count!
Currently this is capped off at 15 friends which should give you plenty of room to grow. These benefits are perpetual and aren’t dependent on your friends staying or paying. However, we reserve the right to revoke these credits for abuse of this system!

Here’s the kicker, we just updated all the users in the database currently to 1 referral by default. So all active Codename One users should already have a larger build credit by default. Due to technical reasons we can’t do it to old users that didn’t use Codename One since our migration from App Engine…​

How Does it Work?

You can invite friends by sharing a special URL which you can see in your console here under the Account tab. E.g. mine is: https://www.codenameone.com/index.html?ref=baa9d923-b26a-430b-a814-02cf55605231.

You might need to logout and login again to see this entry. It should be below the token

The important aspect here is the ?ref=baa9d923-b26a-430b-a814-02cf55605231 part. You can attach it to any html URL in this site including this page e.g.: https://www.codenameone.com/blog/increase-your-build-quotas.html?ref=baa9d923-b26a-430b-a814-02cf55605231 would be valid.

However https://www.codenameone.com/?ref=baa9d923-b26a-430b-a814-02cf55605231 isn’t valid as it doesn’t contain an html file in the URL.

Terms and Questions

Does this Apply to Paid Users?

Yes. If you ever cancel your account you’d revert to paid mode and still have your increased credits. If you signup for a paid program and downgrade you won’t lose any signups during the paid duration or after.

How does this Work?

The referral URL sets a cookie in the users browser that is active for 6 months. If during those 6 months the user signs up he is counted.
You would need to promote Codename One to your friends/social network to get the additional credits.

What if a User Clicks two URL’s?

The last one is counted. This is the industry standard.

How can I Monitor This?

Right now you will only know when the build credits are reset. However, we plan to introduce a UI that will let you see how many users you referred.

How do you Check for Abuse?

We only count activated users. We check user behavioral patterns through an automated system that alerts us of potential abuse.

Future Directions

We’d like to extend this to push notification as well. We have some ideas on how to provide push support for free/basic accounts but for that we would probably want to redesign our push servers.

That’s a bit of an undertaking and we’ll only start that off if this program proves successful.

I hope you all take advantage of this as much as possible!

A Word About the 1MB Jar Size

We’re doing this mostly to battle a perception issue. The JAR size limit should be enough for relatively demanding apps. It’s generally a good practice to stay within it.

The JAR size limit refers to the size of the JAR sent to the server. Not the one returned.

Native libs are essentially free since they are fetched from gradle/cocoapods. By default a clean hello world app is around 2-3kb and the Uber/Facebook clones fall well below the 1mb limit.

It’s a good limit to abide by. It means your builds will be faster and the end result application will be smaller. You shouldn’t need a larger app size.

Keep in mind that a 1mb jar app can grow up to 16 times on iOS and a bit less on Android.

]]>
1,102 Increase your Build Quotas 0 0 0
Uber Book Release Date blog/uber-book-release-date.html Tue, 24 Jul 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/uber-book-release-date.html

I’ve been working on the new "Create an Uber Clone in 7 Days" book for ages. After so much work this is finally almost done!
We have a release date: August 16th (yes 2018!). You can already pre-order the kindle edition but you’d get it for free if you buy the print edition which for some inexplicable reason can’t be pre-ordered.

UPDATE: Since writing this I’ve added a dedicated book site, I’d rather you share that instead of the link to this post. It’s available here: https://uber.cn1.co/

You can see the preorder page on amazon here. For now the digital book is an exclusive on Amazon. This provides some visibility benefits that are helpful at this stage.

I’m pretty happy with how the book turned out, it’s a huge leap over previous things we did. A lot of that is thanks to the huge effort of the multiple people who reviewed it over the course of its production. Unfortunately I don’t have access to the names of all the reviewers so if you aren’t mentioned here please contact me via the chat ASAP and we’ll fix that.

Late stage reviewers of the book were very helpful so far, specifically Francesco Galgani, Rémi Tournier and Steve Nganga. Steve Hannah put so much effort into his review I gave him editor credits for the book.

In that sense this book is a team/community effort so in that spirit I’m publishing the first two chapters and all of the appendices for free!

I’m publishing them in the PDF format which was developed for the print book. I would very much appreciate it if you spread the word on this. I’d prefer if you don’t send out the PDFs and instead send a link to this blog post so people can download the PDF from here. I don’t like those annoying sites that demand your email for download and we don’t do it here.

If you like what you read, the print book will be available for purchase on August 16th. Notice that the digital/kindle book has different formatting which is very noticeable in the source code listings.

This PDF is based on the print version, as a result it has asymmetric padding and layout. This allows elements to "bleed" to the edge of the page and provides a pleasant browsing experience when reading the printed documentation.

The first two chapters and appendices can be downloaded from here.

You can check out this article I wrote for Hackernoon. It explains some of the nitty gritty details of asciidoctor and professional book layout. So if you are curious why it took so long to get this book out, check out the before/after shots in that article.

]]>
1,103 Uber Book Release Date 0 0 0
Rich Push Notifications and Improved Validation blog/tich-push-notification-improved-validation.html Tue, 17 Jul 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tich-push-notification-improved-validation.html

Steve just implemented one of the harder RFE’s we had in a while. It isn’t finished but we can already try some of these features and you should be able to try some of these rich types of push messages.

The difficulty stems from the way these push messages are implemented differently in the native OS’s outside of the domain where we have full control. As part of that work our entire developer guide section related to push was rewritten here: https://github.com/codenameone/CodenameOne/wiki/Push-Notifications

It isn’t available yet in the developer guide as I’m still busy with the book and would need to do some work to incorporate it correctly but this is a huge leap forward for our push support.

We’ll have more announcements about this in the coming months.

Validation on iOS

Francesco Galgani implemented this pull request which animates validation errors in InputComponent on top of the Label.

This was merged and looks great but it did create a regression in validation where regular text fields fail at the moment and throw an exception. We might push a hotfix on Thursday to fix that issue.

]]>
1,104 Rich Push Notifications and Improved Validation 0 0 0
Migrating from App Engine to Spring Boot blog/migrating-app-engine-spring-boot.html Mon, 9 Jul 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/migrating-app-engine-spring-boot.html

Over the weekend we migrated a huge amount of code to the new build servers. In this post I’ll try to cover three separate things. I’ll explain the architecture/history and process of the migration. What worked, what didn’t work and lessons learned. And finally how this will impact Codename One users moving forward.

I’ll start with a bit of history to explain the background and motivations. We formed Codename One in January 2012. Back then Chen and I worked in full startup mode in an accelerator. Chen never worked in a startup before and was amazed by the pace of work, he quipped that we did more work in 3 months than we did in 5 years at Sun Microsystems!

The build cloud was built during that time but was refined over the past 6 years to suit our growing needs. We made the mistake of picking App Engine for the build cloud as we wanted to shorten the time to market while keeping the implementation scalable. The fact that Google offered free hosting credits for startups was also a big incentive.

The build cloud was built as a monolith, this isn’t a bad thing. In fact Martin Fowler specifically advises that people start with a monolith architecture first. The cloud server doesn’t actually do the builds, for that we need dedicated standalone servers (e.g. Macs, Windows machines etc). It essentially orchestrates all the disparate pieces so they will work together. In the past it handled a lot more but as the years went by we chopped out pieces one by one to move into a microservices architecture e.g.:

  • Push

  • Crash protection

  • Build file submission

  • Build server web UI

And more. We tried to remove everything since app engine is just SO bad but still quite a few things remained in the cloud servers:

  • User authorization/signup/activation

  • Build orchestration - submit, status, server assignment etc.

  • Billing - we don’t store billing information but PayPal notified the old servers on payment

  • Some of the transactional mail infrastructure

The first two are big pieces that include a lot of code. They are also deeply tied to everything else.

When we started off with App Engine we believed Googles claims that it supports JPA. To keep our code portable we used JPA so we’d be able to migrate away. As we discovered JPA support on App Engine is a sad joke. Basic functionality didn’t work and failed painfully, ironically these failures occurred only when the business scaled so instead of smooth performance degradation we got downtime.

As we moved ahead we rewrote a lot of JPA code to use the App Engine entity API and memcached. Thankfully we saved a lot of the old JPA code.

Over the past year updating the App Engine deployment became impossible as Google blocked the old plugin it used to support App Engine. The approach of migrating to a completely new project structure is poorly documented and seems like a huge risk as things might fail badly. So we had no choice.

How does it Work?

We’ve worked a lot with Spring Boot while developing the courses and upcoming book. The speed of development and ease of use is at a different scale altogether.

With that in mind we decided to do a live migration to a Spring Boot server that would work as a drop in replacement to app engine. To keep scalability in place we decided to use cloudflare (as we do on the main website). It makes scaling remarkably easy.

Because we already extracted the build UI from app engine in the past and now host it as part of our website, we have a clear API to the backend server. With that we could change all the calls that go into the app engine server and just point the exact same calls into the new cloud server. The cloud server decides whether it should handle a call locally or ask App Engine to perform the call for it. In this way we added a new layer for existing users, but it should be 100% compatible and shouldn’t fail.

The reason we had to take this approach is due to the plugins, since they are installed on the end user machines some might still point at the old App Engine. We don’t want all builds to break suddenly. We’d like users to move away gently from the old App Engine deployment.

Normally rewriting from scratch would have been easier but because we wanted as little disruption as possible we tried to setup the new Spring Boot server to be 100% compatible so we had to import code and try to convert the old mishmash of servlets with no tiers to a proper architecture that separates tiers properly. It still looks a bit messy as the original wire protocol is messy. But now that this is behind us we’ll be able to revisit this and improve a lot of the missing pieces.

What went Wrong?

A lot of things failed in unexpected ways.

The biggest problem we ran into in production was due to cloudflare. Cloudflare blocks DDoS attacks and one of the tricks it uses is blocking all traffic that doesn’t include the User-Agent header. A lot of our code uses the URL class and doesn’t add that header. So basic things worked great in the development environment but failed in production.

We migrated a lot of the old numeric keys as we moved from the datastore to SQL. So the new String based keys worked reasonably well but a couple of JavaScript client side functions used syntax such as: doThis(id).

This seemed to work but just failed silently when the key was a string. We had to add quotes to those methods to get it through. Since there are so many nuanced features in the platform we just missed testing this.

Some OTA and install features failed in production. This relates to relatively hidden/obscure code in the app engine implementation that we completely missed. In fact we used JSP for a couple of features in the old app engine. While Spring Boot supports JSP it has some deployment limitations so we just translated that code to a standard webservice call. Not an ideal solution but since there was so little code to port it was something we could address.

After working a bit Spring Boot would fail with RAM errors. Turns out we need to explicitly set the Xmx/Xms flags in Spring Boot using a conf file. We didn’t run into it in previous deployments as we didn’t do some of the heavy IO that we do here.

What Worked Great

So many things "just worked"!

The migration to SQL was mostly smooth and included only a few pitfalls/schema changes. Being able to use proper SQL instead of datastore is a huge step forward. It’s so much faster we don’t need memcached and get better performance to boot!

The truly amazing thing is the queries and 3rd party tools. This has boosted our ability to address issues tremendously.

Despite the User-Agent issue cloudflare is a huge asset. It makes caching repeated queries trivial. Better yet, since we now proxy some downloads through the servers we can get faster/more reliable downloads thanks to cloudflare.

I can’t sing the praises of Spring Boot enough, it makes this trivial. It has it’s pain points (unreadable huge stack traces) but the ease of development is amazing. We manage our own infrastructure now through IaaS. It’s easier, faster, cheaper and scales better than the previous PaaS deployment. Four out of four criteria.

While we didn’t test scaling to the full extent so far CPU utilization is flat/low. This architecture would probably scale much better than app engine ever did. Google sells App Engine as a "Google Scale" solution but anyone who worked with it will know that this only applies if you can spend "Google Sums" to pay for that. App Engine tries to scale by adding computing resources instead of just slowing down.

That means that if you have 10k active users you’d pay for a lot of servers to handle them. Our current solution can handle 10k concurrent users easily. It would slow down but wouldn’t crash. It would still be cheap thanks to Linode.

We also took the opportunity to move most of our transactional emails to mailgun. So if you get an email from us you will notice it uses a different domain. One of the big problems developers had with signup in the past was due to corporate inboxes relegating us to spam. We made some bad technical choices assuming SendGrid can help us fix these issues. This probably isn’t SendGrid’s fault as much as it’s our lack of understanding in this field.

We decided to start a new leaf with a new domain for the emails. We didn’t move everything there and I’m not sure if we ever will as I’m concerned about deliverability. Regardless this is seems to be a good move as we have 100% deliverability so far.

How could this Impact You

I already discussed some of these here. But there is one additional entry in the pile: IPN.

We handle our subscriptions via PayPal. We’d love to add other options but there are logistical issues with global deployments for all of them. We don’t want any billing data on our servers as we don’t want to deal with that complexity.

PayPal billing can work via a solution called IPN which means paypal invokes us on every billing. That way we can update our user database based on payment received. So far so good.

Unfortunately you can’t change an IPN address after it was set. So existing subscribers still point to the old app engine URL. We have a workaround of polling app engine for subscription level for existing subscribers. This only impacts current paying users and might cause a situation where your subscription appears to have 2 days left even if it has more.

We plan to migrate the project in app engine so the IPN will be the only remaining piece and it will point back to our server. To do that we’ll need to bring app engine offline and set it up with a new project. That will take time. So for now this workaround is in place.

We plan to disable app engine builds by the end of the month. That means you would need to update your plugins to the latest in order to build. If you don’t you would get an error. We would still not remove app engine as a few other features are harder to migrate over.

What’s Next?

Now that this is in place we can finally implement some cool features we’ve been craving…​ Higher build quotas for free users including features such as push notification etc.

All of these would be coming in the next few months…​

I don’t want to announce dates as I’m still working on the book and we need to push out Codename One 5.0 but it’s coming and hopefully before 5.0 which is now slated for September.

]]>
1,105 Migrating from App Engine to Spring Boot 0 0 0
New Build Cloud blog/new-build-cloud.html Mon, 2 Jul 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/new-build-cloud.html

This is important! We will replace the entire build infrastructure of Codename One over this weekend. That means that you might see disruptions in service through the weekend but please report them to us as we might not be aware!
We are finally removing the last remaining pieces of the horrible mess that is Google App Engine from our backend code. This is a huge job and is sure to cause some disruption.

In the long term this is great news. It means our servers will be modernized. As a result they will be better equipped to adopt new features that we’ve rejected in the past due to the age of the infrastructure. The downside is this bump in the road.

A huge part of the difficulty is switching the servers while avoiding disruption and letting you all upgrade your plugins at your own pace. We tried to create the new server in such a way that it would proxy into the old server so builds would seamlessly work even if you still use an old plugin. This should work great for simple cases but might introduce issues with edge cases. E.g. if you have two machines and only updated the plugin/libraries in one of them.

This might create regressions as the infrastructure is quite complicated but we hope we can resolve them quickly now that we have better control over the server process.

A Few Things Will Change

As part of this change we need to change/deprecate some niche features that haven’t been used as much:

  • Log.getUniqueDeviceId() will return -1 after this update and will no longer work for new builds. You will need to switch to Log.getUniqueDeviceKey() which returns a unique string

  • AnalyticsService will now default to app mode. The old web mode is now officially deprecated and will stop working in the future

  • Cloud email which is used via the sendMessageViaCloud API will no longer work at some point (it does work now but we will eventually retire it). If you need an equivalent API we will introduce something via a cn1lib such as SendGrid integration etc.

Other than that this change should be almost seamless for your app logic…​

No New UI Right Now

This change won’t change a lot in terms of features right now. In the near future this will enable us to completely rebuild the UI of the build server. We have some big plans for that and I hope we’ll be able to deliver on them now that we have better control.

But first I need to finish the work on the damn book. After this whole side track on the server. While we’re on the subject, thank you all who sent feedback on the first chapter it was super helpful!
I hope to have the second chapter out and about this week, it will take a while to complete everything as I’m going top-to-bottom through the book and it will take a while to finish the appendices.

Once the book is done I’ll publish the first two chapters and the appendices here for free.

]]>
1,106 New Build Cloud 0 0 0
Moving Away from Intercom blog/moving-away-from-intercom.html Wed, 27 Jun 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/moving-away-from-intercom.html

A few years ago a consultant convinced us to integrate Intercom into our website. In retrospect this was a mistake which I’ll discuss in more depth below. We are migrating away from Intercom right now…​ That means that if you have an email address or ongoing chat history with us in Intercom it might get lost!
Worse. If you unsubscribe this might also get lost due to the migration process (sorry about that!). We’re moving to a new far better system (crisp).

What’s Intercom

It’s the chat widget at the bottom right portion of the website.

It also handles the emails we send out offering help or pointing out information about Codename One. We use it to send out email announcements etc. It’s useful because when you reply to an email or chat it all goes to one place so we know who we’re talking with (sort of, it doesn’t do a great job). E.g. if you use the chat wizard then answer through email for us it’s one interface.

Why does it Suck

Intercom is a bad product and to make things worse it’s expensive. It does the basic things reasonably well but it’s UX and UI are very badly designed for us and our audience.

Despite Intercom’s many shortcomings we were lazy on the issue of migration as there is always something more important to do. Thankfully Intercom decided to change their pricing model so we’d pay 5x the already inflated price which was the perfect incentive for us to discover crisp which is far cheaper and seems like a better product altogether.

With this you should already see the new chat widget below and should be able to interact with us there…​ You can also check out the new https://help.codenameone.com/ website that was generated as apart of the process. Hopefully we’ll strengthen it with a bigger knowledge base.

Where Next

Unfortunately this is just the tip of the iceberg. Intercoms code is embedded deep into the backend systems. The problem is we still have some app engine servers running and it’s really hard to update them as Google effectively killed off the system we were using. So we are finally doing what we procrastinated on for 3 years and removing app engine completely from our stack!

This is a HUGE move, I can’t over state it. All our user logs and everything is in app engine. That might mean that if you had an old "dormant" account that you haven’t used in years it might get deleted in the migration as we won’t be able to migrate it. That’s not a big deal since such accounts would typically be free accounts and you could just re-create that account. The bigger benefit is that we would be able to implement a lot of the features we always wanted to and couldn’t because of the problems in our backend!

You might notice some kinks in the migration let us know in the comments or the chat if things don’t work well. Setting up the automated emails from scratch will be a nightmare but it has to be done.

This might delay some things such as the book release, but we are making progress there…​

I’m sending out an email with some more details there if you are interested. It will be the first email to go out with the new crisp system so hopefully this works out nicely. If you don’t get it let us know in the chat and we’ll try to track that with you.

Codename One 5.0

This isn’t directly related to that but with this overhaul the decision is even more important. We decided to bump Codename One 5.0 to September 2018 instead of the current July release date.

We think features such as JDK 9/10/11 support is crucial with the new JDK release cycles. I’d also like Codename One to work with OpenJDK which is now a more stable target. Since these things require a lot of testing over time we think this new release date is crucial.

]]>
1,107 Moving Away from Intercom 0 0 0
ToastBar Return Value blog/toastbar-return-value.html Tue, 26 Jun 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/toastbar-return-value.html

Last week I pushed out an enhancement to ToastBar that changed the static showMessage methods. I made them return the Status object instead of void which would allow more control of the toast message after it’s shown. Unfortunately, I totally forgot that I can’t do that without breaking some binary compatibility.

In Java return types create a distinct method signature, so even though the language doesn’t allow you to do this:

void myMethod() {
}
int myMethod() {
    return 1;
}

This is actually valid in the bytecode level. Furthermore, it’s a used by the JVM to implement Java language features like covariant return types.

So if you used ToastBar and compiled against older libraries you might experience issues when building. Make sure to update your client libraries before sending a build by going into Codename One SettingsBasicUpdate Client Libs. Then do a clean/build before sending a new build.

]]>
1,108 ToastBar Return Value 0 0 0
Native Logging and Certificate Wizard Downtime blog/native-logging-certificate-wizard.html Tue, 19 Jun 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/native-logging-certificate-wizard.html

I’ve been so busy with the book I completely missed a lot of things I should have blogged about and one such thing is the NativeLogsReader cn1lib which has been in the extension manager for a while now.

The NativeLogsReader cn1lib was created by Francesco Galgani to include native logging into the Codename One log. A lot of times we get on device failures that are really hard to track. In those cases we ask users to connect cables and try to view the native logs to search for clues. With this library you can see native output even without physical access to the device!

That’s really helpful when you’re tracking an issue that happens on an end user device.

Certificate Wizard Issues

We’ve had several cases of downtime with the certificate wizard lately and we are going through such a downtime right now. We are working on fixing it and hopefully it will be fixed by the time you read this…​

However, I’d like to explain why these things happen. The certificate wizard connects to Apples undocumented system to support generating certificates/provisioning. It’s a system they have in place for xcode but it’s a bit flaky. We could just use something like webscraping in the worst case scenario but either way every time Apple makes a change we need to adapt.

A while back Apple made a change, we adapted relatively quickly but introduced a few regressions which were really hard to pinpoint as they relate to behaviors such as cookie policies etc. Things that work for our localized test cases sometimes fail as we scale them to the entire community…​

Hopefully, this round of whack-a-mole will be over soon.

Book Update and 5.0

As usual producing the book is taking way longer than planned but I’m getting there. I’m really excited about what we have so far and can’t wait to share it with you guys.

On a related subject we also need to update Codename One to run on newer JDK’s 9/10/11 all of which broke so many documented features in the JDK that we depend on. This is crucial as JDK 8 will EoL in 2019.

With those two things in mind we decided to postpone Codename One 5.0 to September instead of its current July target. This will give us time to address these issues and give me a bit of time to do some "actual work" that doesn’t revolve around the book.

]]>
1,109 Native Logging and Certificate Wizard Downtime 0 0 0
Uber Clone Book blog/uber-clone-book.html Wed, 13 Jun 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/uber-clone-book.html

I’ve been working on a book form of the Uber Clone module in the course. I finished the principal writing quite a while back but unlike the course a book requires more back and forth. I’ve already gone through the first review cycle and it has made the book much better as a result. There is still a lot to do as I would like the result to be sublime.

As part of that process I’ll publish the first few chapters of the book here which I hope would be helpful and provide feedback from you guys…​

The first couple of chapters are an introduction to Codename One and I think they are far better written than anything we have out there right now.

People who purchased the full online course will get free access to the book within a couple of months after publication (once exclusivity expires). At that time I will embed the book into the course with the other materials.

While the writing itself is quite easy the production aspect is a pain in the neck. Hopefully this book will succeed and if it does I’ll keep doing additional ones which should be easier now that I have the process mostly ironed out.

]]>
1,110 Uber Clone Book 0 0 0
Right SideMenu and Tab Order blog/right-sidemenu-tab-order.html Wed, 6 Jun 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/right-sidemenu-tab-order.html

As I mentioned the other day, we have a lot of new features and announcements. Today I’d like to discuss the upcoming right side menu bar and new tab order functionality.

Right SideMenu

The old SideMenuBar supported the placement of the menu anywhere we want. We could place it in the left/right/top or bottom. The problem was that this support was painfully unreliable and inconsistent.

When we migrated to the Toolbar API we ignored that support and only included the left side menu support. For some of us this isn’t a big deal. Just a small limitation. However, if your app is localized to an RTL language this might be a problem.

What’s RTL

RTL stands for Right to Left to indicate languages written in the opposite direction to most western languages. Typically these are old languages specifically:

  • Arabic

  • Aramaic

  • Azeri

  • Dhivehi/Maldivian

  • Hebrew

  • Kurdish (Sorani)

  • Persian/Farsi

  • Urdu

Historically, these languages date to a time when writing was engraved with a hammer and a chisel. It was easier to hold the hammer in the right hand (the more dominant hand for 90% of the population) and thus write from the right side.

As ink and paper took over languages that didn’t have a writing system before picked a direction that wouldn’t smear the ink.

The problem of RTL languages is far more difficult as numbers or other languages are still written from left to right. This is called: bidi (bi-directional). That means a sentence starts from the right, skips to the left to show a number then goes back.

Because RTL languages are read from the right side users expect the entire UI to be reversed. That means all UI elements should be aligned to the right and all components should be in the exact flipped order. This is done automatically in Codename One see the i18n tutorial.

Up until now we couldn’t do this for the side menu but now that Francesco Galgani introduced support for right side menu in his PR and followup PR this is finally possible!

With next weeks update you could invoke a version of the add to side menu method that lets you add an element to the right side.

Input Cycle

Codename One has its roots well before the move to touch devices. In the old devices one would navigate with 4 direction buttons and our entire focus system revolves around that. This is a powerful system as it lets us work with unique input devices such as TV’s etc.

However, it has some limitations specifically with the virtual keyboard cycle. When we edit a field and press NEXT we would expect the keyboard to move to the next text field. That’s easy. But what if the next field is a Picker?

Now you can override this behavior so the next element will work correctly, we were also able to simplify this similarly to the way that tabIndex works in HTML as this system doesn’t need the full functionality of the original focus code.

Tab Index

Component now has a preferredTabIndex property which specifies the tab order of a component. There are 3 types of values that you can set here.

  • A value of -1 means that the component is not tab-focusable (i.e. you will never get to this field by tabbing or hitting Next/Prev in the keyboard). This is the default value for all components except TextArea, and Picker - which are currently the only components that are tabbable.

  • A value of 0 means that the component is tab-focusable, and the order will be dictated by the logical document order. (The logical document order is defined in the layout managers).

  • A value greater than 0 explicitly sets the tab order. Be careful with this as if you mix explicitly set tab orders (i.e. tabIndex > 0) with implicitly set tab orders (i.e. tabIndex == 0) in the same form, you may get unexpected results.

If you want to query the form for tab order, you can use any of the new methods on Form.

Finding Next/Prev Components.

  • Form.getNextComponent(Component current), which gets the next component by tab order given some currently focused component.

  • Form.getPreviousComponent(Component current), which gets the previous component.

  • Form.getTabIterator(Component current), which gets an iterator that allows you to iterate through all of the tabbable components in the form in their tab order, starting at some currently focused component.

Making a Component Tab-Focusable

If you are designing a new component that should be tab-focusable, you should just set the preferredTabIndex to 0.

cmp.setPreferredTabIndex(0);

If you want to make the component so it won’t be tab-focusable, just do

cmp.setPreferredTabIndex(-1);

There is also a convenience method setTraversable(boolean) which wraps these calls to make them more intuitive. E.g.:

cmp.setTraversable(true) is the same as calling cmp.setPreferredTabIndex(0), and cmp.setTraversable(false) is the same as calling cmp.setPreferredTabIndex(-1). Default Tab Orders

It is recommended that you just stick with preferredTabIndex=0 if you want your component to be tabbable, because it’s a lot of work to explicitly declare tab order for all of your fields. Then the tab order will be automatically calculated based on the logical order of the fields on the form. This default ordering is dictated by the layout manager. For indexed layout managers (e.g. BoxLayout.Y, BoxLayout.X, GridLayout, FlowLayout), the tab order is simply the component order in the container. Constraint-based layout managers like BorderLayout and TableLayout define their own orders that make logical sense. LayeredLayout defines an order based on the x,y coordinates of its children, roughly moving top-left to bottom-right.

If you are developing your own layout manager and want a default tab order that is different than just the component order within the container, you should override the following methods:

overridesTabIndices(Container) - Return true to indicate that the layout manager provides its own traversal order.

getChildrenInTraversalOrder(Container parent) - return array of Components in the parent in the order that they should be traversed. This returned array doesn’t need to include all of the child components, only the ones that should be considered in traversal - but it may also include components that shouldn’t be included in traversal, as the ineligible components will be filtered out at a later step. In fact some of the elements of this array may be null, and those will be filtered out.

See BorderLayout for an example or TableLayout for another example.

]]>
1,111 Right SideMenu and Tab Order 0 0 0
Set Properties, Density PR and Short Material Icon Syntax blog/set-properties-density-pr-short-material-icon.html Tue, 5 Jun 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/set-properties-density-pr-short-material-icon.html

I have a lot to write about so today I’ll only focus on two of the several PR’s we handled over the last month. I’ll try to cover more over the rest of the week. Also as a friendly reminder we will migrate to API level 27 this Friday and the price of the online course including the Facebook/Uber clone apps will go up next week…​
If you didn’t sign up yet this is your chance.

Set Properties

ramsestom contributed a pull request which filled in a gap in the properties API: SetProperty. I don’t use Set very often as List is typically more convenient so it’s something that I just didn’t notice.

You can now declare SetProperty as you would a declare a ListProperty e.g.:

public final SetProperty<User, User> peopleYouMayKnow =
        new SetProperty<>("peopleYouMayKnow", User.class);

The nice thing about this PR is that it also refactored the code in a sensible way so SetProperty & ListProperty have a common base class CollectionProperty so there isn’t too much code duplication.

Density PR

ramsestom also made a more challenging change to the way densities are managed. When we originally integrated this it caused a performance regression on Android which demonstrates just how hard it is to work with such huge PR’s.

This PR is still conflicting and I don’t have the time to go over some of the nuances that should be fixed there. It’s not something I want to merge at the last minute either as it’s a huge change. Ideally if this could be broken down to smaller PR’s we can adopt individually it would be far easier to merge again.

One crucial thing to keep in mind when you submit a PR is binary compatibility. Method signatures must match completely or things will fail badly for some users. This is also true when introducing new API’s, once we accept a PR we need to maintain it for years to come.

Short Material Icon Syntax

One of the tricks I’ve been using recently is:

import static com.codename1.ui.FontImage.*;

This lets me do things like:

setMaterialIcon(send, MATERIAL_SEND);

That’s short and to the point. But why require a static import?

So I’ve added this method to Label (which is the base class of Button etc.):

send.setMaterialIcon(FontImage.MATERIAL_SEND);

That might not seem very different but it carries one big nuance that will appear in the update this Friday. When you make changes to the theme they will be reflected in the material icon.

]]>
1,112 Set Properties, Density PR and Short Material Icon Syntax 0 0 0
Moving to API Level 27 and Facebook Clone is DONE! blog/moving-to-27-facebook-clone-done.html Thu, 31 May 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/moving-to-27-facebook-clone-done.html

This is important: We’ll update the build tools & build target to API level 27 with next weeks update (June 8th). We HIGHLY recommend you check your app before we flip the switch!
I also just uploaded the final lesson in the Facebook Clone App which is now fully live in the online course.

I’ve discussed the build target 27 migration before. We wanted to do it in May but the work on the Facebook clone pushed that back so it will have to wait. You can test this right now by setting the build hints:

android.buildToolsVersion=27
android.targetSDKVersion=27

If you would like to go back to the previous level after the change you can set them to 23 although I’d strongly recommend against that. Google will require at least 26 starting in August and is already producing warnings if you try to submit updates to apps right now.

I answered a lot of FAQ’s about this the last time I wrote about this.

Facebook Clone is GA

I was racing against the clock to get the Facebook clone out of the door and made it just now. In the past 48 hours I recorded the last 20+ lessons to finish the 46 lesson long module. I have a lot of thoughts about the results and insights about Facebook after doing this…​

I’ll try to write an article similar to the one I wrote about Uber detailing some of my experiences here.

I mentioned before when we launched the Uber app that the price of the course will go up by 100USD to $599 once the Facebook clone is up. Since it was late we’ll postpone this price change to June 12th so if you still didn’t signup for the course nows a good time.
We’ll raise the price further in the future as we add things to the courses but probably not by the same amount.

I’ll send out a couple of reminders for this by email during the week.

We Updated our Privacy Policy

Sorry about that. I know it’s a pain I think we all get enough emails without that one…​

I’m personally a big fan of the ideas behind GDPR & I think it will ultimately help in refining better business models that don’t rely on spyware tactics. For the record, we never tracked anything or used personal data in any way. It’s not a part of our business model.

We use a couple of 3rd party GDPR compliant services (Intercom & Google Analytics), that’s pretty much it.

Anyway it’s a minimal privacy policy because we respect your privacy and don’t need to look at details. If you think we need to explicitly state something there that isn’t mentioned let us know.

There is a lot of additional news I’d like to cover but I’ll try to get to those next week.

]]>
1,113 Moving to API Level 27 and Facebook Clone is DONE! 0 0 0
Lightweight Picker, Device Detection and More blog/lightweight-picker-device-detection.html Tue, 22 May 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/lightweight-picker-device-detection.html

One of the worst components in Codename One is the picker component. It’s origin lies in the migration to iOS 7 where the native picker introduced a 3d effect that was hard to replicate with our old graphics layer. We had no choice. We used a native widget for that picker and regretted that decision ever since.

It looks bad on the simulator, it misbehaves and with every update from Apple things break. This has again proven to us the importance of the lightweight architecture of Codename One!

Since the introduction of the picker our graphics layer was heavily revised enough to support something as elaborate as the iOS picker UI. With that Steve spent a lot of time doing a from scratch implementation of the iOS picker UI which you can try right now.

New Picker on the Simulator
Figure 1. New Picker on the Simulator

The new implementation is on right now!

If the platform supports native pickers it will use the native picker UI. If it doesn’t it will default to the lightweight UI.

You can force the lightweight mode by invoking: picker.setLightweightMode(true);.

Since picker is native by default the UI changes in iOS/Android where we get the native Android picker interface. Right now we only replicated the iOS UI, ideally we’d do the same for the Android picker although the urgency is much lower as it’s surprisingly more robust than the iOS picker API.

As part of this implementation Steve introduced some API’s such as a scene-graph API which is currently marked as deprecated as it’s still under development. If there is some interest in it we might explore some of those capabilities in the future.

Device Detection

On a different note…​ We hid most of the device detection functionality in Codename One to prevent the situation where developers write code such as:

if(deviceX) {
} else if(device Y) {
}
//....

This starts as a quick bandaid to a problem & snowballs into something that’s unmaintainable.

Having said that there are some valid use cases for device detection such as statistics & analysis. There are some edge cases that can only be solved in this way e.g. on iOS it’s often impossible to calculate the accurate device DPI without detection code.

With that in mind Diamond introduced this cn1lib which is available in the extension manager. It includes native device detection code that would you to detect the device. Hopefully this will be used for good.

Facebook Clone Update

I’m past the 50% of the content mark (roughly at 65-70% by my estimate) but that doesn’t leave me that much time. Currently I have 25 lessons nearly ready and 17 out of them are already published. I estimate this will take another 25 lessons to complete since I still have a lot of material that’s prepared but not segmented. For comparison the Uber clone module took 40 lessons to complete.

To be fair, these lessons are shorter as I get to the point faster and I improved the material presentation significantly. Either way I’ll need to pick up the pace if I hope to release this module before the end of the month.

]]>
1,114 Lightweight Picker, Device Detection and More 0 0 0
Live CSS Update blog/live-css-update.html Tue, 15 May 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/live-css-update.html

Up until last Friday CSS support has been a second class citizen, with the release of Codename One 4.2 we’re making CSS a core feature. We also improved the builtin CSS support extensively!
Changes you make to a CSS file will instantly reflect in the simulator. You don’t need to compile or do anything special. When you launch the simulator we open a CSS watcher thread that automatically recompiles & deploys the CSS whenever you save.

Installation is also much simpler. You can click the CSS entry in Codename One Settings & activate CSS. That’s it!

Migration doesn’t migrate your theme! You would need to redo the theme in CSS
Disabling CSS after enabling it might have some issues, if you experience that remove the entry codename1.cssTheme from codenameone_settings.properties

If you have a project that uses the older CSS support the settings app is smart enough to recognize that and offer to migrate to the new CSS support.

The CSS Option in Codename One Settings
Figure 1. The CSS Option in Codename One Settings
These changes might need an update to the build.xml file. Make sure to update it when you save the file

Other Improvements in CSS

Other than these great new features CSS has improved by leaps and bounds. One of the biggest benefits of the new CSS processing logic is speed. It’s much faster. The trick behind that is simplification of the process for resource file generation.

The CSS plugin occasionally uses the webkit browser from JavaFX to generate an image of the CSS style. E.g. if a complex gradient is used the CSS processor just fires up webkit and grabs a multi-image screenshot of this style.

This is powerful as it allows for complex CSS styling, but it has many pitfalls such as slower compilation, lower fidelity & larger resources.

The newer CSS version works with some of our new border types such as round border. But the bigger improvement is that it doesn’t launch the browser window unless you actually need it. This results in faster compilation times for the CSS.

What’s Still Missing

The biggest piece we need to do is migrate the documentation to the developer guide and update this everywhere.

Another big missing piece with CSS support is localization. It’s not a CSS feature but rather something we would expect to have within the generated resource file. So java properties files would be implicitly added to the resource file during CSS compile. Personally I think we can make localization much easier by detecting unlocalized strings in the simulator & automatically adding them to the resource bundle.

We need some more demos & tutorials that cover CSS and ideally we would want this exposed in the "new project wizard".

If we could automate the conversion of res files to CSS it would be great for things like this as we could instantly make all of our demos work both ways.

The Big Picture

The obvious question here is: are we replacing the designer tool with CSS?

Not yet.

Though this is something I’m personally conflicted with. The designer tool is showing its age so it makes sense to minimize its usage. I don’t think we’ll deprecate the designer soon as it’s still convenient to work with.

Personally I find the designer more convenient probably due to habit. However, CSS has a few big advantages, for me personally the biggest advantages is documentation. I did the Uber clone app using the designer tool and while that was pretty easy to implement, the tutorial became untenable…​
I had to grab screenshots of every UI setting and if I wanted to revise something later I had to redo the screenshots. This was sometimes complicated as it required reverting existing changes to make the shots real. Since CSS is based on code it’s far easier to walk through the CSS changes I made like I would do with any other block of source code. That’s why the Facebook Clone uses CSS and it’s indeed far more convenient in that sense.

]]>
1,115 Live CSS Update 0 0 0
Spatial, Pluggable SQLite blog/spatial-pluggable-sqlite.html Tue, 8 May 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/spatial-pluggable-sqlite.html

One of our enterprise accounts is working on a complex GIS application that needs fine grained control over mapping. In this case features such as native maps aren’t useful. For some GIS applications the old MapComponent is more useful as it allows working with domain specific data. One of the features he needed was spatial support in the builtin SQL database.

When we initially implemented sqlite support we just delegated all the SQL calls to the OS native equivalents and called it a day. This works well for 98% of the cases but there are two big use cases that are missing by default in Android & iOS: Spatial queries & Security.

SpatiaLite allows you to use geographic locations in SQL. You can effectively query based on physical location which is surprisingly hard to do accurately without such an API. For simple cases like an Uber app you wouldn’t necessarily need something like that but for the more elaborate use cases it might become essential.

The SQL database in iOS/Android isn’t encrypted either, this isn’t a big deal for most applications but for some highly secure implementations that might be a hindrance. There is builtin support for encryption in SQLite that can be turned on as well.

A Pluggable Solution

We needed to solve the first problem so Steve created the Spatialite cn1lib which is now available in the extension manager. The cn1lib extends the Database class & allows you to plug-in a custom version of sqlite to replace the Database.

This approach can be extended to support other use cases for security and potentially other capabilities we aren’t aware of.

As a side note this took a lot of effort to build. That’s the type of work we do for enterprise customers!

Facebook Module Status

With a quick pivot I’ll also provide a quick update on the facebook clone module. We now have 8 lessons in the module and I’ve been adding more on a daily basis. I’m pretty sure we’ll have way more than 30 lessons so I’ll try to pick up the pace!

Here is another sample of the new post Form:

New Post Form in The Facebook Clone App
Figure 1. New Post Form in The Facebook Clone App
]]>
1,116 Spatial, Pluggable SQLite 0 0 0
Facebook Clone Slow Landing blog/facebook-clone-slow-landing.html Mon, 30 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/facebook-clone-slow-landing.html

I mentioned before that I’m really behind on the Facebook Clone module but at least I was able to release the first few lessons by today which makes it fit into an April deadline. I’ll try to release new lessons every day so we can do have the full module out before the end of May. At that point I’ll release two new modules during June.

The new Facebook Clone is similar to the Uber clone in some regards and also very different:

  • I didn’t do a "pixel perfect" clone - Facebook is too messy to clone properly

  • I used CSS instead of the designer tool - it’s easier to tech CSS as I can go over code instead of screenshots

  • I put more logic in the server and tried to make the app more "real"

The things I kept in place are:

  • MySQL - it’s a pretty good database and easy to work with when compared to no-SQL solutions. In fact it’s also WAY faster than most of them for real world loads. I see a lot of startups buying into the no-SQL hype which makes sense for some cases but usually doesn’t

  • Spring Boot - I chose to use version 2.0 which went GA by now. It’s a great option for Java developers!

One of the things I’d love to do is port this to Kotlin, maybe as one of the extra modules to be discussed later.

I’ll leave you with a couple of screenshot studies of the original vs. clone:

Original vs. Clone Portrait
Figure 1. Original vs. Clone Portrait
Original vs. Clone Landscape
Figure 2. Original vs. Clone Landscape
]]>
1,117 Facebook Clone Slow Landing 0 0 0
Version 4.1 and Launch Screen Storyboards blog/version-4-1-launch-screen-storyboards.html Tue, 24 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/version-4-1-launch-screen-storyboards.html

This weekend we pushed out an update that also included new versions of the GUI builder and the designer. We didn’t update the plugins but we still think it warrants the 4.1 version moniker even though we don’t support it in versioned builds. Due to one of the enhancements we added in this update we had a regression in command behavior that we fixed with an update within a few hours.

This regression was caused by new features in the Command class that now has the ability to set the material icon. Unfortunately we neglected to add this into the equals method which cascaded into a hard to track issue in toolbar handling. This impacted people using the addMaterialCommand API’s which also included the GUI builder. The update framework allowed us to issue a quick update and resolve that issue.

Launch Screen Storyboards

With the shift to Xcode 9, which is the default version on the Codename One build servers as of February 2018, it is now possible to use a launch-screen storyboard as the splash screen instead of launch images. This will potentially solve the issue of the proliferation of screenshots, as you can supply a single storyboard which will work on all devices. Launch storyboards are disabled by default at this time, but we will flip the switch for 5.0 so they will be the default. You can enable the LaunchScreen storyboard by adding the ios.multitasking=true build hint and explicitly disable them by setting it to false.

The build hint is called "multitasking", because iOS' split-screen and multi-tasking feature requires that the app uses a launch storyboard rather than launch images.

Besides the advantage of multi-tasking support the launch screen storyboards have a few advantages:

  • It’s the official direction - Apple is clearly heading in this direction instead of splash screens so it’s more "future proof"

  • Faster builds - by now we need to generate 16 screenshots per build, this can slow down builds noticeably

  • Smaller binaries - some of these 16 screenshots can be very large

  • Native widgets & OS fidelity - the screenshot process has a lot of problems most noticeably it fails if you use a native widget in the home screen. These problems go away with this approach

Launch Storyboard vs Launch Images

A key benefit of using a launch storyboard right now is that it allows your app to be used in split-screen mode. Storyboards, however, work a little bit differently than launch images. They don’t show a screenshot of the first page of your app. The default Codename One launch storyboard simply shows your app’s icon in the middle of the screen. You can customize the launch screen by providing one or more of the following files in your project’s native/ios directory

  1. Launch.Foreground.png - Will be shown instead of your app’s icon in the center of the screen.

  2. Launch.Background.png - Will fill the background of the screen.

  3. LaunchScreen.storyboard - A custom storyboard developed in Xcode, that will be used instead of the default storyboard.

Make sure to add the ios.multitasking=true build hint, or your launch storyboard will not be used.

Facebook Clone Status Update

It seems pretty clear I won’t finish the Facebook Clone work by the end of the month…​ My newer course modules have been far more thorough than my earlier work and take much longer as a result…​

The upshot of this is that it already looks pretty amazing, besides the app itself I’m very pleased with the materials I’ve produced so far and I think content creation will reach a new level.

Unlike the Uber Clone, I decided to use CSS for the Facebook Clone app. I still prefer working visually with the designer but explaining CSS in a presentation is simpler. For the designer I need to go over multiple UI screens and explain everything which is ultimately slower.

As part of that I asked Steve to add a few enhancements to the CSS support which is now noticeably faster. Hopefully this is something we can further improve so it will provide the same benefits we have in the Component Inspector tool.

]]>
1,118 Version 4.1 and Launch Screen Storyboards 0 0 0
iOS Back Command Behavior and Facebook Clone Update blog/ios-back-command-behavior.html Tue, 17 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/ios-back-command-behavior.html

I’ve been working on the new Facebook clone app, I have a lot to say about that but I’ll defer that for now. One of the things that Facebook did is provide a different experience in iOS & Android. I wanted to replicate that by using a more iOS style back behavior in my clone.

Back Command iOS/Android & Facebooks native on Android
Figure 1. Back Command iOS/Android & Facebooks native on Android

On Android we use an arrow to indicate back but on iOS we usually show the title of the previous form with a < icon to indicate the back action. Up until recently this was represented in the BackCommand UIID with a an image aligned to the left of the command. This was hard to style and made very little sense.

With the next update we’ll use the builtin material icon for this when you set a back command. In this form I used the following code for back:

getToolbar().setBackCommand(backLabel,
        Toolbar.BackCommandPolicy.WHEN_USES_TITLE_OTHERWISE_ARROW,
        e -> previous.showBack());

The backLabel variable represents the title of the previous form. The toolbar policy indicates an arrow will be used on Android and a Title will appear on iOS.

This will work with the new material font code in the next update (you need to update the skins too). You can disable or force the font image behavior with the new theme constant iosStyleBackArrowBool=false.

Material Icon Commands

As part of that work we added three new methods to Command:

public void setMaterialIcon(char materialIcon);
public void setMaterialIconSize(float size);
public void setIconGapMM(float iconGapMM);

These allow control over the gap between the icon and the label for the command element as well as set the icon as a material icon.

We updated a lot of the Toolbar code to use these internally so UI will adapt better to changes in UIID’s.

Landscape UIID’s

setUIID is a core API that hasn’t changed in ages. We just added a new version of this API that accepts two strings. One represents the UIID in portrait and the other (optional one) can represent a different UIID for landscape.

If they are the same the other string should be null

This allows us to easily implement minor UI changes that make sense when switching orientation e.g. smaller font/padding in the title area. Since that is the chief use case we also added a theme constant which is currently false by default: landscapeTitleUiidBool=true.

When you set this to true the UIID’s: ToolbarLandscape, TitleCommandLandscape, BackCommandLandscape &`TitleLandscape` will be used for landscape mode. They all derive their non-landscape versions so just setting the flag to true should have no effect. You will need to edit these styles to support this behavior.

Container UIID & Font Api’s

While we are on the subject of UI enhancements we also made a few enhancements to the general API.

Container now accepts a UIID in the constructor with the layout. It’s one less line of code when you create a container.

We moved the constants from Font into the CN class and added a couple of helper methods to create fonts more easily. Specifically:

public static Font createTrueTypeFont(String fontName);
public static Font createTrueTypeFont(String fontName, float sizeMm);

So instead of doing:

Font n = Font.createTrueTypeFont("native:MainLight", "native:MainLight").
    derive(convertToPixels(3));

You can do:

Font n = Font.createTrueTypeFont("native:MainLight", 3);

Summary

I hope we won’t break anything with these changes but experience tells me there is always some nuance. Hopefully these will resolve themselves as we move forward.

I have a lot more to say about the Facebook app, I hope I’ll complete it in time but frankly with my current status it’s a bit doubtful as I’m seriously delayed and back logged. On the plus side what I have so far looks great!

]]>
1,119 iOS Back Command Behavior and Facebook Clone Update 0 0 0
Tutorial - DPI Explained blog/tutorial-dpi-explained.html Wed, 11 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tutorial-dpi-explained.html

I’ve been working on improved learning materials for Codename One. One of the problems with videos is that I can sometimes unintentionally drag an idea that can be explained with a single image. DPI is one of those ideas, I’m assuming most of you already understand it but even if you do, I think this graphic helps put things in perspective.

Device Density Explained in One Image
Figure 1. Device Density Explained in One Image
]]>
1,120 Tutorial - DPI Explained 0 0 0
Map Component Positioning Revisited blog/map-component-positioning-revisited.html Tue, 10 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/map-component-positioning-revisited.html

I published two articles on MapLayout here and here. After all that work they are now effectively obsolete thanks to a new API in MapContainer that builds component placement directly into the map itself.

Unfortunately the Google API doesn’t let us position components (native or otherwise) accurately as it pans the map. This creates a small delay when panning/zooming as the components try to catch up to the map. The only workaround is to convert the components to images and ask the map to move images within it. Then convert the images back when the map finishes panning. That’s exactly what Steve implemented within the native maps.

For you as a developer this is all seamless. If you use the map API and add components into the map they will "magically" update to the right position & transition between rendering as an image and as a component. If you would like more control over this process you can override the new toImage() method of Component to tune that behavior.

The new API works just like the regular marker API, you can add a component to the map at a given latitude/longitude position using:

map.addMarker(component,location);

You can also anchor the location to a specific alignment vertically and horizontally.

This makes the Uber demo app much simpler.

]]>
1,121 Map Component Positioning Revisited 0 0 0
Date Util blog/date-util.html Thu, 5 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/date-util.html

Timezones suck. Especially daylight saving. I don’t mind moving the clock or losing an hour of sleep as much as the programming bugs related to that practice. The thing that sucks even more is Java’s old date/time API.
This was publicly acknowledged by the Java community with JSR 310 which replaced the Java Date & Time API’s however due to its complexity we still don’t have it yet. As a small workaround we created a small API to perform some common date calculations.

DateUtil allows you to check if a day is in the daylight saving era or if it isn’t. It works consistently on all platforms without a problem e.g.:

DateUtil du = new DateUtil();
Log.p("Currently in daylight savings time? "+du.inDaylightTime(new Date()));
Log.p("Offset: "+du.getOffset(new Date().getTime()));

Date dec30 = new Date(1483056000000l);
Log.p("Dec 30 is daylight savings time? "+du.inDaylightTime(dec30));
Log.p("Offset: "+du.getOffset(dec30.getTime()));

The DateUtil constructor can take a TimeZone as parameter. Without it, it uses the default TimeZone.

Completion Listeners

Media allows us to track whether it finished playing or not when we first set it up. After that point you were on your own.

Last week we added a new ability to bind a completion listener after the fact and potentially have multiple listeners:

MediaManager.addCompletionHandler(myMediaObject, () -> Log.p("This is a runnable callback"));

Partial Round

I’ve been working on improving this issue. The UI part isn’t there yet but the code is…​

The gist of it is that with the round rect border we currently have 3 options:

  • All corners should be rounded

  • Only the top corners

  • Only the bottom corners

The issue pointed out a use case for some of the corners and I can think of a case where I’d like the left or right corners rounded…​

With that in mind I decided the right thing to do is offer control over individual corners. This is possible only in code at the moment but would hopefully make it to the designer tool too at some point:

RoundRectBorder rb = RoundRectBorder.create().bottomLeftMode(false);

This would create a border whose corners are round except for the bottom left corner. While I was working on the class I also improved the performance/memory overhead of the border for solid colors.

Support for PATCH HTTP Request In Rest

The Rest class now supports the HTTP PATCH method which was missing from the API before. It’s not as common as other API’s so it went unnoticed for a while.

It works pretty much like every other Rest API request.

]]>
1,122 Date Util 0 0 0
Build Native Interfaces - Camera Edition blog/build-native-interfaces-module-camera-edition.html Wed, 4 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/build-native-interfaces-module-camera-edition.html

I discussed the new Camera cn1lib last week. One of the motivations for doing it (besides the request from an enterprise account) was that of a new course module. Last week I added a new module covering the process of building the camera cn1lib…​
We have several online videos (both in the course and outside of it) covering native interfaces. So why do we need another one?

Working with native code is error prone & complex. A lot of the tutorials we built in the past ended up spending more time on configuration issues than on code. In this module I talked a lot about Objective-C, callbacks, delegates & more. If you are thinking about native integration this helps give a more rounded view of the options.

]]>
1,123 Build Native Interfaces - Camera Edition 0 0 0
TIP: IntelliJ/IDEA RAM blog/tip-intellij-idea-ram.html Tue, 3 Apr 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-intellij-idea-ram.html

I’m used to NetBeans but if I will ever switch it will be to IntelliJ/IDEA. It’s a great IDE. I just need to rewire the muscle memory of my fingers for it. The Codename One plugin support on IntelliJ should be as good as the NetBeans support as the code is very similar. There are however a couple of pitfalls that a lot of people trip over which I’d like to discuss.

Tree Mode

IntelliJ defaults to showing errors as a "tree". It parses the output of the app to the console and shows "pretty" output. Unfortunately this doesn’t always play nice with Codename One and hides things like compiler errors.

We strongly recommend you disable that setting…​

Toggling off the Tree Mode
Figure 1. Toggling off the Tree Mode

Out of Memory in Ant Build

When you start working and your code grows (especially with Kotlin) you might run into an out of memory error during compilation. You can resolve that by setting the memory available to Ant as explained in this image.

Configuring the Memory Available to Ant
Figure 2. Configuring the Memory Available to Ant
]]>
1,124 TIP: IntelliJ/IDEA RAM 0 0 0
Camera Kit - Low Level Camera API blog/camerakit-low-level-camera-api.html Thu, 29 Mar 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/camerakit-low-level-camera-api.html

When we introduced support for z-ordering of peer components in Codename One we listed two major targets. The first was already available: Map. The second was still pending: Camera.
Our current Capture API is very high level and removes a lot of the control from the developer. In order to give developers a high level of control we created Camera Kit.

Camera Kit is based on a native Android Camera Kit project whose API we used to implement the Android port and for inspiration. This new API works on Android & iOS at this time. It allows you to grab photos/videos & view the camera preview like you would any other media.

You can overlay your Codename One widgets on top of the camera view as you can see in the project sample and screenshot.

This effectively makes a lot of previously impossible use cases possible. E.g. grabbing a photo after a given interval, grabbing a video for a fixed number of seconds. Placing a UI element on top of the camera view etc.

One of the really cool things about this is that it’s entirely in a cn1lib. That means you can grok the code without understanding Codename One. You can fix issues and add functionality without knowing too much.

As a sidenote the reason we picked up this task is because an enterprise customer asked for this…​ If you can afford an enterprise account we really appreciate it and everyone gets the benefit of the new functionality!
So if the company you work for can purchase an enterprise account this can help everyone who uses Codename One.

Status & Future

Right now only a part of the functionality is implemented on iOS with some nuances still missing. On Android we get errors due to a bug in the native Android Camera Kit library. Since the native library is heading for a 1.0 version this should be resolved when we update to that.

I would love to see ports of this to other Codename One platforms and the simulator. We’ll take a look at these as we move forward based on user demand.

]]>
1,125 Camera Kit - Low Level Camera API 0 0 0
Android Build Target 27 Migration blog/android-build-target-27-migration.html Wed, 28 Mar 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/android-build-target-27-migration.html

A while back Google announced that starting in August 2018 they will no longer accept applications targeting API levels below 26. With that in mind we plan to migrate our builds to use API level 27 which brings with it a lot of great new features but will probably break some things as we go through the migration. Please read this post carefully, I’ll try to cover everything.

Notice that this announcement means that we will need to start updating the API levels every year which is a much faster pace.

I’ve constructed this post as a set of questions/answers.

What’s an API Level?

Every time Google releases a new version of Android it updates the API level e.g. currently Oreo (8.1) is API level 27.

When we build a native Android application we need to declare the "target" this means we compiled the project against this given API level. This is a double edged sword…​ When we pick a higher API level we can target new features of newer OS’s but we are also subject to new restrictions and changes.

E.g. when we migrated to API level 23 we had to change the way permissions are processed in applications. For Codename One code this was mostly seamless but if you relied on native code this sometimes triggered issues.

API level 27 can impact things such as background behavior of your application and can break some cn1libs/native code you might have in place.

FYI this is also explained in this article from Ars.

Will this Work for Older Devices?

The target API level doesn’t restrict older devices. For that we have a separate minimum target device and it indicates the lowest API level we support. Currently the default is 15 (Android 4.0.3 - Ice Cream Sandwich) but you can probably set it as low as 9 (Android 2.3 - Gingerbread) as long as you test the functionality properly and disable Google Play Services.

See this for a table of all the API levels from Google.

What API Level do we use Now?

That depends on your app. Our default is 23 but some cn1libs set the API level to 25.

We chose to migrate slowly as level 23 is generally good and stable.

Test This Now!

Test API level 27 right now before we flip the switch!

This is important as we want to iron out bugs/regressions before they impact everyone. You can enable this seamlessly by setting the build hint: android.buildToolsVersion=27

Remove this build hint after testing, otherwise when we migrate to a newer version later on it might fail!

This will implicitly set a lot of other values including the target level and it will change the gradle version from 2.12 to 4.6.

Other Benefits.

By flipping this switch the build should now work on Android Studio 3.x out of the box without the changes listed in this tip. We also plan to enable other things in the resulting project such as using Googles builtin Java 8 support instead of ours (this isn’t enabled yet).

This will mean that native Android code would be able to use Java 8 features. Notice that this currently applies to the native interfaces only and not to the code in the Codename One implementation.

This should make it easier to work with some 3rd party libraries that already moved forward.

When Will this Happen?

The 27 build target is available now using the build hint: android.buildToolsVersion=27.

Currently we are aiming to flip the switch by May. This might be pushed to a different date based on responses/feedback on the current status. We want to have enough time ahead so the July release of Codename One 5.0 (Social) will use this.

Is this a Good Thing?

I think it is. It prevents stagnation within the appstore.

I still see apps in the stores that target Gingerbread (API level 9). That’s a problem both visually & in terms of permissions/security.

I don’t think it will do enough to combat fragmentation. Google will need to change a lot more to fix that pickle but it’s a baby step in the right direction.

]]>
1,126 Android Build Target 27 Migration 0 0 0
TIP: Uninstall cn1lib blog/tip-uninstall-cn1lib.html Tue, 27 Mar 2018 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-uninstall-cn1lib.html

Unfortunately this isn’t automatic due to the way cn1libs are implemented. In some cases you need to uninstall a cn1lib if you no longer need its functionality and this is far from seamless.

These are the steps you need to take:

  1. Remove the files with the name of the extension (the .cn1lib and the .ver file) from the lib directory - you can see them in the files tab in NetBeans or in the file explorer of your OS.

  2. Open Codename One SettingsBuild Hints & remove the ios. & android. entries you didn’t add manually

  3. Right click project and select Codename OneRefresh Client Libs

This last step will recreate the ios. and android. entries needed by other cn1libs you might have installed.

]]>
1,127 TIP: Uninstall cn1lib 0 0 0
Codename One 4.0 "Taxi" is now Live blog/codename-one-4-0-taxi-live.html Tue, 20 Mar 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/codename-one-4-0-taxi-live.html

We are thrilled to announce the release of Codename One 4.0 - Taxi. Codename One is an open source "Write Once Run Anywhere" mobile solution for Java & Kotlin developers!
This new release overhauled the way Codename One is updated, added support for Progressive Web Apps (PWA’s), overhauled device skins & updated the backend iOS build tools. A major focus of this release is better support for peer (native) components, stability, unit testing and continuous integration.

Codename One is the only platform that…​

  • Has Write Once Run Anywhere with no special hardware requirements and 100% code reuse

  • Compiles Java or Kotlin into native code for iOS, UWP (Universal Windows Platform), Android & even JavaScript

  • Is Open Source & Free for commercial use with an enterprise grade commercial support

  • Is Easy to use with 100% portable Drag & Drop GUI builder

  • Has Full access to underlying native OS capabilities using the native OS programming language (e.g. Objective-C) without compromising portability

  • Has full control over every pixel on the screen! Just override paint and draw or use a glass pane to draw anywhere…​

  • Lets you use native widgets (views) and mix them with Codename One components within the same hierarchy (heavyweight/lightweight mixing)

  • Supports seamless Continuous Integration out of the box

To learn more about Codename One check out the about page you can download it for free right now.

Version 4.0 is nicknamed Taxi because of the Uber Clone application that was developed with it for the online course in the Codename One Academy.

Uber sidemenu next to the clone
Figure 1. Uber sidemenu next to the clone
The Uber login form next to the clone
Figure 2. The Uber login form next to the clone

Highlights of this Release

The top 5 features of this release are covered in this short video, check out further details below…​

  • Progressive Web App Support (PWA) - Progressive Web Apps allow us to try an application on the web and seamlessly transition to a native app. This makes user acquisition easier and installation frictionless. Codename One is the only tool in the world that supports PWA’s seamlessly

  • New Device Skins - We updated the look of Codename One by releasing 33 new device skins including iPhone X & Pixel 2 XL. We included support for non-rectangular device skins and better device fidelity. We also added the ability to grab a screenshot that includes the skin frame around it

  • Xcode 9.2 - Codename One apps are built using xcode 9.2. This change is seamless for most developers as the update happened on the build servers. Xcode 9.2 requires additional permission messages which are added automatically by the simulator

  • Update Framework - Updates to Codename One libraries are now delivered using a unified framework instead of separate adhoc tools

  • Continuous Integration Support - We now support Travis CI out of the box seamlessly. Adding support for additional CI tools should be just as easy

  • New Async JavaScript Interop API - The Java → JavaScript bridge with the embeddable browser component was completely replaced. The new implementation should be faster than the old system

  • Builtin Unit Tests - Unit tests to Codename One are integrated into the core repository and are executed with every commit

  • Improved Peer Components - Multiple bugs and minor issues were fixed in the peer component layer this effectively enabled the Uber clone to work properly with the native map

  • Better Hello World - The new Codename One projects generate better code that handles things such as network errors more effectively

  • GUI Builder Refinements - There were many refinements to the new GUI builder most notably:

    • Improved support for layout nesting in auto-layout mode - you can use all the existing layout managers within an autolayout parent

    • New Window Manager allows you to customize the positioning of the windows & palettes

    • Tabs component is supported again

  • Test Push In the Simulator - The simulator now supports testing push notification

There are many other features both big and small. Check out our blog and the github project history.

Lowlights

As we always do with a release we’d like to shine a spotlight on the things this version could do better and the things the next version can improve. Overall we are thrilled with this release but here are a few things we can do better:

  • On device debugging - I wasn’t optimistic about getting this out for 4.0 and I’m still not optimistic about 5.0. We already have a lot on our plate for 5.0 and this is a huge feature

  • Improved default themes & material design - we did a lot of work on the skins but didn’t move the native theme or make a separate material design theme. We need to do a lot of work on the default hello world applications to make them look great out of the door.

Overall while we implemented a lot of features in 4.0 we didn’t really address most of the problems we highlighted in this section when 3.8 was released. I’m not sure if we have enough time in the 5.0 cycle to improve that but hopefully we can at least move theming more aggressively again.

Onwards to 5.0 - Social

The 5.0 release cycle is relatively short & we already have a lot of things planned for it.

We should have the new social app tutorial in the Codename One Academy which will cover cloning Facebook.

Check out our survey results to see the future apps we’ll release into the academy. Even if you never plan to signup to the academy this should be interesting as it gives you a good notion of what can be built with Codename One.

Other than that we’ll try to launch better docs and designs. We’ve put a lot of effort into improving our design capabilities and one of the big things we’d like to pick up again is app templates. In the past we released a few free themes as Codename One stubs. We’d like to do that again so developers can start from "something".

We Need your Help

If you think we are doing a good job and appreciate our help please help us by:

Thanks for reading this far and if you have any thoughts/suggestions of any kind please let us know!

]]>
1,128 Codename One 4.0 "Taxi" is now Live 0 0 0
Survey Results (2018) Upcoming Apps blog/survey-results-2018.html Wed, 14 Mar 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/survey-results-2018.html

I already teased about the surprises I got from our annual survey results and there are still quite a few surprises but as more people filled out the survey some of the big surprises tamed and a lot of interesting results emerged. I also found the comments very interesting so I’ll go over the numbers/comments and provide my thoughts. If you think I misinterpreted the results please let me know…​

Which would you prefer for the "Social App" clone?

In retrospect I think I made a mistake by not separating Snapchat & Instagram into the next question. But I’m not sure I’d want to create "another" social app too soon so placing them in the 2019 survey might be better:

Which would you prefer for the
Figure 1. Which would you prefer for the "Social App" clone?

Facebook is a clear winner here so the next app I’ll release would be a Facebook clone. Having said that there is clear interest in both ephemeral communication (snapchat) & in photo sharing/manipulation so I’ll try to discuss both of these to some degree. I’m not sure if they will make it into the current app though.

I stripped out a few "no opinion" responses to make the results easier to read

The Next App

I divided the question of the next app into two, the #1 choice and the #2 choice. A lot of developers are focused on one thing so having two choices might help us get a better/wider sense of the things you are building.

What’s your #1 Choice for the Next App? (After Facebook)

Lets start with the data:

app choice 1
Figure 2. What’s your #1 Choice for the next App?

Before I proceed one of the feedback comments brought my attention to the fact that I might have biased the poll by phrasing the question as "After the Facebook App". I totally missed that. I’ll try to pay more attention next time!

Clearly whatsapp won. Initially this wasn’t the case. It was losing to AirBnB. After a while Netflix came from behind to grab the second place spot. Notice that there are a lot of people who specified other which means I missed a few important apps (I’ll get into that soon).

Before I go into the analysis I think it helps to see the second choice too…​

What’s your #2 Choice for the Next App?

app choice 2
Figure 3. What’s your #2 Choice for the next App?

Whatsapp and Netflix pretty much tied here but AirBnb shrunk.

Analysis

Before I go into analysis and comments I’d like to declare the winners!

  1. Facebook - The social app which won #2 place in last years survey will be a Facebook clone

  2. Whatsapp - The next app released in the summer should be a whatsapp clone

  3. Netflix - The app after that should be a Netflix clone

  4. AirBnB - Currently the AirBnB app is in the lead for 4th place but I might revisit that with a new survey

Unsurprising Results

There were quite a few things that seem obvious and made sense in the results. At least in retrospect.

Whatsapp has always been a popular app and our old chat app tutorial is pretty old by now.

AirBnB is one of the best looking apps out in the market, I’m sure people want to learn how to build something like that. It also rides the wave of the sharing economy which is a popular target.

Protosketch got low votes. People don’t know it. Most of the drawing apps don’t enjoy mass market appeal. I added it because people have asked for a drawing app repeatedly, it’s something I’d like to build but it clearly doesn’t have enough commercial appeal.

One thing that didn’t exactly surprise me was the showing for a Mario game. It came in 4th which makes a lot of sense. Most of our developers aren’t game developers but there are a few.

Surprising Results

I was surprised by a lot of the results…​

Netflix is big but I don’t see many Netflix clones or people trying to compete with Netflix. I think this mostly indicates interest in the video browsing/distribution process. I think it would have given similar results if I’d had youtube as an option. I think video streaming is an interesting app type so I’m excited to do something similar to Netflix.

Tinder probably surprised me the most but I was also relieved. To clone an app I need to use it and I doubt my spouse would accept an "I’m just cloning the app" excuse…​
I honestly don’t know why this wasn’t a popular choice.

Square is also surprising. I’m personally familiar with at least two PoS vendors who use Codename One so I assumed there would be more interest in that. It’s possible that this is because square is so focused in the US market & unfamiliar outside of it. Or maybe it’s because of the hardware reader. I’m not sure.

I would have expected Spotify to have a better showing. I’m guessing that most developers feel the music field is too competitive to enter at this time.

Comments & Suggestions

I very much appreciate all the comments and suggestions that highlighted some omissions and also some things I should improve in our communication.

Notice I cleaned up the comments a bit and unified them to make them more readable, if I missed a comment it might be by mistake!
App Suggestions

A common request was for business oriented apps:

  • Appointment Setting App (e.g. for Doctors)

  • Business oriented app (e.g. CRM mobile client)

  • ERP client

I’d love to do one of those but these are harder to market and generalize. Each business is doing something different in their backend and it’s much harder to just make up something from scratch.

If there was a business app I can replicate or a backend ERP platform we can target that could be interesting but it’s very hard to teach/build something vague.

One of the things I did think about was an Intercom clone. It’s a CRM of sort. We use them for the website support and emails. I really don’t like that tool and would love to replace them. I just forgot to add it to the survey, although I don’t think it would have made an impact.

Other suggestions included:

  • FaceApp - Most of the app is in the face processing code, I think this would step outside of our comfort zone. Having said that there are some cool open source image recognition libraries that I’d love to integrate into Codename One given the time/user demand

  • Runtastic/Fitbit - I totally missed those…​ They should have been on the list!

  • Smart tv universal remote - I’m guessing these just use a communication protocol with the TV. I’m not sure what value we can bring here?

  • Android Pay - I’m assuming this means Google Pay? I can’t install it on my device but PayPal might be an interesting option here. My bank also has a nice app but I’m not sure if it’s something that’s marketable

  • Uber clone - Someone actually asked for this…​ I guess I don’t send out enough emails…​

Comments

I’ve removed a lot of the comments especially the positive ones. Thank you for them but I’m not sure those are interesting in this context…​

Of the other comments I tried to moderate as little as possible. If people took the time to write something I’m guessing it’s unclear.

Codenameone networking is not supporting real time updates. Would be nice if it did.

I’m not sure I understand this comment.

Our networking does support realtime updates via: WebSockets, Sockets, Long Polling & Push. Otherwise I wouldn’t have even suggested a whatsapp clone…​

Do you mean something like socket.io or pubnub?

We have somewhat out of date support for the latter but not the former. If there is specific demand we can help with that.

Why social application?

Because people voted on that last year and picked it.

We try to do what people ask for. I think that even if you build completely closed backend systems a lot of your UI and code would be influenced by the top apps in the market today (e.g. Uber, Facebook, whatsapp etc.) so even if you aren’t building a social app you can learn a lot by following a course on how it’s made.

Bluetooth & Hardware Integration

"My proposal is Bluetooth BLe, Application for sensors and anything that can help normal people with real needs"

"Apps with high hardware integration e.g. AR, Bluetooth or development boards for Embedded devices."

Those are great but also vague. I agree we need better demos for bluetooth support but We also need mass appeal. We need concrete targets that we can implement.

One of the huge problems with bluetooth is that the standard is so big and varied. One person sees bluetooth as file sharing, while another sees it as hardware monitoring and another as a data transfer tool. Add to that the need to get physical hardware that works with this and you end up with something most people will find hard to pick up.

AI is becoming the in thing…​ any app that incorporates Artificial Intelligence

I started my professional career working in AI so I’d love to do some more. But moving out of my personal space, the question is what can I teach and how can I help?

I agree, I’d love to have builtin integration for things like tensorflow and other AI related API’s. I’m not sure if it’s something that should go into an app or just a small tutorial. I’d also love to integrate one of the image recognition libraries in the market.

ERP/CRM

"Most of commercial software vendors work on business apps. Not social apps. So I would love to see something resembling CRM, ERP, HCM or anything similar"

I tend to agree with that statement. The problem is that there is no real uniform standard we can target here in the app space. If you have more concrete suggestions for something we can build I’m open to it. The only thing that comes close to this is Intercom but I doubt it would have been popular in this survey.

To be fair I worked with a lot of businesses and they expect things to look like commercial apps. The app from my current bank looks like Facebook with a feed of "stories" that match my spending & stats from the bank. They even have a chat interface to a teller which should be close to whatsapp. It’s actually pretty nice.
My point is that business app developers would probably find more benefit from a tutorial on building facebook than one that’s too business specific. E.g. one could take the Uber app in a business and use it to manage their vehicle fleet.

Offline Builds

Let developers enjoy CN1 features! Offline Build server capabilities for free!

That’s a bit unrelated but I’d like to address that as there are several common misconceptions here.

First is that offline building isn’t free. Offline building is free, the source for building Codename One is 100% open source and all the instructions are publically available. If you don’t want to make the effort to learn how to do it we have a course module that explains the steps one by one.
There is an enterprise grade offline build tool but the cost there is mostly support overhead.

Furthermore, the build servers are free to use commercially with reasonable limits. Notice that the Uber app and all our demos fall within the free quota.

The second misconception is that this would be enjoyable. It’s a pain to build offline. E.g. from our enterprise customers who can build offline (as it’s included in their subscription) a very small percentage makes use of that feature. I can build offline and I don’t use that feature.
It’s painful and surprisingly slower than using the build servers even with our enterprise integration. Even when I want to debug in an IDE I generally prefer sending a build and downloading the source code than going through the offline builder. The only reason we added that feature was for government and other cloud averse entities.

Now back to our regularly scheduled suggestions…​

Any Online shopping App

We already have the restaurant app which is a shopping app of a sort in the building course.

Leading the Survey?

"What’s your #1 Choice for the next App? (after the Facebook app)" is biased.. should be "What’s your #1 Choice for the next App? (after the social app)"

Damn. You are right. I’ll try to be more careful with those subtle things next time. That was unintentional.

Kraken or some other crypto exchange

I don’t use crypto currencies but adding something around them seems appealing. This specific app might be problematic as it’s very poorly rated with a mediocre UI. I’m open to suggestions around this field.

TL;DR

The winners of for the next apps we will build starting this summer are:

  1. Facebook

  2. Whatsapp

  3. Netflix

AirBnB is a strong runner up but we’ll probably do a new survey to determine whether it should be the leader.

]]>
1,129 Survey Results (2018) Upcoming Apps 0 0 0
Codefreeze for Codename One 4.0 - Taxi blog/codefreeze-4-taxi.html Tue, 13 Mar 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/codefreeze-4-taxi.html

Codename One 4.0 (Taxi) will launch next week, to keep the code stable we are entering a week long code freeze. Please update your plugin installs frequently and report bugs immediately so we will have a stable release!

We’ve added a lot of new features to 4.0 but the big things are pretty disruptive:

  • Xcode 9.2 switch

  • Update Framework

Because both of these changes have many subtle implications we are relying on feedback from you on what’s working and what isn’t. If you run into any issue please file the issue ASAP so we can move quickly and update the release if necessary.

During this week of code freeze we’ll try to update the documentation and prepare everything for the release.

Upcoming Milestones

This is our first release in the new numbering scheme and our first code named release.

We will branch to the 4.0 branch in our repository and every change to the branch will be cherry picked individually.

Here are the coming releases after 4.0 (Taxi):

  • 5.0 (Social) - Scheduled for July 17th 2018

  • 6.0 (Name Pending) - Scheduled for November 14 2018

You can track these milestones and the related tasks in our github project here.

]]>
1,130 Codefreeze for Codename One 4.0 - Taxi 0 0 0
Map Layout Update blog/map-layout-update.html Thu, 8 Mar 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/map-layout-update.html
The information in this blog post is slighly out of date. Check out the newer blog post that covers positioning components on the map.

A while back I introduced a MapLayout class as a tip and discussed the usage of this class. Since that introduction we ran into some scale issues as the layout misbehaved when a lot of elements were added to it. The crux of the issue is in the native map API which runs on the OS native thread and the Codename One API which needs immediate responses for layout.

These issues became very apparent in the Uber app clone code. As a solution we updated the layout to use an approach that’s asynchronous and fetches data in batches. This made the layout far more responsive.

While we were there we also needed a way to align components to the position in the map e.g. a marker needs a center/bottom position while a car would be centered etc. So we added support for alignment as well which you can specify in the new API using:

MapLayout.setHorizontalAlignment(myCmp, HALIGN.LEFT);
MapLayout.setVerticalAlignment(myCmp, VALIGN.BOTTOM);

This tool is still a bit of a cludge, ideally as we work with it in the future we’ll abstract it as a nice API into the map cn1lib. For now this is the revised version of the class:

public class MapLayout extends Layout implements MapListener {
        private static final String COORD_KEY = "$coord";
        private static final String POINT_KEY = "$point";
        private static final String HORIZONTAL_ALIGNMENT = "$align";
        private static final String VERTICAL_ALIGNMENT = "$valign";
        private final MapContainer map;
        private final Container actual;
        private boolean inUpdate;
        private Runnable nextUpdate;
        private int updateCounter;

        public static enum HALIGN {
            LEFT {
                int convert(int x, int width) { return x; }
            },
            CENTER {
                int convert(int x, int width) { return x - width / 2; }
            },
            RIGHT { int convert(int x, int width) { return x - width; }
            };

            abstract int convert(int x, int width);
        }

        public static enum VALIGN {
            TOP {
                int convert(int y, int height) { return y; }
            },
            MIDDLE {
                int convert(int y, int height) { return y + height / 2; }
            },
            BOTTOM {
                int convert(int y, int height) { return y + height; }
            };

            abstract int convert(int y, int height);
        }

        public MapLayout(MapContainer map, Container actual) {
            this.map = map;
            this.actual = actual;
            map.addMapListener(this);
        }

        @Override
        public void addLayoutComponent(Object value, Component comp, Container c) {
            comp.putClientProperty(COORD_KEY, (Coord) value);
        }

        @Override
        public boolean isConstraintTracking() {
            return true;
        }

        @Override
        public Object getComponentConstraint(Component comp) {
            return comp.getClientProperty(COORD_KEY);
        }

        @Override
        public boolean isOverlapSupported() {
            return true;
        }

        public static void setHorizontalAlignment(Component cmp, HALIGN a) {
            cmp.putClientProperty(HORIZONTAL_ALIGNMENT, a);
        }

        public static void setVerticalAlignment(Component cmp, VALIGN a) {
            cmp.putClientProperty(VERTICAL_ALIGNMENT, a);
        }

        @Override
        public void layoutContainer(Container parent) {
            int parentX = 0;
            int parentY = 0;
            for (Component current : parent) {
                Coord crd = (Coord) current.getClientProperty(COORD_KEY);
                Point p = (Point) current.getClientProperty(POINT_KEY);
                if (p == null) {
                    p = map.getScreenCoordinate(crd);
                    current.putClientProperty(POINT_KEY, p);
                }
                HALIGN h = (HALIGN)current.getClientProperty(HORIZONTAL_ALIGNMENT);
                if(h == null) {
                    h = HALIGN.LEFT;
                }
                VALIGN v = (VALIGN)current.getClientProperty(VERTICAL_ALIGNMENT);
                if(v == null) {
                    v = VALIGN.TOP;
                }
                current.setSize(current.getPreferredSize());
                current.setX(h.convert(p.getX() - parentX, current.getWidth()));
                current.setY(v.convert(p.getY() - parentY, current.getHeight()));
            }
        }

        @Override
        public Dimension getPreferredSize(Container parent) {
            return new Dimension(100, 100);
        }

        @Override
        public void mapPositionUpdated(Component source, int zoom, Coord center) {
            Runnable r = new Runnable() {
                public void run() {
                    inUpdate = true;
                    try {
                        List<Coord> coords = new ArrayList<>();
                        List<Component> cmps = new ArrayList<>();
                        int len = actual.getComponentCount();
                        for (Component current : actual) {
                            Coord crd = (Coord) current.getClientProperty(COORD_KEY);
                            coords.add(crd);
                            cmps.add(current);
                        }
                        int startingUpdateCounter = ++updateCounter;
                        List<Point> points = map.getScreenCoordinates(coords);
                        if (startingUpdateCounter != updateCounter || len != points.size()) {
                            // Another update must have run while we were waiting for the bounding box.
                            // in which case, that update would be more recent than this one.
                            return;
                        }
                        for (int i=0; i<len; i++) {
                            Component current = cmps.get(i);
                            Point p = points.get(i);
                            current.putClientProperty(POINT_KEY, p);
                        }
                        actual.setShouldCalcPreferredSize(true);
                        actual.revalidate();
                        if (nextUpdate != null) {
                            Runnable nex = nextUpdate;
                            nextUpdate = null;
                            callSerially(nex);
                        }
                    } finally {
                        inUpdate = false;
                    }

                }

            };
            if (inUpdate) {
                nextUpdate = r;
            } else {
                nextUpdate = null;
                callSerially(r);
            }
        }
}
]]>
1,131 Map Layout Update 0 0 0
New Default Code blog/new-default-code.html Wed, 7 Mar 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/new-default-code.html

This is new behavior that went in without fanfare. If you created a new hello world app you might have noticed this. We changed the default boilerplate for Codename One and made it more representative of what you’d want to see in a hello world app.

The entire change to the default generated app is within the init method:

public void init(Object context) {
    // use two network threads instead of one
    updateNetworkThreadCount(2); (1)

    theme = UIManager.initFirstTheme("/theme");

    // Enable Toolbar on all Forms by default
    Toolbar.setGlobalToolbar(true);

    // Pro only feature
    Log.bindCrashProtection(true);

    addNetworkErrorListener(err -> { (2)
        // prevent the event from propagating
        err.consume();
        if(err.getError() != null) {
            Log.e(err.getError());
        }
        Log.sendLogAsync();
        Dialog.show("Connection Error", "There was a networking error in the connection to " + err.getConnectionRequest().getUrl(), "OK", null);
    });
}
1 By default networking in Codename One runs on one network thread for consistency. Two threads make more sense from a performance standpoint. We wanted to make this easily configurable and it should be easy in the init() method
2 Handling network errors in a generic way is probably one of the hardest things to grasp. So many developers are still stuck with the default error handling code…​ With that in mind we added a bit of boilerplate to handle network errors in a way that makes sense and should be easily customizable

I had some thoughts about removing this boilerplate and packaging it in a class. But then I thought again.

I Like Boilerplate

  • It’s simple

  • It’s obvious

  • You can instantly find what you want

This isn’t code for the sake of code or declaration. The code is still concise and does what we expect. This is one of those cases where boilerplate makes more sense.

]]>
1,132 New Default Code 0 0 0
TIP: Include Source with Android Studio 3.0 blog/tip-include-source-android-studio-3.html Tue, 6 Mar 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-include-source-android-studio-3.html

I covered the include source feature extensively. For the most part it’s the simplest way to debug an application directly on the device. When I made that video the current version of Android Studio and Gradle were much older. We still use API version 23 on the build servers to keep everything compatible but you might want to use a newer version of the IDE.

In that case you can use the newer version 3.0+ of Android Studio but you will need to make a few updates to the process.

Notice that this is a temporary thing as eventually we’ll update the build servers to emit newer target versions too as features & devices proliferate

Automatic Update

One of the features in Android Studio is its ability to automatically update the project. You should use that feature to update gradle to the latest version and the SDK version as well. I’ve tested version 26 and it seems to work fine but I haven’t done any stress testing and wouldn’t recommend it other than for debugging purposes.

If you submit an APK to Google play with a newer SDK version they will no longer accept an older version of the same APK!

Once the automatic update is done you will need to update a few files and settings.

gradle.properties

In this file you just need to add the line:

android.enableAapt2=false

This will fix issues with styles later on.

Style Files

There are 3 style files named styles.xml under the directories: res/values, res/values-v11 & res/values-v21.

They appear in the IDE as one file with 3 versions. The default versions from our servers include some @ characters in the wrong place that Google doesn’t play nicely with. Since they are auto-generated they are badly formatted and hard to fix manually. So here are the full versions of all 3 files that you can paste on top of the existing files.

res/values/styles.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CustomTheme" parent="android:Theme.Black">
        <item name="attr/cn1Style">@style/CN1.EditText.Style</item>
    </style>
    <attr name="cn1Style" format="reference" />
    <style name="CN1.EditText.Style" parent="@android:style/Widget.EditText">
        <item name="android:textCursorDrawable">@null</item>
    </style>
</resources>

res/values-v11/styles.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CustomTheme" parent="@android:style/Theme.Holo.Light">
        <item name="attr/cn1Style">@style/CN1.EditText.Style</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowTitleSize">0dp</item>
    </style>
    <style name="CN1.EditText.Style" parent="@android:style/Widget.EditText">
        <item name="android:textCursorDrawable">@null</item>
    </style>
</resources>

res/values-v21/styles.xml :

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CustomTheme" parent="@android:style/Theme.Material.Light">
        <item name="attr/cn1Style">@style/CN1.EditText.Style</item>
        <item name="android:windowActionBar">false</item>
        <item name="android:windowTitleSize">0dp</item>
        <item name="android:colorPrimary">@color/colorPrimary</item>
        <item name="android:colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="android:colorAccent">@color/colorAccent</item>
   </style>
    <style name="CN1.EditText.Style" parent="@android:style/Widget.EditText">
        <item name="android:textCursorDrawable">@null</item>
    </style>
</resources>

Finally

All of this should work and run with Android Studio 3.0. Ideally this blog post will be unnecessary by the time you read it as we’d move forward to align with the current approach from Google.

I’m not sure when we’ll upgrade from 23 as it’s a relatively good target version, to a large degree it depends on feedback from you.

]]>
1,133 TIP: Include Source with Android Studio 3.0 0 0 0
Updated 4.0 Release Date blog/updated-4-release-date.html Thu, 1 Mar 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/updated-4-release-date.html

Last week I mentioned we are considering postponing the release planned for next week. We eventually did just that and the release is now scheduled for the 20th with code freeze on the 13th. So far we didn’t change future release dates but there might be a cascading effect. As I explained before, this is an inevitable result of the migration to xcode 9.2 which is something we need to stabilize before the release.

We already have a lot of big features in the release so the big pieces are already there.

Release Code Names

With the switch to major release numbers we decided to name the releases. We thought about silly names similar to the spirit of Android, RedHat etc.

Then it occurred to us that with the Uber Clone application we essentially proved a use case of a taxi app built with Codename One. It’s not a feature that you can list in the "new features" section but it’s an important highlight…​ Since we will be releasing additional applications to the academy as we go it makes sense to tie them into the release versions.

With that in mind Codename One 4.0 is nicknamed - Taxi.
Codename One 5.0 is tentatively nicknamed - Social.

]]>
1,134 Updated 4.0 Release Date 0 0 0
Tune Performance, Profile on Devices in Latest Academy Update blog/tune-performance-profile-on-devices.html Wed, 28 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/tune-performance-profile-on-devices.html

I promised 2 new course modules for February and just published the second one. In case you don’t recall I discussed the first one here and it covered building Codename One applications from the open source code.
This new module goes through seven lessons that cover everything you need to know when building a performant app. It covers everything from generic performance tips/analysis all the way to profiling and a case study. You can check out the full module in the Deep Dive into Mobile Programming course in the academy.

The deep dive course is bundled into the full Build real world full stack mobile apps course for free!

Together with the offline build course I added 2 hours of additional material this month alone.

This opens the question of where do we go from here?

I’m still weighing some ideas for the module I’ll release in March. I would also like you to help me pick the following apps after that…​

With that in mind I’m publishing a new survey which I would hope you can help me with here.

This will help me create new content that better suits the apps you are trying to build.

]]>
1,135 Tune Performance, Profile on Devices in Latest Academy Update 0 0 0
TIP: Streams are Observable in Codename One blog/tip-streams-observable.html Tue, 27 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-streams-observable.html

We got a pull request the other day that reminded me of some hidden functionality in Codename One that most developers aren’t aware of: observable input streams. By default Codename One API’s try to return BufferedInputStream and BufferedOutputStream instances from our internal API’s. Those classes aren’t the typical java.io versions but rather ones from the com.codename1.io package.

That API allows us to add functionality into the streams without breaking the Java compatibility or specs. One such feature is setProgressListener(IOProgressListener). This is probably better explained with a simple sample:

final Status status = ToastBar.getInstance().createStatus(); (1)
status.setMessage("Reading file");
status.setShowProgressIndicator(true);
status.showDelayed(300);
BufferedInputStream bi; (2)
if(inputStream instanceof BufferedInputStream) {
   bi = (BufferedInputStream)inputStream;
} else {
   bi = new BufferedInputStream(inputStream);
}
bi.setProgressListener((source, bytes) -> { (3)
    callSerially(() -> status.setProgress(100 * bytes / streamLength)); (4)
});
1 The toastbar has a special mode that lets us display progress from 0 to 100
2 Since streams would often already be observable the `instanceof ` should be true for a few cases. In the exception this fallback would be reasonable
3 Notice that the progress listener carries an overhead and might slow your application. This might not be a big deal for some cases
4 The value is between 0 and 100 so we need to know the stream length in advance for the toastbar API

Finally

There are quite a few API’s in Codename One that are hidden under the surface. From our vantage point it’s often hard to remember everything that’s there even if we put it there. It’s worth asking us as some things can be hidden from view.

]]>
1,136 TIP: Streams are Observable in Codename One 0 0 0
Preferences Binding and getAndSet() blog/preferences-binding-getAndSet.html Thu, 22 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/preferences-binding-getAndSet.html

I added support for binding a property object to Preferences a while back and just didn’t have the time to blog about it. I didn’t consider it too crucial as the functionality is very simple to figure out, the only difficult part is knowledge of the features existence.

Some objects make sense as global objects, we can just use the Preferences API to store that data directly but then we don’t have the type safety that property objects bring to the table. That’s where the binding of property objects to preferences makes sense. E.g. say we have a global Settings property object we can just bind it to preferences using:

PreferencesObject.create(settingsInstance).bind();

So if settings has a property called companyName it would bind into Preferences under the Settings.companyName entry.

We can do some more elaborate bindings such as:

PreferencesObject.create(settingsInstance).
    setPrefix("MySettings-").
    setName(settingsInstance.companyName, "company").
    bind();

This would customize all entry keys to start with MySettings- instead of Settings.. This would also set the company name entry to company so in this case instead of Settings.companyName we’d have MySettings-company.

getAndSet

A part of getting this to work seamlessly is the getAndSet API added to the Preferences API. This is a bit of a weird API but it’s pretty useful so lets explain it by example.

Say we have a user setting for refresh as an integer in minutes:

int refresh = Preferences.get("refresh", 60);

That will return the refresh value as 60 if it doesn’t exist. The problem is: "how do we know we got back the default?".

Normally that isn’t a big deal but if we have one path that invokes Preferences.get("refresh", 60) and another one that does a Preferences.get("refresh", 100); how can we tell which one is correct?

I could use Preferences.get("refresh", 60); and if 60 is returned I can invoke set to explicitly set the 60 value just to make sure that 60 will be used from now on. But that would mean changing preferences with every invocation even if the data didn’t change.

getAndSet() essentially solves that problem. You get the data one and if the default is used that default is then stored into the Preferences so you can’t get inconsistent results for a specific entry.

]]>
1,137 Preferences Binding and getAndSet() 0 0 0
Use our Open Source Code to Build Codename One Offline blog/use-open-source-build-offline.html Wed, 21 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/use-open-source-build-offline.html

I promised 2 new course modules for February and just published the first one. It covers the process of building a Codename One app from the Codename One source code. The whole process is done without using the Codename One plugin or build servers. It uses only open source project code to deliver iOS/Android & desktop binaries!
You can check out the full module in the Deep Dive into Mobile Programming course in the academy.

The deep dive course is bundled into the full Build real world full stack mobile apps course for free!

Working with the Codename One sources is not for the faint of heart. You can learn a lot from going through the process. However, if your only goal is to avoid the build servers you might find it harder to work with.

In fact I personally use the build servers when building apps and testing them. I almost never use offline build or the sources directly. Instead I hack and test things via the include source options. However, learning this is still valuable and I’m aware of a few people who don’t share my opinion on this matter…​

Still, why would I create a guide for something like this?

There are 3 types of individuals I can think of who might benefit from this guide:

  • If you are the type of person who needs to do everything yourself then this is pretty much it

  • If you want to understand the underpinning of Codename One at a deeper level than the more abstract descriptions. Then this is a good first step

  • If you want to feel secure that you can hack Codename One manually if our service changes or becomes unavailable in the future then the mere existence of this guide should help calm some of those concerns

To me the third option makes a lot of sense. I think that the existence of this module is probably the biggest value it delivers.

Performance

Next week I will post a module covering performance tuning, tips and pitfalls. I already wrote a lot of material for this module which is shaping up as an even bigger module.

]]>
1,138 Use our Open Source Code to Build Codename One Offline 0 0 0
Xcode 9.2 on by Default this Friday blog/xcode-9-on-by-default.html Tue, 20 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/xcode-9-on-by-default.html

A few weeks ago I announced the xcode 9.2 mode and was rather happy that we can take our time with the migration. Unfortunately, that wasn’t meant to be. Apple will require all new submissions to use xcode 9 within the next few months so it makes no sense to keep 7.3 as the default. This weekend we will flip the switch and builds will default to 9.2.

It’s earlier than we wanted to but it’s crucial that Codename One 4.0 will work well with xcode 9.2 otherwise developers won’t be able to use versioned build halfway through the 4.0 cycle.

As a reminder if you want to still use xcode 7.3 because you are concerned that this change broke something in your code just add the build hint:

ios.xcode_version=7.3

To test xcode 9.2 before the update this weekend just use the build hint:

ios.xcode_version=9.2

For the most part the change should be seamless at this time. I would suggest that you read the original article where I discuss the refined permissions that are now a part of xcode 9. If you run into issues please check that they happen in 9 and not in 7 and file issues immediately!

Multitasking Flag

To support multi-tasking in iPads where we can have a side by side view of the UI we need to use a xib launch file. Unfortunately we just couldn’t nail this correctly for all the use cases so we had to keep the existing screenshot code in place for now.

Regular iOS multitasking will still work as usual this refers only to the iPad split screen support

If you are willing to compromise on some edge case odd behavior for the launch image in exchange for side by side view you can turn this on explicitly by using the ios.multitasking=true build hint. Hopefully we’ll be able to refine the xib behavior and turn this on by default for 5.0.

Plugin Update & Release Delay

With the update of the libs this weekend we’ll add a strong nag to people who haven’t updated their plugins. We need to deploy the new update framework as it allows us to solve some problems in very elegant ways.

If you haven’t updated your IDE plugin please do so soon.

We are considering delaying the release that is planned for March 6th by a week or even two. This would give us more time to test these two big changes: update framework & the 9.2 migration.

]]>
1,139 Xcode 9.2 on by Default this Friday 0 0 0
New Update Framework blog/new-update-framework.html Thu, 15 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/new-update-framework.html

When it comes to big changes this is pretty huge but surprisingly "subtle". This weekend we’ll release a new plugin update that will completely replace the update process of Codename One and a week after that we will start nagging you to update your plugin so we can all be on the same page. This is a HUGE change as we didn’t change anything about the update process since 2012. But the cool thing about it is that you might not notice it…​

When we launched Codename One in 2012 we needed a way to ship updates and fixes faster than the plugin update system. So we built the client lib update system. Then we needed a way to update the designer tool (resource editor), the GUI builder & the skins…​ We also needed a system to update the builtin builder code (CodeNameOneBuildClient.jar so we built a tool for that too).

Notice the uppercase N in CodeNameOneBuildClient.jar. It’s so old even we weren’t sure how to write our own company name correctly…​

Update Framework

A big piece of the change is the removal of code within the IDE plugins that tries to do the update for us. Once we remove that code the new Update Framework can effectively fetch up to date versions of the important jars and make sure everything is at the latest. It solves several problems in the old systems:

  • Download once - if you have multiple projects the library will only download once to the .codenameone directory. All the projects will update from local storage

  • Skins update automatically - this is hugely important. When we change a theme we need to update it in the skins and if you don’t update the skin you might see a difference between the simulator and the device

  • Update of settings/designer without IDE plugin update - The IDE plugin update process is slow and tedious. This way we can push out a bug fix for the GUI builder without going through the process of releasing a new plugin version

For the most part this framework should be seamless. You should no longer see the "downloading" message whenever we push an update after your build client is updated. Your system would just poll for a new version daily and update when new updates are available.

You can also use the usual method of Codename One SettingsBasicUpdate Client Libs which will force an update check. Notice that the UI will look a bit different after this update.

How does it Work?

You can see the full code here the gist of it is very simple. We create a jar called UpdateCodenameOne.jar under ~/.codenameone (~ represents the users home directory).

An update happens by running this tool with a path to a Codename One project e.g.:

java -jar ~/.codenameone/UpdateCodenameOne.jar path_to_my_codenameone_project

E.g.:

java -jar ~/.codenameone/UpdateCodenameOne.jar ~/dev/AccordionDemo
Checking: JavaSE.jar
Checking: CodeNameOneBuildClient.jar
Checking: CLDC11.jar
Checking: CodenameOne.jar
Checking: CodenameOne_SRC.jar
Checking: designer_1.jar
Checking: guibuilder_1.jar
Updating the file: /Users/shai/dev/AccordionDemo/JavaSE.jar
Updating the file: /Users/shai/dev/AccordionDemo/CodeNameOneBuildClient.jar
Updating the file: /Users/shai/dev/AccordionDemo/lib/CLDC11.jar
Updating the file: /Users/shai/dev/AccordionDemo/lib/CodenameOne.jar
Updated project files

Notice that no download happened since the files were up to date. You can also force a check against the server by adding the force argument as such:

java -jar ~/.codenameone/UpdateCodenameOne.jar path_to_my_codenameone_project

The way this works under the hood is thought a Versions.properties within your directory that lists the versions of local files. That way we know what should be updated.

Exclude Versions.properties from Git

Under the ~/.codenameone directory we have a more detailed UpdateStatus.properties file that includes versions of the locally downloaded files. Notice you can delete this file and it will be recreated as all the jars get downloaded over again.

What isn’t Covered

You will notice 3 big things that aren’t covered in this unified framework:

  • We don’t update cn1libs - I’m not sure if this is something we would like to update automatically

  • Versioned builds - there is a lot of complexity in the versioned build system. This might be something we address in the future but for now I wanted to keep the framework simple.

  • Offline builds - Offline builds work through manual download and aren’t subjected to this framework

Finally

I don’t expect a big change like this to go well without a hitch. So please accept our apologies for everything that probably will go wrong over the next couple of weeks as we tune this system. Once it will be in place we will deliver fixes and updates faster.

We won’t need to deal with as many IDE specific behaviors and we will be able to update the system itself moving forward.

]]>
1,140 New Update Framework 0 0 0
New Async Java-Javascript Interop API blog/new-async-java-javascript-interop-api.html Wed, 14 Feb 2018 00:00:00 +0200 steve https://www.codenameone.com/blog/new-async-java-javascript-interop-api.html

We recently introduced a new API for interacting with Javascript in Codename One. This new API is part of the BrowserComponent class, and effectively replaces the com.codename1.javascript package, which is now deprecated.

So what was wrong with the old API?

The old API provided a synchronous wrapper around an inherently asynchronous process, and made extensive use of invokeAndBlock() underneath the covers. This resulted in a very nice API with high-level abstractions that played nicely with a synchronous programming model, but it came with a price-tag in terms of performance, complexity, and predictability. Let’s take a simple example, getting a reference to the “window” object:

JSObject window = ctx.get("window");

This code looks harmless enough, but this is actually quite expensive. It issues a command to the BrowserComponent, and uses invokeAndBlock() to wait for the command to go through and send back a response. invokeAndBlock() is a magical tool that allows you to “block” without blocking the EDT, but it has its costs, and shouldn’t be overused. Most of the Codename One APIs that use invokeAndBlock() indicate this in their name. E.g. Component.animateLayoutAndWait(). This gives you the expectation that this call could take some time, and helps to alert you to the underlying cost.

The problem with the ctx.get("window") call is that it looks the same as a call to Map.get(key). There’s no indication that this call is expensive and could take time. One call like this probably isn’t a big deal, but it doesn’t take long before you have dozens or even hundreds of calls like this littered throughout your codebase, and they can be hard to pick out.

The New API

The new API fully embraces the asynchronous nature of Javascript. It uses callbacks instead of return values, and provides convenience wrappers with the appropriate “AndWait()” naming convention to allow for synchronous usage. Let’s look at a simple example:

In all of the sample code below, you can assume that variables named bc represent an instance of BrowserComponent.
bc.execute(
    "callback.onSuccess(3+4)",
    res -> Log.p("The result was "+res.getInt())
);

This code should output “The result was 7” to the console. It is fully asynchronous, so you can include this code anywhere without worrying about it “bogging down” your code. The full signature of this form of the execute() method is:

public void execute(String js, SuccessCallback<JSRef> callback)

The first parameter is just a javascript expression. This javascript MUST call either callback.onSuccess(result) or callback.onError(message, errCode) at some point in order for your callback to be called.

The second parameter is your callback that is executed from the javascript side, when callback.onSuccess(res) is called. The callback takes a single parameter of type JSRef which is a generic wrapper around a javascript variable. JSRef has accessors to retrieve the value as some of the primitive types. E.g. getBoolean(), getDouble(), getInt(), toString(), and it provides some introspection via the getType() method.

It is worth noting that the callback method can only take a single parameter. If you need to pass multiple parameters, you may consider including them in a single string which you parse in your callback.

Synchronous Wrappers

As mentioned above, the new API also provides an executeAndWait() wrapper for execute() that will work synchronously. It, as its name suggests, uses invokeAndBlock under the hood so as not to block the EDT while it is waiting.

E.g.

JSRef res = bc.executeAndWait("callback.onSuccess(3+4)");
Log.p("The result was "+res.Int());

Prints “The result was 7”.

When using the andWait() variant, it is extremely important that your Javascript calls your callback method at some point - otherwise it will block indefinitely. We provide variants of executeAndWait() that include a timeout in case you want to hedge against this possibility.

Multi-use Callbacks

The callbacks you pass to execute() and executeAndWait() are single-use callbacks. You can’t, for example, store the callback variable on the javascript side for later use (e.g. to respond to a button click event). If you need a “multi-use” callback, you should use the addJSCallback() method instead. Its usage looks identical to execute(), the only difference is that the callback will life on after its first use. E.g. Consider the following code:

bc.execute(
    "$('#somebutton').click(function(){callback.onSuccess('Button was clicked')})",
    res -> Log.p(res.toString())
);

The above example, assumes that jQuery is loaded in the webpage that we are interacting with, and we are adding a click handler to a button with ID “somebutton”. The click handler calls our callback.

If you run this example, the first time the button is clicked, you’ll see “Button was clicked” printed to the console as expected. However, the 2nd time, you’ll just get an exception. This is because the callback passed to execute() is only single-use.

We need to modify this code to use the addJSCallback() method as follows:

bc.addJSCallback(
    "$('#somebutton').click(function(){callback.onSuccess('Button was clicked')})",
    res -> Log.p(res.toString())
);

Now it will work no matter how many times the button is clicked.

Passing Parameters to Javascript

In many cases, the javascript expressions that you execute will include parameters from your java code. Properly escaping these parameters is tricky at worst, and annoying at best. E.g. If you’re passing a string, you need to make sure that it escapes quotes and new lines properly or it will cause the javascript to have a syntax error. Luckily we provide variants of execute() and addJSCallback() that allow you to pass your parameters and have them automatically escaped.

For example, suppose we want to pass a string with text to set in a textarea within the webpage. We can do something like:

bc.execute(
    "jQuery('#bio').text(${0}); jQuery('#age').text(${1})",
    new Object[]{
       "A multi-line\n string with \"quotes\"",
       27
    }
);

The gist is that you embed placeholders in the javascript expression that are replaced by the corresponding entry in an array of parameters. The ${0} placeholder is replaced by the first item in the parameters array, the ${1} placeholder is replaced by the 2nd, and so on.

Proxy Objects

The new API also includes a JSProxy class that encapsulates a Javascript object simplify the getting and setting of properties on Javascript objects - and the calling of their methods. It provides essentially three core methods, along with several variants of each to allow for async or synchronous usages, parameters, and timeouts.

E.g. We might want to create a proxy for the window.location object so that we can access its properties more easily from Java.

JSProxy location = bc.createJSProxy("window.location");

Then we can retrieve its properties using the get() method:

location.get("href", res -> Log.p("location.href="+res));

Or synchronously:

JSRef href = location.getAndWait("href");
Log.p("location.href="+href);

We can also set its properties:

location.set("href", "http://www.google.com");

And call its methods:

location.call("replace", new Object[]{"http://www.google.com"},
    res -> Log.p("Return value was "+res)
);
]]>
1,141 New Async Java-Javascript Interop API 0 0 0
New Skins, San Francisco Font and more blog/new-skins-san-francisco-font.html Tue, 13 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/new-skins-san-francisco-font.html

One of the big issues with some new users who picked up Codename One was the lack of up to date device skins. We made several attempts in the past to improve this situation but these ended up as half measures at best. Last week we addressed some of the core problems that made it hard to add new skins and as a result we now have 33 new skins that are far better than anything we had before.

Specifically:

  • GooglePixel

  • GooglePixel2

  • GooglePixel2XL

  • HTCOneA9

  • HTCOneM8

  • HuaweiP8

  • MicrosoftLumia950

  • MotoE

  • MotoG

  • Nexus4

  • Nexus5X

  • Nexus6P

  • SamsungGalaxyGrandPrime

  • SamsungGalaxyNote5

  • SamsungGalaxyS3

  • SamsungGalaxyS5

  • SamsungGalaxyS7

  • SamsungGalaxyS8

  • iPhone5c

  • iPhone5s

  • iPhone6s

  • iPhone6sPlus

  • iPhone7

  • iPhone7Plus

  • iPhone8

  • iPhone8Plus

  • iPhoneX

  • MicrosoftSurface3

  • MicrosoftSurfacePro4

  • Nexus9

  • iPadAir2

  • iPadMini4

  • iPadPro

You will notice some pretty cool skins in that list and unlike before we got a lot of things right. As part of this update we also added the ability to take a screenshot with the skin which looks pretty cool…​

You will also notice in this list of skins the Google Pixel XL 2 and iPhone X. These skins show a non-rectangular portion of the screen which was previously problematic.

iPhone X non-rectangular skin and screenshot that includes said skin
Figure 1. iPhone X non-rectangular skin and screenshot that includes said skin

Why did it Take so Long?

Besides the obvious amount of work we needed to do to get here there were also several problems we had to resolve.

The most obvious one was a good source for skin images. This is important as a lot of mockups on the Internet get some things wrong e.g. pixels off in the area of the screen etc. Once that was in place we made a few changes that made the whole process of skinning far easier.

Status Bar

When we launched Codename One all operating systems had a status bar on the top of the display. This doesn’t sound like a big deal but it was a HUGE pain. It was technically part of the screen but separate because you could take a screenshot with or without the status bar for different use cases.

However, in recent years modern phone UI views the status bar as part of the applications screen real-estate and so it no longer makes sense to have it in the skin. So the new skins ignore that aspect. This keeps them more consistent with the way modern phone OS’s behave.

PPI - Pixels Per Inch

Up until now when you defined a skin file you had to provide a numeric value that indicated the ratio of pixels per millimeter. It wasn’t hard to calculate that value but it was tedious and unintuitive…​

However, almost every device has a well known PPI value that represents its density. Calculating the pixel ratio from the PPI value is pretty easy. So you can now use the ppi key when defining a new skin like I did here in the iPhone X skin properties.

Oddly Shaped Skins

The iPhone X skin isn’t just rounded. It has a notch on top that hides a portion of the UI. The Pixel 2 XL skin also includes rounded corners which also obstruct a portion of the screen. That was very challenging.

These skins required a fix to some paint bugs that triggered flickering. But they also needed a bigger conceptual fix.

Up until now we just used a system that marked the area of the screen using black pixels. That worked well and removed the need to measure pixels accurately. However, this wouldn’t work with an oddly shaped screen as the API wouldn’t know how to answer getDisplayHeight() for an iPhone X device. We toyed briefly with the idea of writing some complex heuristics for calculating that but eventually decided this wasn’t a good idea…​

Instead we added the following fields to the skin.properties file:

roundScreen=true
displayWidth=1125
displayHeight=2436
displayX=85
displayY=77

When roundSkin is turned on we check these following variables to pick up the square coordinates of the screen then draw the skin on top of them. Notice that the screen is always square regardless of the shape of the phone.

San Francisco Font

In iOS 9 Apple transitioned from the Helvetica Neue font to the San Francisco font. Both look very similar and I for one can’t tell them apart to save my life. But it’s important to keep up so we decided to switch the font as well.

However, unlike previous iterations of the font Apple made the font names somewhat obtuse and inconsistent. So the only way to load the font is by using low level API’s that fetch the default OS font.

As a result when our native system asks for Helvetica Neue instead of loading that we get the system default font which will be Helvetica Neue on older OS’s and San Francisco on newer OS’s. That also means that font changes in future iOS updates should be reflected automatically from now on.

One caveat is italic which still uses the Helvetica Neue font. The syntax for getting weighted italic fonts was problematic so we left it as it is today.

Hidden Status Bar on Android

One of the reasons I gave for the new skins lack of status bar is the trend OS’s have taken to hide the status bar from view. Since iOS 7 developers were expected to deliver an app that runs under the status bar. Our solution for that was including a bit of default padding on the top of the Form in iOS to push the content down.

Android still has a status bar and most apps still use it, you can customize the colors of the Android status bar using the colors.xml as explained here. However, up until now we didn’t support hidden status bars.

We just introduced the build hint android.hideStatusBar which you can set to true. This will make the status bar seem transparent which will allow a more consistent UX with your iOS version.

]]>
1,142 New Skins, San Francisco Font and more 0 0 0
New Rest Calls blog/new-rest-calls.html Thu, 8 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/new-rest-calls.html

I really like the newer Rest API’s Chen added a while back. They are ultimately far more convenient than the ConnectionRequest API’s for most day to day development. In fact I used them almost exclusively in the Uber clone app. As a result of that I added a few useful enhancements and capabilities.

If you are not familiar with the API it works roughly like this:

Map<String, Object> jsonData = Rest.
            get(myUrl).
            acceptJson().
            getAsJsonMap();

This will request using an HTTP GET method from the url. It will set the accepts header to JSON and parse the response.

Content Type

An important ommission was the content type setting, you could use the header() value to achieve that but content type is an important header and deserves its own method:

Map<String, Object> jsonData = Rest.
            post(myUrl).
            contentType("application/json").
            body(bodyContentAsJSON).
            acceptJson().
            getAsJsonMap();

In this case we submit JSON data in a post request with the application/json content type.

jsonContent

Notice that there is duplicate data in the sample above where both acceptJson and the contentType effectively communicate the same idea of using JSON for the protocol (albeit the two sides of the protocol). Since this is a common case it can use the shorthand:

Map<String, Object> jsonData = Rest.
            post(myUrl).
            jsonContent().
            body(bodyContentAsJSON).
            getAsJsonMap();

Asynchronous Callbacks

We always had asynchronous callbacks in the API but in the past they were delivered via the interface Callback which has 2 methods. That means we couldn’t leverage the shorthand lambda notation when implementing simple requests. We added two new permutations to the method and removed the Async suffix for them to avoid potential collision.

E.g. in the past I would have had to write something like this:

Map<String, Object> jsonData = Rest.
            post(myUrl).
            jsonContent().
            body(bodyContentAsJSON).
            getAsJsonMapAsync(new Callback<Response<String>>() {
                @Override
                public void onSucess(Response<String> value) {
                    // code here...
                }

                @Override
                public void onError(Object sender, Throwable err, int errorCode, String errorMessage) {
                    // code here...
                }
        });

We can now use a terse approach like this:

Map<String, Object> jsonData = Rest.
            post(myUrl).
            jsonContent().
            body(bodyContentAsJSON).
            getAsJsonMap(value -> {
                    // code here...
            });

You will notice I somewhat glazed over the error handling which will go to the global error handler in this case. You can avoid that by overriding the error handler separately in the second argument to this method (which is optional).

]]>
1,143 New Rest Calls 0 0 0
TIP: Stop Editing blog/tip-stop-editing.html Tue, 6 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/tip-stop-editing.html

Device only bugs are the worse. You need to go through a device build and reproduce/rinse/repeat. Thankfully these bugs are rare but sometimes they just hit you smack in the face. One such problem occurred when I was debugging a transition on Android related to a login form. I would move between a Form where I had the keyboard open to one where it was closed. This created a nasty effect where the keyboard folded leaving a black space and the transition played out about that black space.

On the simulator this won’t happen, we can’t realistically simulate the virtual keyboard.

It won’t happen on iOS either. Only on Android.

The Android port resizes the display during input and that behavior triggers this end result where the display doesn’t have time to recover before the transition starts.

Initially I thought I can workaround this by invoking:

textField.stopEditing();
callSerially(() -> showOtherForm());

But that only helped on some cases, not all. Even the fact that I used callSerially didn’t help as this depends on a native event going through.

The solution is to use the new stopEditing(Runnable) API. On most OS’s the runnable will be invoked immediately but on Android it will wait for the screen resize before it invokes the code. So this will work as you would expect:

textField.stopEditing(() -> showOtherForm());
]]>
1,144 TIP: Stop Editing 0 0 0
Xcode 9.2 Mode blog/xcode-9-mode.html Thu, 1 Feb 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/xcode-9-mode.html

Every time we switch a version of xcode in the build servers things go haywire because of minor behavioral changes from Apple. Over the holidays we started a long and painful migration to xcode 9.2 which required an update to the Mac OS versions on our servers. Thankfully this wasn’t as bad as the old xcode 5 to 7.3 migration where the old build code literally stopped working…​

This makes things FAR easier, but the migration itself still didn’t work on first try due to changes in the signing process. Over the past week we were able to address those final issues and xcode 9.2 builds are now functional!

Building for xcode 9.2 is still experimental at this time so be sure to test and submit issues!

Toggling the Xcode Versions

You can toggle the xcode 9.2 build by using the build hint:

ios.xcode_version=9.2

The default currently maps to 7.3 but at some point in the future we will probably flip the default. If at that point you would want to try the old version you could use the reverse:

ios.xcode_version=7.3

Notice that we will make a clear announcement about flipping the default xcode version.

Things to Notice

Currently the splash screen seems problematic. We are working on resolving that.

Push and local notifications might experience issues when running on xcode 9.2 as there were some changes related to those API’s.

Permissions

iOS has tightened the requirements around API access permissions and now expects description entries for every problematic API. E.g. if your app uses the camera, Apple expects you to include a description explaining why you need camera access.

The same holds true for other API’s.

When our simulator encounters usage of those API’s it automatically adds the appropriate build hint containing default description text. This will help you get a build through but you might need to customize that text before submission to Apple.

Common permission descriptions you will need are: ios.NSCameraUsageDescription, ios.NSContactsUsageDescription, ios.NSLocationAlwaysUsageDescription, NSLocationUsageDescription, ios.NSMicrophoneUsageDescription, ios.NSPhotoLibraryAddUsageDescription, ios.NSSpeechRecognitionUsageDescription, ios.NSSiriUsageDescription.

You will notice they all follow the convention ios.NSXXXUsageDescription. The XXX portion maps to the way Apple represents this permission in the plist file. You can see the full list from Apple here.

]]>
1,145 Xcode 9.2 Mode 0 0 0
Progressive Web Apps blog/progressive-web-apps.html Tue, 30 Jan 2018 00:00:00 +0200 steve https://www.codenameone.com/blog/progressive-web-apps.html

PWAs (Progressive Web Apps) are an extremely hot topic right now, and Codename One apps are very well suited to being deployed this way. In case you haven’t been following the PWA buzz, the idea is that it’s a web app that behaves like a native app. When they are first loaded in a user’s browser, they behave like a normal responsive web app, but users can install them to their home screen just like native apps. At which point, they can behave as "offline-first" apps. Parts of this have been available for quite some time, but the concept of PWA brings a lot of little things under a single umbrella.

If this all sounds familiar it’s because the JavaScript port of Codename One was practically built for PWA…​

Deploying as a Progressive Web App

Out of the box, your app is ready to be deployed as a progressive web app (PWA). That means that users can access the app directly in their browser, but once the browser determines that the user is frequenting the app, it will "politely" prompt the user to install the app on their home screen. Once installed on the home screen, the app will behave just like a native app. It will continue to work while offline, and if the user launches the app, it will open without the browser’s navigation bar. If you were to install the native and PWA versions of your app side by side, you would be hard pressed to find the difference - especially on newer devices.

Below is a screenshot from Chrome for Android where the browser is prompting the user to add the app to their home screen.

Add app to homescreen banner
Figure 1. Add app to homescreen banner

If the app is available as a native app, in the Play store, you can indicate this using the javascript.manifest.related_applications and javascript.manifest.prefer_related_applications build hints. Then, instead of prompting the user to add the web app to their home screen, they’ll be prompted to install the native app from the Play store, as shown below.

Add native app banner
Figure 2. Add native app banner
The PWA standard requires that you host your app on over HTTPS. For testing purposes, it will also work when accessed at a localhost address. You can use the Lighthoust PWA analysis tool to ensure compliance.

For more information about Progressive Web Apps see Google’s introduction to the subject.

Customizing the App Manifest File

At the heart of a progressive web app is the web app manifest. It specifies things like the app’s name, icons, description, preferred orientation, display mode (e.g. whether to display browser navigation or to open with the full screen like a native app), associated native apps, etc.. The Codename One build server will automatically generate a manifest file for your app but you can (and should) customize this file via build hints.

Build hints of the form javascript.manifest.XXX will be injected into the app manifest. E.g. To set the app’s description, you could add the build hint:

javascript.manifest.description=An app for doing cool stuff

You can find a full list of available manifest keys here. The build server will automatically generate all of the icons so you don’t need to worry about those. The "name" and "short_name" properties will default to the app’s display name, but they can be overridden via the javascript.manifest.name and javascript.manifest.short_name build hints respectively.

The javascript.manifest.related_applications build hint expects a JSON formatted list, just like in the raw manifest file.

One nice feature (discussed above) of progressive web apps, is the ability to specify related applications in the app manifest. Browsers that support the PWA standard use some heuristics to "offer" the user to install the associated native app when it is clear that the user is using the app on a regular basis. Use the javascript.manifest.related_applications build hint to specify the location of the native version of your app. E.g.

javascript.manifest.related_applications=[{"platform":"play", "id":"my.app.id"}]

You can declare that the native app is the preferred way to use the app by setting the javascript.manifest.prefer_related_applications build hint to "true".

According to the app manifest documentation, this should only be used if the related native apps really do offer something that the web application can’t do.

Device/Browser Support for PWAs

Chrome and Firefox both support PWAs on desktop and on Android. iOS doesn’t support the PWA standard, however, many aspects of it are supported. E.g. On iOS you can add the app to your home screen, after which time it will appear and behave like a native app - and it will continue to work while offline. However, many other nice features of PWA like "Install this app on your home screen" banners, push notifications, and invitations to install the native version of the app, are not supported. It is unclear when, or even, whether Apple will ever add full support; but most experts predict that they will join the rest of the civilized world and add PWA support in the near future.

On the desktop, Chrome provides an analogous feature to "add to your homescreen": "Add to shelf". If it looks like the user is using the app on a regular basis, and it isn’t yet installed, it will show a banner at the top of the page asking the user if they want to add to their shelf.

Add to shelf banner
Figure 3. Add to shelf banner

Clicking the "Add button" prompts the user for the name they wish the app to appear as:

Add to shelf prompt
Figure 4. Add to shelf prompt

Upon submission, Chrome will generate a real application (on Mac, it will be a ".app", on Windows, an "exe", etc..) which the user can double click to open the app directly in the Chrome. And, importantly, the app will still work when the user is offline.

The app will also appear in their "Shelf" which you can always access at chrome://apps, or by opening the "Chrome App Launcher" app (on OS X this is located in "~/Applications/Chrome Apps/Application Launcher").

Chrome App Launcher
Figure 5. Chrome App Launcher
The Chrome App Launcher lists apps installed both via the Chrome Web Store and via the "Add to Shelf" feature that we discuss here. The features we describe in this article are orthogonal to the Chrome Web Store and will not be affected by its closure.
]]>
1,146 Progressive Web Apps 0 0 0
Uber Clone Clarifications and Xcode 9.2 Update blog/uber-clone-clarifications-xcode-9-2-update.html Wed, 24 Jan 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/uber-clone-clarifications-xcode-9-2-update.html

The other day I sent out an email alert mentioning the Uber clone release and price change for the course on the 30th. I also mentioned upcoming modules in February which caught some attention. I wanted to clarify some things and answer some of the repeat questions I’ve been getting. I also have a lot of updates to make so I’ll include some of those below.

Uber Clone

I uploaded 33 lessons into the module by now which you can see in the main course page. I’m almost finished for this part.

If there are issues/missing functionality that’s problematic I might revisit this code in the future. However, based on the amount of work I had to put in I’m not sure how likely that would be. I would have to repeat all this work again for the social network app coming in April.

By the way, thank you all who visited and hearted my article at hacker noon. It got republished by dzone as well.

Pricing

This has been somewhat contentious the last time I wrote about it. We don’t raise prices often. We actually only did it once for the basic subscription a few years back.

Having said that. The pricing of the course makes no sense once the Uber module is uploaded. It becomes a far larger course and increases its value. This will repeat once I upload additional apps. It’s unfair to existing users if we charge the same price regardless of value. It also provides a purchase disincentive.

We will raise the price to 499USD on the 30th of January and to 599USD in May once the social networking app is up.

We will still have an installments plan but it will probably raise prices too rather than stretch the installments further.

As you may recall I guaranteed a new app 4 times a year which means 8 new apps where the Uber clone is the first in that series. That doesn’t imply we’ll raise the course price by 800USD. I think that we’ll reach a price that we deem reasonable and stop (or slow down) at that point.

Upcoming Modules

Next month I will publish two new modules into the academy.

The first I’m pretty clear about and it’s currently tentatively titled: "Working with the Codename One Source code: working without the build servers".

It will walk through the process of setting up an environment based mostly on the source code without using the build servers. It will continue where include the source left off by going into device build complexities without the servers.

I’m still open for suggestions about second module so feel free to comment below. A good suggestion I received was "performance" which I think I can probably spend hours talking about…​

Xcode 9.2 Update

We started the process of migrating to xcode 9.2. The big pain of upgrading servers to the latest version is behind us but builds still don’t go through. If by chance you select xcode 9.2 in the build hints or in the Codename One Settings UI the build will fail with a signing error.

You will need to select xcode 7.3 explicitly for now.

We will keep you posted on the migration, unfortunately we can’t make this migration 100% seamless as you might need build hints to clarify usage of some API’s that didn’t require these things in older versions of xcode.

Finally

There are a lot of new features and news I hope to get into in February but I’m trying to get some closure so we can get back to the fast development pace.

]]>
1,147 Uber Clone Clarifications and Xcode 9.2 Update 0 0 0
Meltdown and Other Updates blog/meltdown-updates.html Wed, 17 Jan 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/meltdown-updates.html

I’ve been so busy I just don’t have time to blog as much as I should. I do hope next month will be better in this regard (more on that below) but right now I have to make an important announcement. There are new chip vulnerabilities I’m sure you heard a lot about specifically Meltdown & Spectre. Thankfully we are at a layer that shouldn’t be impacted by these issues but we need to update our servers and will be doing so over the course of the next few days (possibly more as patches get updated).

So you might see some sudden downtime starting today, don’t be alarmed but let us know in case we didn’t notice. You can post to the google group even if our website goes down…​

There is some concern about potential impact to the build process speed. We don’t think this should change significantly as the big bottleneck in our build process isn’t CPU speed it’s IO. The kernel is involved in IO but it’s far from the bottleneck of that process.

New cn1libs for Objective-C & Code Scanning

In other news Steve published two new cn1libs. The first is CN1ObjCBridge which is effectively reflection from Java into the Objective-C platform on iOS.

Instead of using native interfaces to invoke native code you could use an API that lets you send Objective-C messages (their equivalent of method calls) from Codename One Java code. That’s pretty impressive.

For most normal cases I think using native interfaces would still be better but this could fill in a niche for things that could use reflection or better callback functionality.

If you want to see a usage example check out his new scandit library which uses the Scandit barcode/QR code scanning API.

Scandit provides much faster barcode/QR code scanning speeds but it comes at a price. You need to pay per seat licensing fees that can be a bit high. The worst part of it is that we can’t redistribute their binaries or use them with cocoapods/gradle. This means we can’t distribute a precompiled cn1lib for this product. However, if you need professional grade bardcode scanning this should work rather well.

We implemented this library based on a request from an enterprise subscriber

Push Console

We added a lot of new features over the time I’ve taken away from blogging. I’ll write more about those but for now I’ll just mention one new feature. Push simulator.

In the simulator you can now open a window that will help you debug push applications. E.g. you can press a button to send a registration success callback where you will get a push key. You can also send a registration error and send a message.

Notice that there is no message type option as that’s mostly seamless for the client. E.g. if you send a type 3 message it’s really just two separate message.

Type 2 or 1 etc. are all meaningless in the simulator as we only simulate the running application and not background behavior. Still I found it very useful to simulate these messages and was able to debug some nuanced behavior in NetBeans.

Status of Uber Module

I was hoping that the Uber module would be completely finished by now but it isn’t. I’m pretty close though.

I’ve done 30 lessons by now and uploaded 23. I’m guessing 40 or 45 lessons should be the final number when I’m done. I thought I’d finish by next Sunday but that’s already pushed back to Tuesday and I’m not sure I can make that deadline either. I’m doing everything I can to finish this before the end of the month as we need to start preparing to the 4.0 release which is already looming.

The reasons this is taking so long have a lot to do with the amount of extra work I need to do but they also have a lot to do with how I divide my time. I have some administrative tasks in the company that just keep me from finishing this process.

Regardless of the above I’m pretty happy with the results so far and I have a lot to say about this once I’m done.

]]>
1,148 Meltdown and Other Updates 0 0 0
Travis CI Integration blog/travis-ci-integration.html Tue, 9 Jan 2018 00:00:00 +0200 steve https://www.codenameone.com/blog/travis-ci-integration.html

We’ve just added support for Travis CI in your Codename One projects. Travis can be set up to automatically test your project (i.e. run unit tests) on a variety of different platforms every time you commit changes to github.

There is a wiki page with full documentation of this feature, but the general idea and workflow are:

  1. Enable Travis CI for your project via Codename One settings

  2. Push your project (including .travis.yml and .travis directory, which are created for you when you enable Travis) to Github.

  3. Activate your Project On Travis.

Then every time you commit changes to Github, travis will run your tests.

Settings Panel

After you’ve activated Travis, the "Travis Settings" form will look like

Travis select jobs form
On-device continuous integration requires an Enterprise account. Other accounts will see the Android and iOS options disabled. But they can still enable JavaSE.

This is a list of the jobs that you can have travis run for you. If you only select "JavaSE", then Travis will run your unit tests in the Codename One simulator. Android jobs are run on the appropriate Android emulator, and iOS jobs are run on the appropriate iOS simulator.

We will be adding more versions and platforms as time goes on.

For full details, see the wiki page.

Also check out this screencast where I demonstrate Travis integration on the old GeoViz demo.

]]>
1,149 Travis CI Integration 0 0 0
Uber Clone Trickling Down blog/uber-clone-trickling-down.html Thu, 4 Jan 2018 00:00:00 +0200 shai https://www.codenameone.com/blog/uber-clone-trickling-down.html

I hope you all had a great time over the holidays, I was working a lot but was able to enjoy the relative quiet of the holiday period to get some stuff done. There are a lot of new features I’d like to update you about and I will over the next few weeks (albeit slower than usual). Despite my best efforts I still didn’t finish the full Uber clone app course but I’m getting REALLY close. There isn’t that much work left to do though and I’m starting to trickle out the module lessons.

I hope to be done with this before the middle of January but I suck at deadlines…​ I’ll make this up by doing two modules in February and by the fact that the Uber course will have roughly 30 lessons which is a HUGE amount of content.

I’m already trickling out a few lessons (just published two today). I’ll try to keep this up and push them out at a good rate through the month. As a small teaser check out this screenshot from the simulator (notice that it’s the simulator so the map doesn’t look as good as it does on the device):

Book ride
Figure 1. Book ride

A community member sent me this link which is an overview from an outsourcing company about the cost of building an Uber like application. For the impatient they estimate the amount of total work at 5000 hours costing between 20USD to 150USD per hour. Which means the unrealistic minimum cost of such an app is 100,000USD and a more realistic value would be over the 200,000USD range!

That sounds about right, building a full featured complex mobile app with backend server and taking it through the whole process with QA etc. isn’t cheap. I’m sure though that if these guys would have used Codename One the costs would have gone down significantly and would go down further now that they could use this app as a starting point.

Price Changes in the Academy

When we launched the Codename One Academy we set the price for the Build Real World Full Stack Java Apps course at 399USD and provided some installment options.

Now that we’ll add the Uber module it doesn’t make sense to keep the same pricing in place so we’ll raise the price to 499 within a couple of weeks. I’ll send out more details about this before we do this but if you want to avoid the price increase now is a good time to upgrade.

If you already purchased the course or buy it before the price hike you have it forever. Future price changes won’t impact you regardless of updates we make to this course

We intend to do something like this for every app we add to the course, we should add a social media app around April and that will probably trigger a similar price hike to 599 USD. We will probably cap this at some point though.

Plugin Release & Updates

We did a minor plugin update to 3.8.1 over the last weekend so people would get access to some new features and bug fixes. I have a blog post from Steve pending for next week that was waiting for this plugin update.

Blogging will still be slow through January as I try to catch up to all the tasks and finish the course.

]]>
1,150 Uber Clone Trickling Down 0 0 0
Updates and Happy New Year blog/updates-happy-new-year.html Wed, 20 Dec 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/updates-happy-new-year.html

Happy holidays, Merry Christmas, happy new year to all. All of us here at Codename One hope you have a lovely vacation if you are taking one. As I mentioned before we are still working but only partially due to the holidays. We still got a lot of things out and have a lot more coming up. I will blog more about it in January. In the meantime, I’ll leave another teaser for my current project…​

I’ve been working on cloning the Uber UI and functionality focusing on the chief use cases. The app itself isn’t that much work but documenting the process is a huge effort as I’m trying to explain every single step in as much detail as possible. Unfortunately since there is so much there screenshots don’t really convey the amount of material I created so far but that’s what I have to show for it right now…​

The Uber side menu next to the clone
Figure 1. The Uber side menu next to the clone

You can see a larger version here.

I hope I’ll make the end of year deadline to publish the full module but with everything going on around here I might slip into January. I’m still hopeful I can get it out but realistically I’m trying to "do it right" and that takes time.

]]>
1,151 Updates and Happy New Year 0 0 0
Keeping Busy blog/keeping-busy.html Wed, 6 Dec 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/keeping-busy.html

I’ve been remarkably busy with the Uber clone application. Cloning Uber proved to be pretty easy but writing the material about it is much harder so I’m taking longer than I anticipated to finish everything. I’m already up to slide 200 and I’ve barely started…​ I think I might end up with more than 1,000 slides in this module!

I’m pretty optimistic about the results though and hope you will like it too. This did give me a chance to look deeply into the Uber application and I have a lot of insights into the current state of app development. E.g. the similarity between the Uber app on iOS and Android is amazing!

E.g. this is the iOS version of the login form I showed before:

iOS Uber Login UI
Figure 1. iOS Uber Login UI

The similarity to the Android version is amazing. In other forms they even use the floating action button and the underline style text field from Android…​ I have a lot more to say about this and will try to collect my thoughts on this after I finish this module.

In terms of features we didn’t implement anything new over the past couple of weeks but we did make a few bug fixes and enhancements for under the hood behaviors. E.g. title animation and pull to refresh work better now. Text input in the JavaScript port is also much improved.

Website Server Update

We need to migrate the main server hosting this site to a new location so there might be some service interruption this Sunday around 8AM GMT but it should be pretty quick. If you notice an issue after that just reload as server state might have been impacted.

This is the second server update that’s user noticeable although there were a few others. I’m assuming Linode who are our main hosting provider is doing some infrastructure work.

]]>
1,152 Keeping Busy 0 0 0
Updates and Holidays blog/updates-holidays.html Thu, 30 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/updates-holidays.html

Before I go into the details a quick announcement, we need to update some of our push servers. We will have a short amount of downtime on Sunday December 3rd around 8AM GMT. This update should be very fast and barely noticeable but it might impact some push message deliverability for a short period.

Some of us will be on vacation around December but I’ll personally still work during the month. However, I won’t post regular blog updates until mid January as the traffic during the Christmas/new year season is relatively low and I’m afraid some important updates might slip between the cracks. We do plan to push out a plugin update version 3.8.1 during this time as we have some new features and bug fixes pending.

Steve already did a lot of work on continuous integration/TDD (Test Driven Development) support and Travis CI in particular. We already have a blog post pending but since some of the functionality requires a plugin update we’ll publish that in 2018 when people will actually read the post…​ If you want to get a teaser you can check out this wiki page.

We also pushed in a lot of fixes in the past few weeks and a couple of new features…​

South Component

One of the common RFE’s in side menu bar is the ability to add a component to the "south" part of the side menu. Up until now we had various patches and workarounds to allow this but these often required some "dubious" hacks.

We now have a new API that works with the on-top and permanent side menu:

toolbar.setComponentToSideMenuSouth(myComponent);

This places the component below the side menu bar. Notice that this component controls its entire UIID and is separate from the SideNavigationPanel UIID so if you set that component you might want to place it within a container that has the SideNavigationPanel UIID so it will blend with the rest of the UI.

Unit Tests in the Core

We moved the unit tests for Codename One from Steve’s repo to the Codename One repo. You can see most of the unit test code here we hope to add more extensive tests as we run into regressions and implement new functionality.

Default Gap

The Label component might have been one of our mistakes when designing Codename One. It embeds too much functionality into a single component with the icon and the text. If I would start Codename One over again I’d separate the image and text functionality and use a layout/container approach.

Case in point: the gap between the label text and the icon. This defaults to 2 pixels and very few people know how to change it (it’s with the setGap method).

Two pixels is ridiculous for most cases and really hard to customize. We can/should fix this in the themes but I’m afraid this might break a lot of "working" code.

We added a theme constant labelGap which is a floating point value you can specify in millimeters that will allow you to determine the default gap for a label. We also added the method Label.setDefaultGap(int) which determines the default gap in pixels.

I’m conflicted about the right way to "fix this":

  • Set the default gap to 1mm in the Label class

  • Set the theme constant to 1 or 2mm in the native themes

  • Change the default template to the project to include Label.setDefaultGap(convertToPixels(1));

I’m very much inclined to do the 1st option but I’m concerned about compatibility which is why the last option is interesting too. I’m not too crazy about littering our hello world application with hacks though.

]]>
1,153 Updates and Holidays 0 0 0
Clone and Run Codename One Demos In Single Line of Code blog/git-clone-and-run-project-from-cli.html Tue, 28 Nov 2017 00:00:00 +0200 steve https://www.codenameone.com/blog/git-clone-and-run-project-from-cli.html

We have loads of demo Codename One apps hosted on Github, however cloning and running a project can be a little tricky because we generally don’t publish the dependent jar files (e.g. CodenameOne.jar) in the Github repository. This helps keep the repository lean, but it adds some steps to the process of cloning and running the project.

For use cases like this, you may want to try the Codename One CLI tool, as it provides many useful functions directly on the command line. In this post I’ll demonstrate how you can easily clone a Codename One project from Github and run it in the Codename One simulator using a single line of code.

Consider the KitchenSink demo. We can clone this repository using the following command

$ cn1 git-clone https://github.com/codenameone/KitchenSink
Cloning into 'KitchenSink'...
Installing jars into KitchenSink...
Downloading 11606508 bytes
Download completed!
Project ready at KitchenSink

What just happened?

The cn1 git-clone command is a thin wrapper around git clone (which implies that you need to have git clone in your PATH). Therefore you can pass it the same parameters as you pass to git clone. After cloning the repository, cn1 git-clone downloads the latest Codename One libs and adds them to the project so that it is ready to roll.

Running the Demo

Now that the project is cloned, we can run the demo in the Codename One simulator by running the "run" target of the project. E.g.

$ cd KitchenSink
$ ant run

Combining it into a Single Line

If you are on Mac/Linux, it is easy to use the '&&' operator to combine all this into a single line:

$ cn1 git-clone https://github.com/codenameone/KitchenSink && cd KitchenSink && ant run

This clones it, changes to the KitchenSink directory, and runs it.

Finding Existing Demos

You can also use the cn1 list-demos command to find existing Codename One demos on Github that you can clone. E.g.

$ cn1 list-demos

This will produce a list of all of the repositories on Github that are tagged with both the "codenameone" topic, and the "demo" topic. The output will look like

shannah/GeoVizDemo            : A demo app using the Codename One GeoViz Library
codenameone/KitchenSink       : Rewrite of the kitchen sink demo to match design aesthetics of 2016
... etc..

You can filter the results by adding a parameter.

$ cn1 list-demos "GeoViz"

This will only show demos that also match the GeoViz search. We have only just started tagging our demos so for now there aren’t very many listed there. But the list will grow as time goes on.

cn1 list-demos uses the Github search API, so you can use any filters that you would put into searches on the Github website.

Once we see a demo that we want to run, we can pass its full name to cn1 git-clone. E.g.

$ cn1 git-clone shannah/GeoVizDemo
This demonstrates that git-clone allows you to omit the https://github.com from the repository name, and just provide the full repository name of the form ownername/repositoryname

Adding Your Own Demos

Adding your own demos so that they will be included in the cn1 list-demos results is easy. If your project is already hosted on Github, you simply need to add the codenameone and demo topics to the repository.

When hosting a project on Github I recommend stripping out all of the jar files just as we do in our demos. You can do this by simply copying the following directives into your .gitignore file
The .gitignore contents from the KitchenSink repository
*.jar
nbproject/private/
build/
nbbuild/
dist/
lib/CodenameOne_SRC.zip
*.p12
*.mobileprovision

A shortcut would also be to use cn1 git-init instead of git init when you initialize the repository.

Appendix: Installing the CLI Tool

Thus far, I’ve skipped the step of actually installing the CLI tool. It is distributed using npm, which is included when you install NodeJS, which has a simple installer for Windows, Linux, and Mac.

Installing Globally

Installing globally on Windows (Requires Admin permissions)
npm install -g codenameone-cli
Installing globally on Mac/Linux
sudo npm install -g codenameone-cli

Installing Locally

If you don’t have admin permissions, or you just want to install it in the current directory, you can omit the -g flag. Then installation becomes

Installing locally
npm install codenameone-cli

This will install the command at ./node_modules/.bin/cn1

Screencast

I’ve create a short screencast demonstrating the use of the cn1 git-clone command.

]]>
1,154 Clone and Run Codename One Demos In Single Line of Code 0 0 0
Uber vs. Clone - Spot the difference blog/uber.html Tue, 21 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/uber.html

I’ve been working on creating a clone of the Uber app for our upcoming update of the Build Real World Full Stack Mobile Apps in Java course. There is a lot to go through there but the basics are surprisingly easy.

E.g. this is the login form for the native Uber app on my Android device next to my clone mock code also on the same Android device…​ See if you can spot which one is mine. You can see a high resolution version of the image here:

Images of the native Android Uber app and my clone code
Figure 1. Images of the native Android Uber app and my clone code

Notice that I didn’t even go for "pixel perfect" as that would mean the code would be a bit more complicated and I want the code to be simple. One of the things I like about Uber is how similar the app looks on iOS and Android. That validates a lot of what we have been saying all along: you need your own branding. Not Apples or Googles.

]]>
1,155 Uber vs. Clone - Spot the difference 0 0 0
Codename One 3.8 is Live blog/codename-one-3-8-live.html Tue, 14 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/codename-one-3-8-live.html

We are thrilled to announce the release of Codename One 3.8. Codename One is an open source "Write Once Run Anywhere" mobile solution for Java developers!
This new release significantly refines the native look and feel of Codename One, it brings the GUI builder to a new level with styling support. It finally adds Mac OS appstore distribution support which means all the major appstores are now supported targets for Codename One applications.

Codename One is the only platform that…​

  • Has Write Once Run Anywhere with no special hardware requirements and 100% code reuse

  • Compiles Java or Kotlin into native code for iOS, UWP (Universal Windows Platform), Android & even JavaScript

  • Is Open Source & Free for commercial use with an enterprise grade commercial support

  • Is Easy to use with 100% portable Drag & Drop GUI builder

  • Has Full access to underlying native OS capabilities using the native OS programming language (e.g. Objective-C) without compromising portability

  • Has full control over every pixel on the screen! Just override paint and draw or use a glass pane to draw anywhere…​

  • Lets you use native widgets (views) and mix them with Codename One components within the same hierarchy (heavyweight/lightweight mixing)

To learn more about Codename One check out the about page you can download it for free right now.

As part of the release we significantly refined our developer guide which is now also available in print form on Amazon. Notice that this guide is available for free here & in pdf format. This developer guide is a community effort which you can contribute to as explained here.

Highlights of this Release

The top 5 features of this release are covered in this short video, check out further details below…​

  • Improved Native Look & Feel - We changed the core look of buttons, labels, text components, ripple effect and more. The goal is to make Codename One applications indistinguishable from native OS apps out of the box

Before: Codename One 3.7 text Input (on Android)
Figure 1. Before: Codename One 3.7 text Input (on Android)
After: Codename One 3.8 text Input (on Android)
Figure 2. After: Codename One 3.8 text Input (on Android)
  • Kotlin Support - Kotlin is now officially supported by Codename One and works out of the box

  • On Top Side Menu - The on top side menu adapts the side menu UI to render on-top of the application instead of below but it’s really a complete rewrite of the old SideMenuBar which was implemented in a problematic way. The new on-top mode works better with native peers such as maps and can be extended more easily

  • GUI Builder Styling Support - There are a lot of enhancements and refinements in the new GUI builder one of the big ticket features is the new style UI which allows you to style an element without leaving the GUI builder

  • Mac OS Appstore Support - We now support building signed Mac OS apps which means we now support all the major vendor appstores. We already support iOS/Android stores and Windows/Microsoft’s store (via the UWP port). The Mac appstore was the last major vendor whose store we didn’t support out of the box

  • Signal Handling & Fast UTF in ParparVM - ParparVM is our open source iOS VM. It now handles low level OS signals to catch illegal access and convert it to Java exceptions. This means performance is slightly better but more importantly: you can catch errors even when they originate from native code. We also made significant improvements to the UTF-8 decoding logic which should make apps that rely on localized data faster and more memory efficient

  • Theme Enhancements - We added many new capabilities into the Codename One themes specifically: Fractional padding/margin, Rounded border, Underline borders & more

  • Table Sorting - You can now sort a table by clicking on the column header

There are many other features both big and small. Check out our blog and the github project history.

Lowlights

As we always do with a release we’d like to shine a spotlight on the things this version could do better and the things the next version can improve. Overall we are thrilled with this release but here are a few things we can do better:

  • On device debugging - this was planned before for 3.7 but didn’t make it. We have a running proof of concept but that also highlights the amount of work needed to bring this to production grade. We didn’t think it will make it for 3.8 and I’m not optimistic about 4.0 with our current workload. We think this will be a great enhancement but right now we think theming is more important

  • Improved default themes & material design - we made huge strides in this area but we are still way behind and our demos still don’t reflect the progress we made. Hopefully by the time 4.0 rolls around we’ll be in a different place entirely

  • Theme & Localization - Steve added some better theming to the new GUI builder. We think we can improve on this further and generally improve theming. Localization is something that regressed a bit from the old GUI builder which allowed for great automatic localization. We need something more "seamless" in this department

Onwards to 4.0

We have way more time for the 4.0 release so we can probably fit in more things than we did in 3.8. One of the difficulties in 3.8 is that a lot of the time between 3.7 and 3.8 was spent in summer months that are less productive. We fully expect 4.0 to be far richer in terms of features.

By the time 4.0 rolls around we should have two new major demos/tutorials in the Codename One Academy.

  • The Uber style application

  • A social network style application

We’ve already laid some ground work for the Uber style app and we plan to push it out before the end of the year. This continues the 3 major trends we are trying to drive:

  • Better design

  • Better docs

  • More "ready made templates"

Another big focus which we’ll see in 4.0 is quality and continuous integration. Our QA process is now open as part of our continuous integration support. We are now running automated tests of all our commits on device farms which should make future versions of Codename One far more stable.

We Need your Help

We got a record number of community pull requests during the 3.8 timeline, that is fantastic!

If you think we are doing a good job and appreciate our help please help us by:

Thanks for reading this far and if you have any thoughts/suggestions of any kind please let us know!

]]>
1,156 Codename One 3.8 is Live 0 0 0
Tutorial - What is Codename One blog/tutorial-what-is-codename-one.html Thu, 9 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/tutorial-what-is-codename-one.html

I published this video a while back but it was longer and a bit confusing (over 40 minutes). Since some developers watch it before getting into Codename One I thought it would be in order to streamline it into a more manageable length and transcribe the content like I did with the newer videos. I also cleaned it up a bit and the result is below.

You can also read the full transcript within this blog post and use the captions that are a part of the video.

Transcript

In this video I’m going to explain “what is Codename One” but I’m going to take the scenic route…​ I’m going to go through the full history of our platform and I’m going to explain the underlying technology. How it works. What makes it different from other tools at least at a high level. If you want a 3 minute hello world video this isn’t it, go to the download section of Codename One dot com and check out the 3 minute videos there. Here I try to go deeper.

Let’s start with the historic timeline of Codename One.

First there was LWUIT, Chen started this project at Sun Microsystems back in 2006-2007 with the goal of write once run anywhere that actually works for mobile phones. Back then there were only J2ME & RIM platforms and device fragmentation was a major problem. The standards were implemented poorly by the various vendors and they were very hard to use. So Chen took inspiration from Swing which was the leading GUI API on the desktop and adapted those concepts to mobile.

LWUIT was open sourced by Sun at JavaOne 2008. It became a huge success and was the top open source mobile project from Sun Microsystems but as LWUIT succeeded JavaME was dying. We had ports of LWUIT running on Android, RIM and the iPhone but we couldn’t support or publish them. This was due to company bureaucracy that didn’t improve as Oracle purchased Sun Microsystems.

In late 2011 we quit Sun and formed Codename One. We made a clean break from LWUIT by moving to a different package space and remodeling a lot of the API’s with a focus on smartphones. We ported everything to iOS, Android and other platforms. The big change was a change in concept. LWUIT was mostly a UI kit. Codename One was a completely inclusive environment that includes everything from the IDE plugin to the virtual machine, GUI builder and all the surrounding API’s. Codename One had a far larger and more ambitious scope than LWUIT.

This proved successful at least on a technical level as more than 100M apps were installed on devices relatively quickly and on a very diverse set of device types.

As the platform matured we expanded the scope of Codename One further and today we target not just Android & iOS. We target Universal Windows Platform which is the Microsoft standard for unified apps on Arm & intel. We target JavaScript which allows you to compile the Java applications into a JavaScript application including threads and everything so it can run in the browser. We even support desktop development which started as a curiosity due to user requirements and has picked up so well we use it ourselves.
Recently we added Kotlin support and are adding new features on a weekly basis.

So I gave all of that background but still didn’t get to “what the hell is it?”. The problem is that it’s so big and hard to explain so lets break it into 6 pieces and I’ll go over each one of these pieces in great detail soon. First: It’s a virtual machine for all devices. The virtual machine is the environment that lets Java run on the device in a portable way.

It’s an API for all devices. The Application Programming Interface provides us with functionality that is distilled commonality from the various devices.

It’s an IDE plugin. In fact this is the only piece of Codename One you actually need. By installing the IDE plugin we automatically deliver everything else on the list

It’s a set of tools from device simulators to theme designer, GUI builder etc. Those are delivered within the plugin but deserve their own bullet as they are technically the same regardless of the IDE you use.

The cloud based system is the most confusing part. No, you don’t need the cloud to run a Codename One app. The resulting app is native. However, to build iOS apps you need a Mac and xcode which is Apples development tool. To build a Windows UWP application you need a Windows machine and Visual Studio which is Microsofts development tool. Well, we have Macs and Windows and Linux machines in the cloud. We seamlessly translate your Java bytecode to native projects and compile them using the official tools such as xcode then return the binary directly to you so you can install it on your phone or upload it to apple, Microsoft, google etc. Cool right?

Here’s a fun fact. When we started pitching the company to investors we just stuck Codename One in the slides as a placeholder and investors liked it. We later on came up with the “idea” that these 6 different pieces constitute “one” product for “one code base” and that made sense as a company name.

But first lets start with the more in depth explanation of our VM’s. We currently have 3 virtual machines that we work with. Android has it’s own native Java support and we just use that. For desktop we use standard Java too. iOS doesn’t have a virtual machine and doesn’t allow JIT compilation which is a common practice in most virtual machines. 
Furthermore, historic attempts to build iOS virtual machines ran into issues of size and into problems with Apples frequent changes to their development toolchain. Our open source Parpar VM solves this problem by avoiding compilation. It translates Java bytecode to C which is one of the officially supported languages in iOS and much faster than Objective-C or Swift. We can then compile this C code using Apples compiler which guarantees that every change Apple makes will be easy to adapt to. This isn’t just a “theoretical claim”. Since we released our VM Apple migrated from 32 bit to 64 bit and added support for bitcode compilation. All of these worked out of the box with ParparVM with no change from us. Even native developers struggled with some of those changes which were mostly seamless to Codename One developers. ParparVM is very performant and can reach C levels of performance in some cases. It also produces a very small binary clocking under 5mb for applications which is very small for an iOS app. Similar attempts at JVM’s often produce binaries that exceed the 100mb limit… One of the cool things you can do with ParparVM is run the code using Apples xcode and debug or profile on the device. This is a very powerful way to discover things about the application and work with native OS code.

The UWP port uses iKVM which is a project that brought Java bytecode support to .net. Initially this project didn’t work with UWP but we worked with community members to update iKVM and published our iKVM fork as open source. This again means that we use native .net support when building windows applications.

The web port uses a 3rd party VM called TeaVM. This VM is very efficient at translating bytecode to JavaScript and can produce very small binaries when compared to similar technologies. We stripped out support for some JVM classes and as a result apps can be loaded with no cache within seconds. The VM supports threads by breaking down the code in very creative ways which keeps the code very efficient.

The Codename One Application Programming Interface is a single set of API’s that abstracts common device functionality you would expect to have on a mobile phone. This includes everything from widgets to camera, media, files, networking etc.

The trick with the API is our internal porting layer which is very clearly defined and relatively thin. That means 97% of Codename One’s code is in Java and this makes it more consistent to developers across platforms.

The user interface uses a lightweight UI approach which is a common term used to define Swing widgets. you might recall that Codename One was heavily inspired by Swing… Lightweight UI means Codename One draws most of its widgets itself and uses the native platform mostly as a canvas and event handling environment. I’ll discuss this more in depth later on…

The API tries to be simple and unified so it does the “right thing” when possible. We can do that because the API is statically linked. That means that when an app is compiled our API becomes a part of the application. This means that if we change the API it will never impact apps in production. This isn’t true for native OS API’s who need to change very carefully so they won’t break applications that might rely on edge case functionality.

Codename One ships plugins for the 3 top Java IDE’s specifically IntelliJ, Eclipse and NetBeans.

The plugin literally includes everything that’s a part of Codename One. Since the build happens in the cloud servers you don’t need to install native OS tools or anything like that.

Essentially the plugins are relatively “stupid” as they mostly invoke Ant and our tools such as the simulator etc. This is a good thing. I use NetBeans as I’m used to it from my years at Sun. However, almost everything I do here is identical if you do it in Eclipse or IntelliJ. Recently we started making them even “dumber” by moving the settings from the IDE specific settings UI to the Codename One Settings tool which is common between all 3 IDE’s. This allowed us to move much faster.

The tools include 4 major groups. The first is the group of build tools which is really the ant script you have in the project and some extensions for that.

The device simulator includes skins representing various device types that you can download dynamically. It also includes a lot of additional functionality to test device behavior such as network monitor, performance analyzer, component inspector etc.

The resource editor also known as the Codename One Designer is our workhorse tool dating back to the days of LWUIT. It allows you to theme the application, localize it, manage builtin images and even included the old GUI builder of Codename One.

The new GUI builder is far more powerful and more closely matches common Java GUI builders in terms of functionality and technology.

I’ve discussed the cloud build before but it’s important enough to recap. We have Macs, Windows and Linux machines that do the build for you. This is more than just having the build platforms. Setting up the native environments and all the prerequisites for a proper build is a nightmare. The cloud build servers essentially mask this whole process completely. Notice that we have an offline build feature that allows you to skip the cloud build but we don’t recommend that. It’s mostly for government or highly regulated industries where the word cloud is a synonym to “no management approval”. In those cases offline build becomes a lesser of two evils.

The cloud is mostly abstracted, you just right click the project and send a build. Everything else is seamless thanks to the tie in between the build tools and the build severs.

You don’t need to install anything other than the plugin. Not for Android or iOS. You can work with Mac, Windows or Linux and everything should “just work”.

This is a huge contributor to the seamlessness and simplicity of Codename One that allows you to get started “right away”.

Codename One’s UI uses a lightweight UI approach which is a common term used to define Swing widgets. As I mentioned before Codename One draws most of its widgets itself and uses the native platform mostly as a canvas and event handling environment. Lightweight frameworks are very common especially in Java where Swing and JavaFX are both lightweight. QT is another example of a lightweight framework. Heavyweight frameworks include SWT and AWT from the Java side of the fence and other common tools such as Xamarin, Appcelerator etc.

As I mentioned before lightweight frameworks draw their own widgets. A native framework needs to have a native widget for every API. That might not sound like a big deal but platforms have very nuanced differences in threads, events etc. This means very nuanced bugs on the heavyweight framework side. In fact many heavyweight frameworks don’t define themselves as write once run anywhere tools since that is so hard to accomplish with this architecture.

A lightweight architecture is really complex internally. Codename One is a hugely complex API developed for over a decade. Heavyweight API’s tend to be smaller and thinner wrappers around the native OS code. For the developer that means running more into OS differences since the framework doesn’t really hide them from you.

Layout, theming, translation and localization are all really hard to do in a portable way. Heavyweight frameworks make you copy data to native OS structures and formats. They make you maintain images in the native OS locations which is radically different between iOS and Android. In lightweight framework this is all portable.

The tools of lightweight frameworks are generally all inclusive which is usually not the case for heavyweight frameworks. This isn’t always true for all cases but it’s a very common concept. This becomes obvious with hardware requirements where heavyweight frameworks require a Mac for iOS development. That isn’t true for Codename One. The cloud build works thanks to the lightweight architecture as the simulator can have better fidelity.

So lets compare concrete advantages of lightweight vs. heavyweight. Lightweight allows for extreme portability where what you see in the desktop simulator becomes a closer facsimile of what you will see in the device.

It also gives you far more control as most of the code is written in Java you can override almost any behavior in a very portable way. So lightweight is far more customizable. The flip side is that heavyweight can be more performant in some cases. That’s debatable as performance is a very complex beast. I find that caching is by far the most important performance optimization and it’s also very portable. Usually code that is slow on Android will be slow on iOS so a smart optimization will work well for a lightweight framework.

That essentially means you get a more consistent result overall. Bugs are very portable too and so a fix in iOS will often fix an issue for the Android version as well. The heavyweight widget approach will have better consistency with the native OS. For instance in a Samsung customized device the buttons might match Samsungs styling more closely. I’m not sure that’s an advantage. When I program my button to have a specific color I want it to keep that color and behavior. I don’t want a device manufacturer or OS vendor to change the look of my application after the fact.

In general lightweight frameworks are MUCH easier to use as they are simpler. In some cases native code in the heavyweight approach is easier but this isn’t as true with Codename One where heavyweight widgets can be integrated with lightweight widgets… Codename One is still a lightweight framework but if you need a complex heavyweight widget such as Google Maps you can just embed it into a Codename One application and it will work across platforms consistently!

Which brings us to this. Codename One supports native interfaces, that allows you to invoke native code from Java and that native code can create an Android view or iOS UIView etc which you can just add into the component hierarchy.

One of the cool things we allow is z-ordering of native widgets which means you can add a google map and add lightweight components that reside on top of that map such as components representing cars, landmarks etc.

The Codename One lightweight API is VERY performant. E.g in iOS it is implemented on top of Open GL.

The lightweight API is VERY customizable, you can literally override the paint behavior of every component or place a drawing region and just draw on top of everything…

Thanks for baring with me. I hope this was helpful and informative.

]]>
1,157 Tutorial - What is Codename One 0 0 0
Mac Appstore Builds & Device Farms blog/mac-appstore-builds-device-farms.html Wed, 8 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/mac-appstore-builds-device-farms.html

Steve has been pretty busy. We have new support for Mac Appstore builds as part of our desktop build process. That means you can build a signed Mac desktop app with Codename One which required a bit of work with previous releases. He also adapted our automated tests for Codename One so they would run on device farms and test against major versions of Android.

Mac Builds

In order to use these changes you’ll need to install the latest release candidate for Codename One which includes an updated version of Codename One Settings. Once you do that check out this new section of the developer guide that covers the process of building and signing the Mac app.

Mac OS is locked down to some degree and if you ship an app outside of the app store you might run into problems. With this you can get the same benefit as you would get with itunes for iOS app sales on the Mac desktop. Since we already support UWP shipping through the Microsoft Windows Store is already possible.

Device Farm Testing

One of the more important features we are still lacking is device farm testing. We have support for auto-tests and quite a few other capabilities but running tests automatically for all devices using a cloud based device farm is something we just never got around to do.

Apparently we’re already there, when we commit a change to Codename One or a pull request it will now test that change on the simulator as well as on multiple versions of Android out of the box. The complexity of adding automated iOS tests is due to the complexity of the fact that we are testing an "unpublished" version of Codename One but this should be relatively easy to accomplish for your apps.

Steve also added a status badge to the project that highlights when all the tests pass in green so you know if something is failing just by looking at our github project page.

We hope to add automated iOS tests for better stability of Codename One but as I mentioned before this isn’t trivial.

Device Farms and Your Projects

We already discussed CI support for your project but not the full TDD process. Now that we have a bit more experience with these device farms we hope to leverage that experience to make TDD trivial in Codename One.

This is hugely important, when you build multiple applications TDD allows you to change code rapidly without concern that you might run into a regression. This is hugely important in the volatile world of mobile development where new mobile OS’s/devices come out on a daily basis. Codename One does a lot of the heavy lifting here but no technology is perfect. With this approach you can instantly test on devices that you don’t have in your possession and instantly get a notice if a commit of your broke something.

That’s pretty cool…​

We’ll hopefully post a more in-depth tutorial on this within the next couple of months. Notice that this will require the synchronous build functionality of the enterprise accounts.

]]>
1,158 Mac Appstore Builds & Device Farms 0 0 0
Codefreeze for 3.8 blog/codefreeze-for-3-8.html Tue, 7 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/codefreeze-for-3-8.html

Codename One 3.8 should land one week from now and from later today we are effectively in code freeze. Please update to the release candidate of 3.8 tomorrow morning to help us track some last minute bugs and regressions. As is usually our process we will release updates as necessary with code reviews and next week we won’t have the usual set of releases as we’ll take some time off.

We will branch to the 3.8 branch in our repository and every change to the branch will be cherry picked individually.

During this week of code freeze we’ll try to update the documentation and prepare everything for the release. There are many important features in this release but as is our approach in recent releases the emphasis is strongly on refinement. If you run into any issue please file the issue ASAP so we can move quickly and update the release if necessary.

Upcoming Milestones

As you might recall we announced a switch to major version milestones which means the next release is 4.0. Here are the coming releases:

  • 4.0 - Scheduled for March 6th 2018

  • 5.0 - Scheduled for July 17th 2018

  • 6.0 - Scheduled for November 14 2018

You can track these milestones and the related tasks in our github project here.

]]>
1,159 Codefreeze for 3.8 0 0 0
Tutorial - Working with Images and Densities blog/tutorial-working-with-images-densities.html Thu, 2 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/tutorial-working-with-images-densities.html

We had a lot of posts covering images, multi-image and density. It’s one of the more challenging concepts to explain to developers coming from a desktop development background. Density is a simple concept to explain but the implications of it are sometimes counter-intuitive e.g. which device has higher density: Your phone or your 4k TV?

The answer is your phone and I explain why in the video.

I also discuss the need to adapt UI’s for tablets to make better use of the available space although I don’t go deep into that subject. It might be worth covering that in a separate video, I discuss this in the academy though…​

]]>
1,160 Tutorial - Working with Images and Densities 0 0 0
Pixel Perfect - Text Input (Part 2) blog/pixel-perfect-text-input-part-2.html Wed, 1 Nov 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/pixel-perfect-text-input-part-2.html

Last week we discussed the first part of working with text components and left some big tasks for this week. This week we’ll try to address the remaining issues with text input and make it easier to construct UI’s. This is crucial as we’ll be going into code freeze soon and we need enough time to iron out the issues in the text input.

Last week I ended the post with this set of tasks:

  • The floating hint API is bad - we will need a new API to represent text field and label together both for iOS and Android

  • We will need a solution for the special case characters or icons that remain when typing and aren’t there as a hint. There are several tricks we can do but I need to give this some thought especially how they will work with something like a floating hint

  • We need to handle error messages in a standard way

All of these can be solved by adding a new component type that will replace the problematic FloatingHint hack. One of the mistakes we made with Codename One was following the conventions of Swing where the label and component are separate. However, this isn’t really the case and theming can have a deep impact on this. E.g. the label on Android should be above the text field and float into place while the label in iOS should be next to it.

This means we need a new API that will encapsulate the text component and the label next to it. This new API should be consistent, elegant and most importantly: "seamless"…​

TextComponent & TextModeLayout

Let’s start with the end of last weeks post:

Final result
Figure 1. Final result

Back then I wrote that using the FloatingHint as such:

TableLayout tl = new TableLayout(3, 2);
Form f = new Form("Pixel Perfect", tl);

TextField title = new TextField("", "Title");
TextField price = new TextField("", "Price");
TextField location = new TextField("", "Location");
TextArea description = new TextArea("");
description.setHint("Description");

f.add(tl.createConstraint().horizontalSpan(2), new FloatingHint(title));
f.add(tl.createConstraint().widthPercentage(30), new FloatingHint(price));
f.add(tl.createConstraint().widthPercentage(70), new FloatingHint(location));
f.add(tl.createConstraint().horizontalSpan(2), new FloatingHint(description));

f.show();

Besides being verbose this looked bad on iOS:

Not horrible but not exactly
Figure 2. Not horrible but not exactly "iOS"

So we need something better…​ This code produces the exact same look on Android (more on that soon) but it does that while producing a good looking result on iOS too:

TextModeLayout tl = new TextModeLayout(3, 2);
Form f = new Form("Pixel Perfect", tl);

TextComponent title = new TextComponent().label("Title");
TextComponent price = new TextComponent().label("Price");
TextComponent location = new TextComponent().label("Location");
TextComponent description = new TextComponent().label("Description").multiline(true);

f.add(tl.createConstraint().horizontalSpan(2), title);
f.add(tl.createConstraint().widthPercentage(30), price);
f.add(tl.createConstraint().widthPercentage(70), location);
f.add(tl.createConstraint().horizontalSpan(2), description);
f.setEditOnShow(title.getField());

f.show();
An iOS native
Figure 3. An iOS native "feel" with the exact same code

Why a New Layout?

As you can see from the code and samples above there is a lot going on under the hood. On Android we want a layout that’s similar to TableLayout so we can "pack" the entries. On iOS we want a box layout Y type of layout but we also want the labels/text to align properly…​

The new TextModeLayout isn’t really a layout as much as it is a delegate. When running in the Android mode (which we refer to as the "on top" mode) the layout is almost an exact synonym of TableLayout and in fact delegates to an underlying table layout. In fact there is a public final table instance within the layout that you "can" refer to directly…​

There is one small difference between the TextModeLayout and the underlying TableLayout and that’s our choice to default to align entries to TOP with this mode. It’s important for error handling which I’ll cover below.

When working in the non-android environment we use a BoxLayout on the Y axis as the basis. There is one thing we do here that’s different from a default box layout and that’s grouping. Grouping allows the labels to align by setting them to the same width, internally it just invokes Component.setSameWidth(). Since text components hide the labels there is a special group method there that can be used. However, this is implicit with the TextModeLayout which is pretty cool.

TextComponent

The text component uses a builder approach to set various values e.g.:

TextComponent t = new TextComponent().
    text("This appears in the text field").
    hint("This is the hint").
    label("This is the label").
    multiline(true);

I think the code is pretty self explanatory and more convenient than typical setters/getters. It automatically handles the floating hint style of animation but does that more smoothly using layout & style animation instead of the outdated morph animation.

We also added some pretty spiffy new features to address the points above…​

Error Handling

I’ve added support to the validator class for text component and it should "just work". But the cool thing is that it uses the material design convention for error handling!

So if we change the sample above to use the validator class:

TextModeLayout tl = new TextModeLayout(3, 2);
Form f = new Form("Pixel Perfect", tl);

TextComponent title = new TextComponent().label("Title");
TextComponent price = new TextComponent().label("Price");
TextComponent location = new TextComponent().label("Location");
TextComponent description = new TextComponent().label("Description").multiline(true);

Validator val = new Validator();
val.addConstraint(title, new LengthConstraint(2));
val.addConstraint(price, new NumericConstraint(true));

f.add(tl.createConstraint().horizontalSpan(2), title);
f.add(tl.createConstraint().widthPercentage(30), price);
f.add(tl.createConstraint().widthPercentage(70), location);
f.add(tl.createConstraint().horizontalSpan(2), description);
f.setEditOnShow(title.getField());

f.show();

You would see something that looks like this on Android:

Error handling when the text is blank
Figure 4. Error handling when the text is blank
Error handling when there is some input (notice red title label)
Figure 5. Error handling when there is some input (notice red title label)
On iOS the situation hasn't changed much yet
Figure 6. On iOS the situation hasn’t changed much yet

The underlying system is the errorMessage method which you can chain like the other methods on TextComponent as such:

TextComponent tc = new TextComponent().
    label("Input Required").
    errorMessage("Input is essential in this field");

InputComponent & PickerComponent

To keep things simple I focused on the the TextComponent but after the initial commit we decided to move to a more flexible system where other component types could be laid out in a similar way to maintain consistency with Android/iOS.

To keep the code common and generic we use the InputComponent abstract base class and derive the other classes from that. PickerComponent is currently the only other option. We considered options such as CheckBox or OnOffSwitch but both are problematic in some ways so we’d like to give them a bit more thought.

A picker can work with our existing sample using code like this:

TextModeLayout tl = new TextModeLayout(3, 2);
Form f = new Form("Pixel Perfect", tl);

TextComponent title = new TextComponent().label("Title");
TextComponent price = new TextComponent().label("Price");
TextComponent location = new TextComponent().label("Location");
PickerComponent date = PickerComponent.createDate(new Date()).label("Date");
TextComponent description = new TextComponent().label("Description").multiline(true);

Validator val = new Validator();
val.addConstraint(title, new LengthConstraint(2));
val.addConstraint(price, new NumericConstraint(true));

f.add(tl.createConstraint().widthPercentage(60), title);
f.add(tl.createConstraint().widthPercentage(40), date);
f.add(location);
f.add(price);
f.add(tl.createConstraint().horizontalSpan(2), description);
f.setEditOnShow(title.getField());

f.show();

This produces the following which looks pretty standard:

Picker component taking place in iOS
Figure 7. Picker component taking place in iOS
And in Android
Figure 8. And in Android

As I mentioned this is pretty obvious once we got through everything else. The one tiny caveat is that we don’t construct the picker component using new PickerComponent() instead we use create methods such as PickerComponent.createDate(new Date()). The reason for that is that we have many types of pickers and it wouldn’t make sense to have one constructor.

Underlying Theme Constants

These varying looks are implemented via a combination of layouts, theme constants and UIID’s. The most important UIID’s are: TextComponent, FloatingHint & TextHint.

There are several theme constants related that can manipulate some pieces of this functionality:

  • textComponentErrorColor a hex RGB color which defaults to null in which case this has no effect. When defined this will change the color of the border and label to the given color to match the material design styling. This implements the red border underline in cases of error and the label text color change

  • textComponentOnTopBool toggles the on top mode which makes things look like they do on Android. This defaults to true on Android and false on other OS’s. This can also be manipulated via the onTopMode(boolean) method in InputComponent however the layout will only use the theme constant

  • textComponentAnimBool toggles the animation mode which again can be manipulated by a method in InputComponent. If you want to keep the UI static without the floating hint effect set this to false. Notice this defaults to true only on Android

  • textComponentFieldUIID sets the UIID of the text field to something other than TextField this is useful for platforms such as iOS where the look of the text field is different within the text component. This allows us to make the background of the text field transparent when it’s within the TextComponent and make it different from the regular text field

Final Word

We went through a lot to get to this point but there is quite a bit more that we need to address:

  • Other component types - we need better support for things such as on-off switches etc.

  • We didn’t implement the material design feature of icon or symbol on the side of a text field, it’s something we might want to address in a future update. I’m not sure how this will play nicely with the animation

  • There are some cool features and iOS refinements we’d like to add, e.g. on iOS icons are common next to the labels and error handling there should be better

  • The properties instant UI code should migrate to this as soon as possible, right now it’s not practical since we don’t have checkbox/on-off support but once those are in place this is something we should support

Overall I hope the work speaks for itself and that soon we’ll be able to say that our UI matches and hopefully exceeds the refinement of OS native code.

]]>
1,161 Pixel Perfect - Text Input (Part 2) 0 0 0
New Milestones and Features blog/new-milestones-features.html Tue, 31 Oct 2017 00:00:00 +0200 shai https://www.codenameone.com/blog/new-milestones-features.html

We will enter code freeze for Codename One 3.8 next week and have a lot of things to clear off the table in order to get there!
The first order of business is that there will be no Codename One 3.9…​ Instead we will go right to 4.0 and switch to major version number update scheme only.

This will align us better with industry standards. We spent ages in 3.x and far less in 1.x or 2.x numbering. That doesn’t make sense. It’s hard to tell what feature is big enough for a "major version number update" and it doesn’t indicate as much with the fast pace of releases. So switching to major version numbers in terms of milestones will simplify and give a better indication of the work we put in.

We will enter code freeze next week on the 7th of November with release of 3.8 scheduled for the 14th.

Eclipse Oxygen Issue

Some eclipse users with the latest version of Oxygen (4.7.1a) have complained about an issue with the simulator. This seems to be a bug or change in the latest version of eclipse that we are trying to nail down.

There is a workaround mentioned in the stackoverflow answer which should be pretty easy to follow. However, if this doesn’t work as expected you might want to use an older version of eclipse until this is fixed. We support versions from Neon 2 and up.

On Top Sidemenu

We switched this this on by default this weekend and already found a regression that eluded us before with the overflow menu. This is now fixed in the GIT but there might be more issues, hopefully we’ll be able to resolve everything before the code freeze so please check your code and report back to us ASAP if you encounter issues!

Automated Tests

While we have some internal tests for Codename One most of them are ad-hoc and not exactly organized. Steve setup the groundwork with a new automated test repository that is now also bound to our travis CI setup in github.

This means you can add a test case together with a bug report or pull request. This could help tremendously in improving the long term stability of Codename One!

callSeriallyOnIdle

One of the features that we added and totally slipped under the radar is the new callSeriallyOnIdle method which essentially works exactly like callSerially but doesn’t perform its work until the device is idling. If you have multiple entries in this call it will serialize them and won’t do them all at once.

The main use case is for tasks that might be CPU intensive but should be done on the EDT. This allows you to postpone such a task for a moment where the EDT will sleep anyway. A good example is processing images for scrolling. You would want the scrolling to still be smooth with the placeholder images and have the processing only when the user isn’t active.

This feature is a drop-in replacement for callSerially and in the next update will be available in the CN class too as well as Display.

Caching cn1lib & SMS Activation Improvements

Yaakov added a cn1lib for caching objects as JSON representations or as he put it in his description:

This CodenameOne library enables apps to easily cache data downloaded from the app server (or any other source) in the device’s file system. Cached data is saved in JSON format, one file per object type. Caching data can potentially improve the app’s performance and responsivity, cut down on cell data usage, extend battery life, and enable 'offline mode'.

Diamond fixed an NPE in both of the cn1libs for SMS activation so if you use one of them I suggest updating the library to the latest to avoid that.

]]>
1,162 New Milestones and Features 0 0 0
Tutorial - Creating Lists blog/tutorial-creating-lists.html Thu, 26 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tutorial-creating-lists.html

Some of our how do I videos are so old it’s embarrassing…​ This is especially true for some core concepts videos as they were some of the first ones we made. The worst part is that these are probably the most important videos we have as they are viewed by developers completely new to Codename One. We had two videos covering creations of lists and both of them used com.codename1.ui.List with the old GUI builder!

That’s embarrassing…​ Especially considering this.

So I’ve created a new video that’s more up to date. I discuss lists as a concept but I really describe box layout Y and the InfiniteContainer class. It’s a big improvement over the old videos.

]]>
1,163 Tutorial - Creating Lists 0 0 0
Pixel Perfect - Text Input blog/pixel-perfect-text-input.html Wed, 25 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/pixel-perfect-text-input.html

I started working on this post back in August and so much happened in between I just had to scrap the whole thing and start over again…​ I’ve discussed buttons before and now it’s time for TextField & TextArea. But before we go into that lets take a moment to talk about the problems that arose from the last installment of pixel perfect.

I made a lot of changes to Codename One but there was one particularly painful change: caps text. This proved to be a far more controversial change than I had intended and I had an interesting debate with Nick. I’d appreciate participation in the thread there or here. The gist of the debate is how can we change functionality people rely on without disrupting working code. I honestly didn’t think the caps text change was a big change or would cause the problems it did. However, having said that it’s really hard to rollout a feature without disrupting developers occasionally.

IMO one of the best ways around it is if more people used the source when working with Codename One:

  • You would see problematic things before they make it to the build servers

  • You would be able to patch/fix issues at least locally

  • Debugging into the code would be easier for you

So please use the source and help us all make a better product!

On Top Sidemenu Update

One of the things that did get some scrutiny over the past few weeks is the on-top side menu that caused quite a few regressions. We now have an up to date version of this code which should work nicely with the on-top side menu so if all goes according to plan we’ll flip the switch over this weekend.

As a reminder, we will transition from the original Toolbar sidemenu implementation to a completely new approach that solves a lot of the issues with the old side menu and looks closer to modern native side menus by residing on top of the UI. You can toggle this side menu on/off by invoking Toolbar.setOnTopSideMenu(true); in your init(Object) method (you should use false to turn it off).

Text Components on Android

The material design guideline includes several modes and states for text fields but generally this image represents the desired native look reasonably well:

Native Android text fields
Figure 1. Native Android text fields

One thing that isn’t clear from the screenshot is the floating label which is now becoming standard in Android. I think we’ll probably need a better abstraction for that one…​ Right now a similar UI in Codename One looks like this:

The before shot
Figure 2. The before shot

The current code for doing this looks like this:

TableLayout tl = new TableLayout(3, 2);
Form f = new Form("Pixel Perfect", tl);

TextField title = new TextField("", "Title");
TextField price = new TextField("", "Price");
TextField location = new TextField("", "Location");
TextArea description = new TextArea("");
description.setHint("Description");

f.add(tl.createConstraint().horizontalSpan(2), new FloatingHint(title));
f.add(tl.createConstraint().widthPercentage(30), new FloatingHint(price));
f.add(tl.createConstraint().widthPercentage(70), new FloatingHint(location));
f.add(tl.createConstraint().horizontalSpan(2), new FloatingHint(description));

f.show();

What’s Wrong

There are several problems when we look at the screenshots side by side:

  • No margin

  • Text field border is in Android 4.x style instead of the material design line style

  • Font color is wrong for the title label and the color of the bottom line (both should match)

  • The font is wrong both in the title label and the text

  • Floating hint makes sense in Android but we might need to rethink it for iOS

  • Price has a special case $ symbol prefix that has the color of the title label

Besides those things that are obvious from the screenshots here are a few other things that might be a problem:

  • Focus color behavior is incorrect - in native code the color of the title text and underline change on focus but not the color of the text itself

  • Error message labels should appear below in red

  • Hint text looks different from native hint text

As before I’ll try going through this list and I’ll try fixing these so the look will be more native.

Margin & Border

These are probably the easiest fixes to do with the exception of the colors. After making a few changes to the margin and the border and applying that to the theme we can now see this:

After applying margin and underline border change
Figure 3. After applying margin and underline border change

These were both pretty easy to fix in the designer tool, I just edited the Android native theme and changed the styling for the border and margin. I also needed a style for FloatingHint which matches the label on top. It needs to match the margin of the text field now.

Colors

One of the problem in the simulator screenshot process is that it hides the currently editing text, in the picture below it isn’t clear that the text we are editing is black while the line is blue and the label on top is also blue.

The editing line and label have the same color
Figure 4. The editing line and label have the same color

Right now we don’t have any way to define a standard color palette in the theme. So I can’t pick a color constant and need to pick a specific color for the border to work with. This is something we should probably address in the theme toolchain but it’s not a trivial fix and requires some thought. In general I’d like to define a color scheme and automatically generate the native Android colors.xml etc. to create a more uniform UX.

In terms of focus I had to do a bit more work. I added a new method to Component shouldRenderSelection() which can be overriden to indicate whether selection is shown. Normally in touch devices the selected state only appears when touching the screen so this is essential for this case.

Fonts

The fonts are also a pretty easy change. I used the native regular at first but then comparing it to the native UI it seems that the light version of the font is closer to the design.

Better sized roboto font
Figure 5. Better sized roboto font

I also styled the FloatingHint text with a smaller version of the same font and adapted the TextHint to use the same font with a gray color.

What’s Next

All of these changes should be up in the next update and might disrupt existing code. As I mentioned before this is expected as we move the product forward. In the next installment I’ll try to address the remaining issues and hopefully fine tune some of the behaviors:

  • The floating hint API is bad - we will need a new API to represent text field and label together both for iOS and Android

  • We will need a solution for the special case characters or icons that remain when typing and aren’t there as a hint. There are several tricks we can do but I need to give this some thought especially how they will work with something like a floating hint

  • We need to handle error messages in a standard way

I hope to address all of those quicker than I got around to doing this installment…​

]]>
1,164 Pixel Perfect - Text Input 0 0 0
TIP: Map Layout Manager blog/tip-map-layout-manager.html Tue, 24 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-map-layout-manager.html
The information in this blog post is slighly out of date. Check out the newer blog post that covers positioning components on the map.

I’ve recently added a segment to the online course covering the native maps. This segment is a part of a larger trend towards the upcoming Uber demo which we will release before the end of the year. As part of that work I did some initial "half baked" work on a map layout manager.

The motivation was to position components on top of the map in an effective way, I hacked this a bit and mixed some logic from the map and the layout thus breaking some of the separation but it’s still a pretty cool demo…​

The main thing to learn here is just how easy it is to build your own layout manager, don’t shy away from doing that if you need more control. The case for maps is great because we want to position components based on longitude/latitude values and this works perfectly with the layout manager semantics.

This is the layout manager I created, you’ll notice the code is really simple:

public class MapLayout extends Layout implements MapListener {
    private static final String COORD_KEY = "$coord";
    private MapContainer map;
    private Container actual;
    public MapLayout(MapContainer map, Container actual) {
        this.map = map;
        this.actual = actual;
        map.addMapListener(this);
    }

    @Override
    public void addLayoutComponent(Object value, Component comp, Container c) {
        comp.putClientProperty(COORD_KEY, (Coord)value);
    }

    @Override
    public boolean isConstraintTracking() {
        return true;
    }

    @Override
    public Object getComponentConstraint(Component comp) {
        return comp.getClientProperty(COORD_KEY);
    }

    @Override
    public boolean isOverlapSupported() {
        return true;
    }

    @Override
    public void layoutContainer(Container parent) {
        for(Component current : parent) {
            Coord crd = (Coord)current.getClientProperty(COORD_KEY);
            Point p = map.getScreenCoordinate(crd);
            current.setSize(current.getPreferredSize());
            current.setX(p.getX() - current.getWidth() / 2);
            current.setY(p.getY() - current.getHeight());
        }
    }

    @Override
    public Dimension getPreferredSize(Container parent) {
        return new Dimension(100, 100);
    }

    @Override
    public void mapPositionUpdated(Component source, int zoom, Coord center) {
        actual.setShouldCalcPreferredSize(true);
        actual.revalidate();
    }
}

I can use it like this:

Form mapDemo = new Form("Maps", new LayeredLayout());
mapDemo.getToolbar().addMaterialCommandToSideMenu("Hi", FontImage.MATERIAL_3D_ROTATION, e -> {});
if(BrowserComponent.isNativeBrowserSupported()) {
    MapContainer mc = new MapContainer(JS_API_KEY);
    mapDemo.add(mc);
    Container markers = new Container();
    markers.setLayout(new MapLayout(mc, markers));
    mapDemo.add(markers);

    Coord moscone = new Coord(37.7831, -122.401558);
    Button mosconeButton = new Button("");
    FontImage.setMaterialIcon(mosconeButton, FontImage.MATERIAL_PLACE);
    markers.add(moscone, mosconeButton);

    mc.zoom(moscone, 5);
} else {
    // iOS Screenshot process...
    mapDemo.add(new Label("Loading, please wait...."));
}
mapDemo.show();
]]>
1,165 TIP: Map Layout Manager 0 0 0
Tutorial - Background, Images, Borders and Gradients blog/tutorial-background-images-borders-gradients.html Thu, 19 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tutorial-background-images-borders-gradients.html

Backgrounds include a lot of nuance, especially the part covering the priorities which I start with in the tutorial below. Because of that this is one of the longer how do I videos I’ve done in a while as there is just so much to cover. I still had to cut a lot of stuff away and focus almost entirely on the designer tool.

Styling in code isn’t covered at all in the video. I think most developers who would prefer hand coding would probably feel more comfortable learning from the developer guide which covers all of that already. I also didn’t cover the CSS support although there is some discussion of that in the academy.

]]>
1,166 Tutorial - Background, Images, Borders and Gradients 0 0 0
Don't Touch That Code blog/dont-touch-that-code.html Wed, 18 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/dont-touch-that-code.html

Last week scrolling broke and we had a few relatively complex regressions. This can be traced back to a change we did to the getComponentAt(x, y) method, this change in itself fixed a problematic bug but triggered far worse bugs and we just had to revert the whole thing…​

So why did we even do a change to a method that’s so deep in the code and so risky?

The logic behind that is to prevent situations like this. getComponentAt(x, y) is a remarkably important method which is used all over the place to implement touch interface logic. Unfortunately, as one of those methods that evolved it became "unwieldy" and because it’s so deep we tried to avoid changing it…​

Don’t touch it! It works!
— Hacker Culture

These types of methods exist in every project and they form code rot around them. That’s why we try to avoid that mentality and try to improve on the things that are working. Sometimes in cases such as this, we fail. But more often than not we succeed and that is truly important.

Why this Method?

As I mentioned before getComponentAt(x, y) is used internally all over the place. The benefit of code reuse goes well beyond basic aesthetics and code reduction. It guarantees better test coverage, increased stability and performance as the compiler & optimizations can do much more.

However, that also means a method can become a point of hacks that impact everyone. So now that we reverted the change we’ll need to go over every use case and surgically remove the usage of this method so we can understand why these regressions happened and hopefully make the code more sensible.

Unfortunately, this delays our planned migration to the on-top side menu which we were hoping to do this week. Right now the menu just doesn’t work properly with peer components which was the exact fix we aimed at here.

What does that Mean?

It means we might not be able to finish everything we wanted for the 3.8 release and need to move even more things to 3.9. Accessibility is something I specifically wanted to deliver for 3.8 but it’s a non-trivial feature so it probably won’t make it with our existing commitments.

]]>
1,167 Don't Touch That Code 0 0 0
TIP: Use Weak References blog/tip-weak-references.html Tue, 17 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-weak-references.html

One of the less familiar features of Java is the mess of weak/soft/phantom references. This is a confusing mess and it’s compounded by the fact that other languages (such as the reference counting Swift/Objective-C) have used these terms with a different meaning. To simplify this weak references in a garbage collected language allows you to keep a pointer (reference) to an object that won’t force it to stay in RAM.

This can be made clearer by this sample:

class MyObject {
    private Object otherObjectInCache;

    // ... rest of code
}

Lets imagine that otherObjectInCache is an object that’s expensive to load e.g. an image. As long as MyObject is in RAM the otherObjectInCache won’t be removed from RAM and this might take up a lot of memory that could be used elsewhere…​

The thing is we don’t know. We might have a lot of RAM but we might not, this varies based on the device and checking OS memory is problematic in multi-tasking OS’s and GC languages. What we really want is:

  • If we have extra RAM we want to keep the object in memory

  • If we run out of RAM we are happy to just reload the object as needed

A weak reference allows you to keep a reference to an object that can still be removed by the GC if it needs the RAM. Standard Java code for weak references looks like this:

import java.lang.ref.WeakReference;

class MyObject {
    private WeakReference otherObjectInCache;

    public void codeThatNeedsObject() {
        if(otherObjectInCache != null) {
             Object cachedData = otherObjectInCache.get();
             if(cachedData != null) {
                // use cachedData
                return;
             }
        }

        // object was gc'd
       Object cachedData = loadObject();

        // store the object in the weak reference
        otherObjectInCache = new WeakReference(cachedData);
    }
    // ... rest of code
}

Notice that once the cachedData variable is not null we have a hard reference in RAM so that object won’t be GC’d until hard references are gone.

This works rather well in Codename One and elsewhere but there is an even better option: SoftReference. Weak references are GC’d relatively aggressively but this isn’t as true for SoftReference. Unfortunately not all target OS’s of Codename One support SoftReference so we can’t support the official JDK version of that. That’s why we have a slightly different API that you should probably use:

static import com.codename1.ui.CN.*;

class MyObject {
    private Object otherObjectInCache;

    public void codeThatNeedsObject() {
        if(otherObjectInCache != null) {
             Object cachedData = extractHardRef(otherObjectInCache);
             if(cachedData != null) {
                // use cachedData
                return;
             }
        }

        // object was gc'd
       Object cachedData = loadObject();

        // store the object in the weak reference
        otherObjectInCache = createSoftWeakRef(cachedData);
    }
    // ... rest of code
}
This relies on a change to the CN class that will be a part of next weeks update, right now you can use the Display class which provides this functionality
]]>
1,168 TIP: Use Weak References 0 0 0
Tutorial - Versioned Builds/Repeatable Builds blog/tutorial-versioned-builds-repeatable-builds.html Thu, 12 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tutorial-versioned-builds-repeatable-builds.html

Versioned builds are one of the more confusing features of Codename One mostly because the word "version" is so overloaded with meaning. The feature itself is remarkably simple as explained in this video tutorial.
Versioned builds allow you to build against a fixed point release version of Codename One, that means that if we break something in the build servers you can still work against the last major version that we released.

This is also useful in cases where you want to see if something failed recently and also for getting consistent build results so you can update Codename One versions at your own pace. There is a limit to how far back we can support. The mobile world moves very fast so keeping support for older versions of Codename One is just impractical in the long term. However, this allows you to slow down the fast pace of progress a little bit.

]]>
1,169 Tutorial - Versioned Builds/Repeatable Builds 0 0 0
Media Controls and Print Developer Guide blog/media-controls-print-developer-guide.html Wed, 11 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/media-controls-print-developer-guide.html

I did a lot of work on the developer guide PDF making it more suitable to print, as part of this work I submitted the guide to Amazons KDP which means you can now order a physical book of the developer guide. I reduced the page count significantly for lower cost and image size requirements. As a result the book is much smaller but contains the exact same information in a denser package.

You can order the book on Amazon, make sure you select the print edition.

Media Controls & Stability

Steve did a lot of work on media over the past week and it should be more stable/consistent across platforms. One of the big changes is support for native video controls which up until now was platform specific. Starting with the next update you would be able to hint to the player whether you want native playback controls or not by using this code to show the native controls:

someMedia.setVariable(Media.VARIABLE_NATIVE_CONTRLOLS_EMBEDDED, true);

You can use false to hide them.

On Top Sidemenu Update & Regressions

We’ve put a lot of effort into refining the on-top sidemenu which is already showing a lot of promise. Unfortunately one of the changes we made caused a regression to the general functionality of Codename One. We’ve posted a fix and might push an earlier update to the libs before Friday to workaround this issue.

The on-top side menu is still off by default and will remain that way with the coming update due to these regressions. We still think it’s important to turn this on by default with enough time for the 3.8 release so this feature becomes stable.

So far the on-top side menu is a huge improvement especially in UI’s that contain native peers such as Maps where this feature is invaluable. The regular side menu causes a noticeable flicker which doesn’t happen with the on-top variety.

]]>
1,170 Media Controls and Print Developer Guide 0 0 0
TIP: Using Lombok and Other tools blog/tip-using-lombok-other-tools.html Tue, 10 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-using-lombok-other-tools.html

A few weeks back maaartinus asked on stack overflow whether we can get lombok working with Codename One. After a short back and forth it seems that this was as easy as I’d hoped and he was able to get it working without a change.

Personally, I like our approach of property objects more but that’s obviously a matter of taste. Lombok works by processing the bytecode after compilation to convert annotations to terse code. E.g. this Java code using Lombok:

@Getter @Setter @NonNull
private List<Person> members;

Would be similar to this regular Java code:

private List<Person> members;

public Family(final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}

public List<Person> getMembers() {
    return members;
}

public void setMembers(final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}
I took the code sample above from here

That’s pretty nice and it has some other features that hide some of the verbosity of Java.

As mentioned in the answer adding Lombok to a Codename One application is pretty easy:

  • Just add the lombok.jar to the build path which you can find in the project settings for the native IDE

  • In the build.xml replace compiler="modern" with compiler="extJavac"

  • Add the path to lombok.jar to the classpath in the build.xml (this might not be necessary in NetBeans)

Doing this for Other Things

Lombok is just one of a huge set of bytecode manipulation tools that work statically. I’m sure there are many other tools like that in the wild that would "just work" and we never got around to test them.

If you have a pet tool in your toolbox check it out and see if it works with Codename One. You might be surprised.

]]>
1,171 TIP: Using Lombok and Other tools 0 0 0
Tutorial - Desktop and JavaScript Ports blog/tutorial-desktop-javascript-ports.html Thu, 5 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tutorial-desktop-javascript-ports.html

Codename One has ports for Mac, Windows & even for the browser. These are often confused and the introduction of the UWP port which also includes some overlap only made matters worse. Each one of these ports covers a segment that the other ports don’t.

In this tutorial I try to separate the various ports & explain when each one of them should be used. I also dispel common misconceptions about these ports.

]]>
1,172 Tutorial - Desktop and JavaScript Ports 0 0 0
Security, Map Tutorial and ComScore blog/security-map-tutorial-comscore.html Wed, 4 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/security-map-tutorial-comscore.html

I’ve added two modules recently to the deep dive course in the academy. These are a part of a trends together with the SMS activation posts I’ve done over the past couple of weeks. These are all preparations for the upcoming Uber demo that will launch before the end of the year.

So the first module I added is a security module which I think I mentioned before, the one I added just last week explains the process of working with native maps and the various types of maps we have in Codename One. It goes all the way from basic to drawing stuff on top of the map and building for the various devices (including plot twists like build failures…​).

As you can see this is all part of a bigger plot so if you didn’t signup for the academy now is the time to ask your boss…​

Comscore

Diamond built and submitted a new comscore analytics cn1lib to replace the older version built by Fabricio.

I didn’t use Comscore much but if you need better insight into your users than the one provided by google analytics this is a good place to start. It’s also a very interesting library since the integration wasn’t trivial and you can learn some nuances from the build hints used.

]]>
1,173 Security, Map Tutorial and ComScore 0 0 0
TIP: Activation UI and the Builder Pattern blog/tip-activation-ui-builder-pattern.html Tue, 3 Oct 2017 00:00:00 +0300 shai https://www.codenameone.com/blog/tip-activation-ui-builder-pattern.html

I wrote two posts about the SMS activation process. In the first I discussed using the Twilio API via REST and in the second I discussed the native interfaces for SMS interception we can use in Android. Now it’s time to put this all together and create a single API that’s fluid. It should include the full UI process but be flexible enough to let you design your own experience.

The Builder

I wanted to create a UI experience that’s pretty typical and standard while hiding a lot of nuance. My first intuition was to just derive Form and build the UI. But that’s not a good approach.

When you derive a component you expose the entire API of the base class onward, that creates a confusing UI API so I decided to expose a builder API which looks something like this:

TwilioSMS smsAPI = TwilioSMS.create(accountSID, authToken, fromPhone);
ActivationForm.create("Signup").
        show(s -> Log.p(s), smsAPI);

There are several things going on here. I wrapped the code from the SMS rest calls in an API called TwilioSMS. This allows me to hide the details of sending an SMS from the ActivationForm UI class. You will notice I create an instance of this class using the create method which accepts the title of the form. I then have a show method which calls us back with the phone number when activation succeeds…​

Since this is a builder pattern you can customize all sorts of things:

ActivationForm.create("Signup").
        codeDigits(5). // number of digits in the activation code sent via SMS
        enterNumberLabel(string). // text of the label above the number input
        includeFab(true). // true if a fab should be shown, by default a fab button will appear in Android only
        includeTitleBarNext(true). // true if a next arrow should appear in the title, by default this would appear in non-Android platforms
        show(s -> Log.p(s), smsAPI);

The neat thing is that you can’t do all sorts of things you aren’t supposed to do as the ActivationForm class derives from Object and has a private constructor.

The UI for the activation form shows a form on top of the current form with a number entry UI.