Over a million developers have joined DZone.

Port Native Android App to iOS: Part II

Here in Part II, we will build the skeleton of the app.

· Mobile Zone

In Part I of this series we discussed the port of Swiftnotes to iOS by porting it to Codename One and covered what Codename One is and how it differs from Android.

As a reminder, the finished app is on Apple iTunes, Google Play, and the Microsoft store as well as online with a JavaScript build which you can see here.

The full source code of the app is here: https://github.com/codenameone/SwiftnotesCN1

Porting Tool

A while back we announced an open source project to ease the porting process cn1 android importer. This is a very basic tool that just scaffolds the new Codename One project from an Android project without really duplicating the UI or converting the code.

We can obviously improve this tool significantly but to do so we need to gauge community interest which so far has been "underwhelming". I will start by using this tool as it is now to get started quickly and then port the code.

The Porting Process

First, we need to get the pre-requisites:

Step 1: Create a New Codename One Project

The instructions are for NetBeans but should work for IntelliJ as is. I’m not sure about Eclipse.

We create a standard Codename One project:

In the new project wizard we select Codename One Figure 1. In the new project wizard, we select Codename One

The project name doesn’t matter much I just used SwiftnotesCN1

The project name and location Figure 2. The project name and location

We then pick the class/package name, the package name should match the existing Android package name if you want to replace the original project. I also picked a native theme for simplicity and the barebone application to start from scratch:

We should use the same package name as the android project Figure 3. We should use the same package name as the android project

After doing this I chose to refactor the app to the com.codename1 package space so we can run it side by side with the native app

Step 2: Run the Conversion Tool

The conversion tool is very simplistic and preliminary. It had issues with some of the UI elements even with it’s limited support for these features.

If you show enough interest, file issues we’ll fix them and move this tool forward

I used the following command to convert the project:

java -jar AndroidImporter.jar import-project -i Swiftnotes-master/app/src/main/res -o ~/dev/SwiftnotesCN1 -p com.moonpi.swiftnotes

The first argument is the resource directory for the original Android project. Followed by the output directory (the new Codename One project) and the package name where GUI files should be created.

This placed the localization bundles and imported the images, it also generated the GUI XML files.

To generate the GUI sources right-click the project and select "build" this will generate GUI source files for all the XML files.

Step 3: Bind Localization Code

This is really trivial and will allow us to see something running almost at once, open the main class in our case SwiftnotesCN1. Edit the init(Object) method to load the localization Strings:

public void init(Object context) {
    theme = UIManager.initFirstTheme("/theme");

    Map<String, String> v = theme.getL10N("strings", L10NManager.getInstance().getLanguage());
    if(v == null) {
        v = theme.getL10N("strings", "en");
    }
    UIManager.getInstance().setBundle(v);

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

    // Pro only feature, uncomment if you have a pro subscription
    Log.bindCrashProtection(true);
}

To see/edit the Strings in the app double click the

theme.res

file in the root of the project and select the localization section.

Interlude: The GUI Builder

The wizard generates GUI builder XML files that are "hidden" under the res/guibuilder directory and must correspond to Java source files. They carry the .gui extension and use a relatively simple format of hierarchy/layout.

You can edit the XML files but if you remove the java files they will be regenerated. You need to remove/move the XML & Java files together if you want to work with both.

Currently, the generator doesn’t generate much as it can’t replicate the layout and Android is too different from Codename One, but it’s a starting point.

Step 4: Fix the Main Activity Form

The generated code derives from Container instead of Form since Android doesn’t have an equivalent of Codename One’s concept of a top level component. We can just edit the ActivityMain class to derive from Form instead of Container (you can also do that in the .gui XML file but it’s not essential).

We can now right-click the MainActivity.java file and select the GUI builder option which should open this UI:

Main Activity UI as it is generated Figure 4. Main Activity UI as it is generated

