Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Build Air Quality Index widgets for both Android and Windows using ThinkAlike (Part I)

5.00/5 (3 votes)
15 May 2015LGPL314 min read 19K   284  
New samples of using ThinkAlike to accomplish Android/Desktop cross-platform development, including helloworld, Web API access and widget UI.

Image 1 Image 2

Table of Contents

Introduction

My first article introducing ThinkAlike framework was post to CodeProject on March, 2014. In short, it illustrated a story of swiftly developing a HearthStone game card reference in JavaFX firstly, according to the principles of MVVM, and then efficiently extending it to its Android equivalent with only native View layer replaced.

One year passed by, many things evolved (see Background section), and joyfully I still found the philosophy of ThinkAlike useful and would like to share a new user story about the framework.

The new story originates from the "Airpocalypse" which is puzzling Chinese, and contains three parts:

  • ThinkAlike which had been forged to an independent library and become more handy (see Framework section)
  • asynchronous mechanism poached from Android's source to be launched from generic module (see Service section)
  • and a pair of widgets which could respectively report Air conditions from outdoors and in-the-office (see the screenshots above and UI section).

Background

During the year after my previous post, quite a few events occurred.

  • Community: CodeProject held an Android Tutorial Contest

    Although encouragingly attracted 20,000+ reads, my first ThinkAlike article has not received enough public comments yet. This was probably due to the difficulties of having to learn both JavaFX and Android at the same time, espeically for Android newcomers. Fortunately, CodeProject held a contest of Android tutorial writing last year. A great number of new articles and authors have emerged and I hope my MVVM cross-platform solution will appeal to more potential participants.

  • JavaFX: community introduced JavaFXPorts

    JavaFXPorts might have kick off very close to when ThinkAlike did (Dec,2013). It was motivated by the possibility of translating Java byte code into mobile OSs' and propelled by support from the JavaFX team, whereas ThinkAlike was based on experience in Android, MVVM and SWT (In fact I thought of taking SWT as Desktop UI, which is employed by Eclipse). Being a part-time project, JavaFXPorts also suffers lack of resource but Johan Vos (author) works really hard on it. I believe if succeeded it will bring great convenience to JavaFX developers.

  • Android: ART replaced Dalvik

    Google introduced a new compiler called ART (Andriod Runtime) and explained its difference with Dalvik. As an application layer solution, however, ThinkAlike needn't worry about such underlying modifications.

  • ThinkAlike: Library + HearthStone Card Reference filtering function accomplished

    First of all cross-platform related parts were split from the original all-in-one HearthStone card reference source. A HSCardRef project separated out and linked with the thereafter isolated ThinkAlike library project. And by now, functionally, HSCardRef has been upgraded to Func#3 which can filter cards by Mana/Atk/Hp as well as Ability traits! (And have to mention it's mainly fulfilled by my colleagues who have studied my tutorial articles!)

Day 1 -- Framework: Hello, ThinkAlike Library!

Challenges prior to an "Airpocalypse" expedition is to build up a start-up project for both JavaFX and Android platforms. Here we go!

For 1st Day tutorial, there will be no need to read back through my previous "HearthStone card reference" article.

Image 3 Image 4

Challenge 1. Environment Setup (2-4hours)

My development tools inventory remains the same:

The sample projects I provided are configured in e(fx)clipse. You may use other IDEs like Android Studio, NetBeans, but then you will be required to setup relationships among projects by yourself (mentioned below).

In addition, if you were a novice at either Android or JavaFX programming, or especially at using the Eclipse IDE, I recommend you run through at least one example project in your new environment. (Estimated 2-4 hours. Otherwise if any compiler/setting problems defeated you, remember to come back for a self-aid revival :))

Challenge 2. Project Settings (15-min for Eclipse users)

Settled down in the new environment, apprentices may unzip the package named HelloThinkAlike and find 6 folders deployed like below.

Image 5

In the middle of the bottom generic module of ThinkAlike project can be located, flanked by Android module and JavaFX module. Accompanied by any one of the platform-specific modules, the platform-independent module can "awaken"(build) a relevant version of ThinkAlike library. Eclipse apprentices can implement this by importing the modules as "Existing Projects", with linkage well pre-configured.

