As technology advances, enterprises are finding themselves wondering what do we do to catch up? A lot of companies have their systems designed to work on older devices that run operating systems such as Windows CE and many of their apps are in a form of web applications . Most of these corporations are struggling with the decision of how to upgrade to newer platforms fast and without spending tons of money.  The solution could be as simple as “Let’s leverage what we have and temporarily adapt it to what we need until we can fully integrate the new technology.”   Using the following steps could help companies migrate fast and allow them to migrate to newer platforms with only a couple of lines of code.

 

15w809

What are we talking about? 

Let’s say you are in a situation where you are stuck with a fleet of Windows Mobile web apps and your company has decided that is time for a change in technology. The challenge is that the company needs to spend minimal resources, time, and money upgrading to the new platform. The clock is ticking, and your boss needs a solution now. What do you do? Well, that is usually the question most enterprises ask themselves when confronted with this type of decision. Their main concern is how to upgrade all these apps to a better system without spending  time, money and resources. There is an easy and practical way to begin migrating applications to newer platforms. This process can be accomplished with any of the major mobile Operating Systems like iOS, Android and Windows Phone 8.1/10; However, I am going to concentrate on Android because it is most popular enterprise mobile platform.

There are different ways to accomplish a web app migration, but we will focus on a native solution. We will go through four easy steps that we have used before to help clients overcome their app migration difficulties in a fast, simple and very inexpensive process.

What resources do we have?

Most web applications created to work on Windows Mobile devices share common attributes. For example, they are hosted on a particular web server, use plain or scripting languages (HTML,JS,PHP, JSP. SERVLETS,  ASP, etc.) and rely on the mobile browser to handle most system events such as Scanning, Notifications, Wireless printing, etc.  This is something we will leverage when migrating mobile web applications.

Why don’t we use Cordova?

We could use Cordova to wrap the applications and think we are “done”, but even though we have used Cordova to migrate apps in the past, we found that it was a little bit more troublesome than just going native.  In addition, we need to find, create and support the required app plugins, find the perfect configuration that would allow us to manipulate the existing web server HTML code within the Cordova wrapper and have “elegant” code that can be shared and used across the fleet of Windows Mobile web applications.

vibrate

However, we do not completely throw away the idea of using Cordova, We actually believe is a great tool when upgrading and redesigning applications from the current web app state to a more dynamic HTML5 version. Then creating  hybrid apps that leverage what Cordova does best; wrapping HTML5 content that communicates with the hardware.

Building a native wrapper first can greatly help to identify the plugins required, and help with the development of them in case they are not available in the Cordova plug-in library.

Let’s go native. 

Image provided by - http://www.androidpolice.com/

by: http://www.androidpolice.com/

Before we begin, we need to set up the development environment and download all the necessary tools to start building Android applications. Make sure you have Android Studio, Java, Java SDK, Gradle, and Genymotion as a good emulator resource.  For more info go to the Install Android Studio.

The First Step:

The first thing we do is to create a project thats only control would have a WebView Control. The WebView control allows you to view web content, either with files within the device or on a hosted server. In our case, we need to point to the hosted server that has our application.