This looks a bit weird but if we look at the Android XML this starts to make sense:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    tools:context=".MainActivity"
    android:background="@color/background_white" >

    <include
        android:id="@+id/toolbarMain"
        layout="@layout/toolbar"/>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listView"
        android:divider="@null"
        android:dividerHeight="8dp"
        android:drawSelectorOnTop="true"
        android:fastScrollEnabled="true"
        android:scrollbarStyle="outsideOverlay"
        android:paddingRight="16dp"
        android:paddingLeft="16dp"
        android:clipToPadding="false"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        android:layout_below="@+id/toolbarMain"
        android:paddingTop="8dp"
        android:paddingBottom="8dp" />

    <ImageButton
        android:layout_width="65dp"
        android:layout_height="65dp"
        android:id="@+id/newNote"
        android:scaleType="fitXY"
        android:background="@drawable/ic_new_selector"
        android:layout_marginBottom="30dp"
        android:layout_marginRight="30dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:contentDescription="@string/new_note_content_description" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/noNotes"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="@string/no_notes_text"
        android:textColor="@color/theme_primary"
        android:textStyle="bold"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true"
        android:gravity="center"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_centerInParent="true"
        android:visibility="invisible" />

    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/shadow_elevation"
        android:layout_below="@+id/toolbarMain"
        android:layout_alignParentRight="true"
        android:layout_alignParentLeft="true"
        android:background="@drawable/drop_shadow" />

</RelativeLayout>

Above we have these elements:

  • Toolbar - this is builtin to Codename One with the Toolbar class
  • ListView - Codename One doesn’t recommend lists and instead uses box layout so we will use a Container
  • ImageButton & Shadow - this is used to provide the floating action button, this is builtin to Codename One
  • TextView - used to show content when the view is empty. We will have a special case for it in the Container

Notice that there is a toolbar.xml file included but it doesn’t include anything important just theme stuff and nothing of value to us.

All of these elements except for the text view are useless to us:

  • The Toolbar is built-in to Codename One
  • We should avoid lists and instead use BoxLayoutContainer
  • We have a built-in FloatingActionButton so we don’t need that.

So we will delete everything except for the text. We will also verify that the layout is BoxLayout.Y_AXIS (it should already be with that layout).

One important thing we need to do is select the root form and make sure its UIID property is Form otherwise the converter will try to assign the default UIID’s from Android which won’t work well.

After removing all the redundant stuff... Figure 5. After removing all the redundant stuff…

We save and open the Java source file, then edit the constructor to include the FloatingActionButton as well as the proper title. So we change this:


public ActivityMain(com.codename1.ui.util.Resources resourceObjectInstance) {
    initGuiBuilderComponents(resourceObjectInstance);
}

To this:

public ActivityMain(com.codename1.ui.util.Resources resourceObjectInstance) {
    super("app_name");

    initGuiBuilderComponents(resourceObjectInstance);

    FloatingActionButton fab = FloatingActionButton.createFAB(FontImage.MATERIAL_ADD);
    fab.bindFabToContainer(getContentPane());

    getToolbar().addSearchCommand(e -> Log.p("Todo"));
    getToolbar().addCommandToOverflowMenu("Backup Notes", null, e -> Log.p("Todo"));
    getToolbar().addCommandToOverflowMenu("Restore Notes", null, e -> Log.p("Todo"));
    getToolbar().addCommandToOverflowMenu("Rate App", null, e -> Log.p("Todo"));
}

It should be mostly self-explanatory and should construct the main UI.

Step 5: Run

To see what we have we can just edit the main class ( SwiftnotesCN1 ) and replace this code:

Form hi = new Form("Hi World");
hi.addComponent(new Label("Hi World"));
hi.show();

With this:

new ActivityMain(theme).show();

The end result still needs styling which we will do in the next step.

Before styling the result Figure 6. Before styling the result

Next Time

In the final part of this series, we will finish the app and make it look very close to its native counterpart. 

Topics:
mobile ,android ,iphone ,ios ,porting ,codenameone ,java ,opensource

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}