In case you are not using Eclipse, please try to configure the linkage with the following hints in mind:
  1. ThinkAlike Android is an Android library project which references(links) to the source of ThinkAlike generic.
  2. ThinkAlike JavaFX is a JavaFX library project which also references(links) to the source of ThinkAlike generic.
  3. Although for Eclipse IDE, different kinds of references (source-level vs project-level) are used (mainly for a constraint of e(fx)clipse plugin), it ain't necessarily so.

Then move on to the 3 modules above. Application there should be replaced by the true name of your host application which employs the ThinkAlike library/architecture for cross-platform extensibility. For our tutorial package, it is named as simple as HelloThinkAlike.

For non-Eclipse apprentices:
  1. Application Android is an Android application project, with references both to Application generic for sharable sources and asset files, and to ThinkAlike Android as a library project(*1).
    *1 Current version of Android SDK doesn't allow distributing a library as a JAR file.
  2. Application JavaFX is a JavaFX application project, which references to the Application generic as well, and employs the correspoding ThinkAlike JavaFX in form of a JAR library (namely ThinkAlike_jfx.jar, in its libs subfolder).

Challenge 3. Building, Running, and Distributing (1-2hours for Eclipse users)

Through the perspective of the Eclipse, 6 modules would be reflected flawlessly like the following (excepts several suppressed warnings).

Image 6

Our next objective is to activate the HelloThinkAlike_jfx application on our laptops and the HelloThinkAlike_android on our fingertips.

If any of the 6 reflections were flawed, that may be ascribed to Java Build Path or Android Project Build Target setting problems. Eclipse apprentices can amend them by openning the Project Properties of the untuned projects. After tunning, for double-check, you may try to build the projects in the following order (optional): ThinkAlike_generic, ThinkAlike_jfx, ThinkAlike_android, HelloThinkAlike_generic, HelloThinkAlike_jfx, and HelloThinkAlike_android.

In fact, most of e(fx)clipse apprentices could have directly launched HelloThinkAlike_jfx project from their IDE. ([Context Menu] Run As > Java Application, be aware when being asked to select an Application class, choose "HelloThinkAlikeApp")

Image 7

A JavaFX GUI will rise up, with an round-cornered outline and alpha transparent background. It's merely a tutorial example, so simply "Click here", and you will unveil the secret.

Similar things would happen to the HelloThinkAlike_android project, except that your Android device should have been connected to and identified by your development OS and IDE. (USB driver problems could baffle you, as they had done to me. Using virtual device? overwhelmingly cumbersome..) For Eclipse apprentices, [Context Menu] Run As > Android Application, after transferring a zipped APK to your mobile and deploying on it, a similar GUI will appear.

Once being launched, rather than being merely compiled, Android project will encapsulate an .apk file in its bin subfolder, which can be submit to most Android App Stores.

By contrast, JavaFX project has extra learning curve on distributing stand-alone packages.

  • For details (and for e(fx)clipse apprentices only), there is a must-read named JavaFX 2 Tutorial Part VII - Deployment With E(fx)clipse for Windows as well as MacOS.
  • For skimmers, you may go directly to HelloThinkAlike_jfx\build\build.xml for a glimpse of the Ant script, which helps you directly jumping to Step 4 of the above tutorial. Then, if you're Windows user, download an .exe installer utility called Inno Setup, and remember to add its installation path to your Path environment variable. (For MacOS, no installer utility is required) Finally, run the build.xml by [Context Menu] Run As > Ant Build, the building will take a while. If succeeded, there will be an .exe installer in the folder \build\deploy\bundles.

Challenge 4. Customizing (1-10hours)

The warm-up, or start-up project, ends up here? Not until the true force be with you, force of the Code.

Overlook the code

HelloThinkAlike projects can be used as template for new development. The table below illustrates to which extent each source file having been customized for the HelloThinkAlike demo. Essential sources/assets are all marked in bold and will be explained later.