We then need to configure the webView Settings.

  • Enables JavaScript::
    • [code]]czo1MDpcIl93ZWJWaWV3LmdldFNldHRpbmdzKCkuc2V0SmF2YVNjcmlwdEVuYWJsZWQodHJ1ZSk7XCI7e1smKiZdfQ==[[/code]
  • Set or overwrite the User Agents:  In some cases, the server looks at the user agent header to determine what content to load.
    • [code]]czo1NjpcIl93ZWJWaWV3LmdldFNldHRpbmdzKCkuc2V0VXNlckFnZW50U3RyaW5nKFwiV2luZG93cyBDRVwiKTtcIjt7WyYqJl19[[/code]
  • Disable built in zooming: we can handle zooming.
    • [code]]czo1MzpcIl93ZWJWaWV3LmdldFNldHRpbmdzKCkuc2V0QnVpbHRJblpvb21Db250cm9scyhmYWxzZSk7XCI7e1smKiZdfQ==[[/code]
  • Set the WebView Client call back:  This Callback is a class we need to implement to handle page load cycles (Start, Finish, Error).
    • [code]]czo0MjpcIl93ZWJWaWV3LnNldFdlYlZpZXdDbGllbnQobmV3IENhbGxiYWNrKCkpO1wiO3tbJiomXX0=[[/code]
  • Set the WebChromeClient:  This is useful if the app uses Dialogs, favicons, progress, and titles.
    • [code]]czo1MTpcIl93ZWJWaWV3LnNldFdlYkNocm9tZUNsaWVudChuZXcgV2ViQ2hyb21lQ2xpZW50KCkpO1wiO3tbJiomXX0=[[/code]
  • Enable File access within the WebView
    • [code]]czo1MDpcIl93ZWJWaWV3LmdldFNldHRpbmdzKCkuc2V0SmF2YVNjcmlwdEVuYWJsZWQodHJ1ZSk7XCI7e1smKiZdfQ==[[/code]
    • Adds the javascript interface: assigns a name to this interface to reference the Java class in javascript
    • [code]]czo1NTpcIl93ZWJWaWV3LmFkZEphdmFzY3JpcHRJbnRlcmZhY2UoanNJbnRlcmZhY2UsIFwiQnJpZGdlXCIpO1wiO3tbJiomXX0=[[/code]

So at the end, the code will look something like this.
[code]]czo0NjpcIg0KcHVibGljIGNsYXNzIE1haW5BY3Rpdml0eSBleHRlbmRzIEFjdGl2aXR5IHtcIjt7WyYqJl19[[/code]

WebView _webView;
private ProgressBar _loadingCircle;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_webView = (WebView) findViewById(R.id.web_view);
_webView.loadUrl(“https://localhost:8080/MyServerApplication/mobileIndex.aspx”);
_webView.getSettings().setBuiltInZoomControls(false);
_webView.getSettings().setJavaScriptEnabled(true);
_webView.getSettings().setAllowFileAccess(true);
_webView.getSettings().setUserAgentString(“Symbol”);
_webView.setWebViewClient(new Callback());
_webView.setWebChromeClient(new WebChromeClient());
_webView.addJavascriptInterface(jsInterface, “Bridge”);
}

protected class Callback extends WebViewClient {

@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);

// When the page finish loading, we inject any javascript to the webview or any css.
// and handle any native code like end the loading spinner.
injectJS(view);
injectCSS(view);
_loadingCircle.setVisibility(View.GONE);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//
return false;
}

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {

// Overwrite or handle any native codea when the web page is requested. Useful for loading spinners.
_loadingCircle.setVisibility(View.VISIBLE);
}
}
}

Step #2: 

We create a bridge  that will connect our native code with the existing Javascript. As an example lets say we want to play a sound once a button is pressed. We will have to create our own ActiveX object and pass that call to the native Java bridge and play the sound natively.

        ActiveXObject.js
[code]]czo1NTpcIg0KZnVuY3Rpb24gQWN0aXZlWE9iamVjdCAodHlwZSkgew0KdGhpcy50eXBlID0gdHlwZTsNCn1cIjt7WyYqJl19[[/code]

ActiveXObject.prototype.InvokeMETAFunction = function(module, values) {

switch(module){
case ‘invokenotification’:

Bridge.setSound(“generic”);
Bridge.setVolume();

break;
case ‘generic’:
break;
}
}
….

        Bridge or JSInterface.java

[code]]czoyODpcIg0KcHVibGljIGNsYXNzIEpTSW50ZXJmYWNlIHtcIjt7WyYqJl19[[/code]

private Context _context;

public JSInterface(Context context){
_context = context;
}

@JavascriptInterface
public void setVolume(){

AudioManager audioManager = (AudioManager)_context.getSystemService(Context.AUDIO_SERVICE);
int vol = audioManager.getStreamMaxVolume(audioManager.STREAM_MUSIC);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, vol, 0);

Timber.i(“Volume set to %s”, vol);
}

@JavascriptInterface
public void setSound(String name){

//TODO: map name to sound.

Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
MediaPlayer player = MediaPlayer.create(_context, notification);
player.start();

Timber.i(“Played notification”);
}

@JavascriptInterface
public void pairPrinter(String hexCode,String printerId){
//TODO: pair with printer.
Timber.i(“Pair with printer”);
}
}

Step #3:  

In the Java Activity Class, we now  inject any JavaScript that is required to override your methods. You can make this function very dynamic to include a series of local JavaScript files and looping through them to inject, as shown below.

[code]]czo2NjpcIg0KcHVibGljIFN0cmluZyBnZW5lcmF0ZUphdmFTY3JpcHRUYWdzRm9yU2VydmVyRmlsZXMoTGlzdCBmaWxlcykge1wiO3tbJiomXX0=[[/code]

StringBuilder str = new StringBuilder();
str.append(“(function(){“);
str.append(“var parent = document.getElementsByTagName(‘head’).item(0);”);

for (int i = 0; i & lt; files.size(); i++) {

str.append(“var scanJs” + i + ” = document.createElement(‘script’);”);
str.append(“scanJs” + i + “.type = ‘text/javascript’;”);
str.append(“scanJs” + i + “.src= ‘ ” + files.get(i) + ” ‘;”);
str.append(“parent.appendChild(scanJs” + i + “); “);

}

return str.toString();
}

[code]]czoxMzA6XCINCnByaXZhdGUgdm9pZCBpbmplY3RKUyhXZWJWaWV3IF93ZWJWaWV3KSB7DQpTdHJpbmdbXSBmaWxlcyA9IG5ldyBTdHJ7WyYqJl19aW5nWzJdOw0KZmlsZXNbMF0gPSBcIkFwcC5qc1wiOw0KZmlsZXNbMV0gPSBcIkFjdGl2ZVhPYmplY3QuanNcIjtcIjt7WyYqJl19[[/code]

String js = generateJavaScriptTagsForServerFiles(files);
_webView.evaluateJavascript(js, new ValueCallback() {
@Override
public void onReceiveValue(String value) {
// handles any response with value from the injection
}
});
}

private void injectCSS(WebView _webView) {
String css = “.mycss {width:100%; height:100%; }”;
_webView.evaluateJavascript(css, new ValueCallback() {
@Override
public void onReceiveValue(String value) {
// handles any response with value from the injection
}
});
}

Step #4: 

If you need to update your CSS, you can inject any extra CSS to make the html look pretty and match the new device.
[code]]czo0NDpcIg0KcHJpdmF0ZSB2b2lkIGluamVjdENTUyhXZWJWaWV3IF93ZWJWaWV3KSB7XCI7e1smKiZdfQ==[[/code]

String css = HtmlInjector.getInstance(getApplicationContext()).generateHtmlForCSSInjection(
_config.getCurrentConfiguration().getLocalCSStoInclude(),
_config.getCurrentConfiguration().getServerCSStoInclude());

_webView.evaluateJavascript(css, new ValueCallback() {
@Override
public void onReceiveValue(String value) {
Timber.i(“%s: css injected”, TAG);
}
});
}

Conclusion:

As shown above, we see that with a couple of lines of code, and a couple of steps, you can produce a  quick start on migrating a fleet of mobile web apps to better and newer platforms such as Android, iOS and Windows. Even though we did not talk about the last two mobile operating systems, these four steps can guide with the general ideas that to migrate on those platforms The only difference is using the required APIs and language.

If you have any questions about migrating your old organization applications to a newer platform I will be happy to help. Please reach out!

 

Related Posts

Andres Avendano

Andres Avendano

Lead Developer - Andres Avendano is a developer specializing in web & mobile development with a passion for aeronautics. Andres has skills in C#, Java, Ext JS, HTML5, iOS and Android. He also has his private pilot certification and working on an instrument rating certification. Andres holds a BS in Computer Science from Georgia State University.