Source Codeline Level of Customization
--- HelloThinkAlike_generic ---
Config.java 18 low. log level, assets file root
Constant.java 13 low. app title, assets file path, propertyNames
HelloViewModel.java 30 high. ViewModel, main logic
logo_*.png 0 high. UI, assets
--- HelloThinkAlike_jfx ---
Assets.java 3 n/a. helper class
Res.java 23 n/a. helper class
Config.java 3 n/a
Constant.java 15 n/a
Factory.java 21 n/a. create platform-specific adaptor classes
HelloThinkAlikeapp.java 147 medium. entrance, initialization
MainController.java 80 high. 1.UI, View layer 2.employs ViewModel
scene_main.fxml 25 high. UI, layout markup
style_base.css 8 high. UI
bundle.properties 1 high. UI, I18N, text
ApplicationPreloader.java 7 n/a. optional
--- HelloThinkAlike_android ---
Config.java 3 n/a
Constant.java 3 n/a
Factory.java 27 n/a. create platform-specific adaptor classes
HelloThinkAlikeApp.java 105 medium. entrance, initialization
MainActivity.java 91 high. 1.UI, View layer 2.employs ViewModel
activity_main.xml 31 high. UI, layout markup
styles.xml 26 high. UI, I18N, font size
strings.xml 5 high. UI, I18N, text

Change basic settings

The entrance, of each platform-specific project (HelloThinkAlike_jfx or HelloThinkAlike_android), can be located at source file of the class com.{domain_name}.{platform}.{app_name}App. Application level settings can be configured there.

Take HelloThinkAlike_jfx as an example. You may open com.helloworld.jfx.HelloThinkAlikeApp, the Application class, and move to its entrance method start(), to check which settings you need to modify. (Similarly, Android Application class has an entrance method called onCreate())

Java
@Override
public void start(Stage primaryStage) {
	
	Locale.setDefault(new Locale("en", "US"));
	......
	
	//1.Initialize platform-specific, domain-specific objects
	com.helloworld.jfx.common.Constant.initialize();
	Config.STORAGE_BASEPATH = "C:\\Test\\"; //platform-specific
	
	//2.Initialize Log scheme (File IO)
	LogTag.removeExclusionItem(LogTag.AssetThread); //for novices, or new project 
	Util.trace(getClass().getSimpleName(), "......");
	
	......
}

As shown above, locale, storage base (asset files will be extracted to an external folder), log filters (e.g. LogTag.AssetThread) etc., can be customized there. You may also open up com.helloworld.generic.common package of the HelloThinkAlike_generic module, and check domain-specific constants/configs like application titile, log levels, which are statically defined.

The Application class also implemented a Platform interface declared by ThinkAlike library (com.thinkalike.generic.Platform) so as to provide required platform-specific data/method to generic modules. For instance, by calling getOsType(), generic module can tell current platform type so as to determine which OS logo should be displayed.

Change resources, assets

If you want to animate your own favourites onto the screen, e.g. AOE Paladins like I've done before in HTML5, you will find it convenient to manage the images in a folder common to both your Android project and JavaFX project. HelloThinkAlike_generic\assets-{locale} will be such repositories.

In the world of ThinkAlike, there are 2 kinds of methods to render an image: ordinary way by feeding path or drawable info to native classes (e.g.android.widget.ImageView), or alternative approach by employing adaptive classes which wrap the native controls with generic interface and consume generic content data, as will be detailed below.

Change logic

UI logic of an Model-View-ViewModel(MVVM) project resides in ViewModel classes which abstract essence from real View classes. ThinkAlike takes advantage of the MVVM philosophy to make the user story oriented code generic to platform-specific projects and easy to maintain, like this:

Java
//com.helloworld.generic.viewmodel.HelloViewModel

import com.thinkalike.generic.viewmodel.ViewModelBase;
import com.thinkalike.generic.viewmodel.control.UIImageNode;

public class HelloViewModel extends ViewModelBase{

	private static String _helloMessage = "Hello, ThinkAlike!";
	private static UIImageNode _helloImage;

	private void updateHelloMessage(){
		//1.hello message
		this.firePropertyChange(Constant.PropertyName.HelloMessage, null, _helloMessage);

		//2.hello image
		if(_helloImage==null){
			OsType osType = Loader.getInstance().getPlatform().getOsType();
			ImageNode node = new ImageNode((osType==OsType.Android) ? Constant.PATH_LOGO_ANDROID : Constant.PATH_LOGO_JAVAFX);
			_helloImage = new UIImageNode(null, node, false); 
		}
		this.firePropertyChange(Constant.PropertyName.HelloImage, null, _helloImage);
	}
	
	public void onRefresh(){
		updateHelloMessage();
	}
}

Logic of HelloThinkAlike is extremely simple: handling a Click by displaying Message+Logo. Details of accepting user input and rendering contents can be accomplished by platform-specific View layer, whereas ViewModel layer encapsulates UI related data, logic, and communicates with the View through 2 kinds of interface:

  • ICommand

    User commands (not the intermediate ui operations) will be interpreted as ICommand events which will be dispatched from the View and handled by the ViewModel. onRefresh() is a simple example. For time consuming transaction, you may also employ asynchronized mechanism for handling the command.

  • IPropertyChange

    UI update feedback will be interpreted as IProperty(Change) events which will be fired from the ViewModel and consumed by the Views which have registered as listeners. There are 2 kinds of properties to be announced changed: _helloMessage and _helloImage. The former represents primitive data types/objects which can be easily rendered by ordinary native UI controls (e.g. android.widget.TextView), while the latter represents ComponentModel (e.g.UIImageNode) which can be consumed by platform-independent "adaptive" component classes (e.g. com.thinkalike.android.control.ImageNodeView). The ComponentModel, like a component-level ViewModel, can initialize the data required by a specific kind of component across different platform SDKs. You may modify the source to respond with weekday images, View layer will honestly render them.

Remember that the generic ViewModel doesn't know platform-specific View classes. It's View class who is responsible for the instantiation of corresponding ViewModel(s) and subscribes itself to their IPropertyChange events.

Change UI for specific platform

Last month, HearthStone released its Android phone version (lower left). More compact layout, more cascading UI design which will lower user experience but maintain readability, makes it even different with its tablet ancestor (lower right).
Image 8 vs. Image 9

There are several key factors to consider before deciding how many sets of UI need to design, e.g. available screen size or DIP (device independent pixel), available interactive methods/devices (e.g. glasses, watches). Under some circumstances, concerns about performance, compatibilities, support on new features, legacy sources etc., will leads to adopting platform-dependent development. ThinkAlike, with its MVVM design, however, provides cross-platform possibilities to such cases. Developers may employ adaptive UI components to suppress diversity among platforms, at the same time they may also keep enjoying platform-specific benefits as long as modeling ICommand/IPropertyChanged stuffs into generic ViewModel classes.

For Android UI modification, as documented officially, resources under the res subfolder (e.g.activity_main.xml), and Activity classes should be checked out. On the other hand, for JavaFX, there is no strict conventions on the organization of resources, and currently they're managed under the com.{domain_name}.jfx.res package. A controller class named MainController interprets the native view-level logic of the layout scene_main.fxml ("Scene" equals "Activity" in JavaFX).

The following document collection will be helpful to novices while tunning the UI of HelloThinkAlike:

Change project name

This step may be moved to the first place as well.

When you determine to set off for your own project, you may replace {app_name}and {domain_name} throughout the HelloThinkAlike_* projects, which may appear at folder names, package names, or source file contents.

  • In {app_name}_generic project,
    • folders, packages: assets-{locale}\{app_name}, src\com\{domain_name}
    • sources: globally replace string patterns like "com.{domain_name}", "{app_name}" throughout all source files, and .project setting file.
  • In {app_name}_jfx project,
    • folders, packages: src\com\{domain_name}
    • sources: .classpath, .project, all source files, log4j.properties, build.xml.
  • In {app_name}_android project,
    • folders, packages: src\com\{domain_name}
    • sources: .classpath, .project, project.properties, all source files, AndroidManifest.xml.

Summary

Day 1 is actually a simplification of my first article on ThinkAlike. Library has been separated out, leaving out implementation details of the ThinkAlike. And application logic has been simplified, transitions between MVVM layers become clearer. I hope that even novices at either Android or JavaFX programming could pass through all 4 challenges with least energy cost, and get prepared for further expeditions.

As planned, Day 2 will introduce AsyncTask enhancement made to the framework (com.thinkalike.generic.concurrent.*). Day 3 will embrace AppWidget UI, which highly depends on interaction with platform, and will prove the adaptability of ThinkAlike.

Thank you for your concern about Android/Desktop cross-platform discussion. I will try my best to provide reader-friendly introduction, and will definitely appreciate any interaction. :)

TO BE CONTINUED...

History

05-06-2015: 1st edition

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)