APEX-applicatie als hybride smartphone app
20 dec 2013 - Christian Rokitta

Sinds versie 4.2 en de integratie van het jQuery Mobile framework in Oracle Application Express is het mogelijk declaratief applicaties te ontwikkelen die geschikt zijn voor het gebruik op mobiele toestellen als smartphones. Ondanks de verbeteringen die het jQuery Mobile framework in look-and-feel voor gebruiker en ontwikkelaar biedt, blijft het resultaat een webapplicatie die in een browser aangeroepen moet worden. Ook is de toegang tot de extra functies die mobiele apparaten bieden, zoals came

Voor wie in zijn APEX-applicatie toch gebruik wil of moet maken van de geavanceerdere toestel functies bieden frameworks voor het ontwikkelen van hybride apps een uitkomst. Hybride apps worden ontwikkeld in standard webtechnologie, dus HTML, CSS en Javascript, en door het framework in een app verpakt en voorzien van een API, die toegang geeft tot native features. En omdat een Application Express applicatie uiteindelijk in de browser op dezelfde standaarden gebaseerd is zou het mogelijk moeten zijn om een APEX-applicatie als hybride app aan te bieden.

Het bekendste framework voor het ontwikkelen van hybride apps is PhoneGap. PhoneGap is een gratis framework, oorspronkelijk ontwikkeld door Nitrobi. Het bedrijf werd in 2001 overgenomen door Adobe, dat PhoneGap als open source project onder de naam Apache Cordova beschikbaar stelde. Adobe heeft op basis van PhoneGap de online service Adobe PhoneGap Build ontwikkeld.


Hoe werkt PhoneGap

Een PhoneGap app bestaat uit een Container App, eigenlijk een huls die als belangrijkste component een WebView bevat, een browser zonder toolbar. Deze Container App verschilt natuurlijk voor elk OS, want elk besturingssysteem heeft zijn eigen WebView implementatie. Bovendien bevat de PhoneGap Container App ook een API, die de native features van het toestel beschikbaar stelt.

De eigenlijke applicatie wordt, platform onafhankelijk, in HTML en Javascript ontwikkeld. De API wordt als Javascript bibliotheek beschikbaar gesteld en heeft weliswaar voor elke platform een eigen implementatie, maar de functieaanroepen hebben op alle platformen de zelfde signature (naam en parameters).

 

Afbeelding 1: PhoneGap Architectuur

 

De eigenlijke bedoeling van het ontwikkelen van hybride apps is om alle bestanden (HTML, CSS, Javascript, images …) die bij de applicatie horen in de container op te nemen en met de app mee lokaal te installeren op het uiteindelijke toestel. Ontwikkelen van een applicatie kan het beste in een IDE voor native app-ontwikkeling voor een van de door PhoneGap ondersteunde besturingssystemen. Voor Android kan dit bij voorbeeld in Eclipse in combinatie met het Android SDK, voor iOS Xcode en iOS SDK en voor Windows Visual Studio en het Windows Phone SDK. Om met PhoneGap een app te bouwen is weinig tot geen kennis van de platform-specifieke programmeertaal nodig.

Wie zijn (hybride) app uiteindelijk voor andere OS varianten wil aanbieden, kan de ontwikkelde HTML, CSS, Javascript en bijbehorende resources gezipt aan Adobe PhoneGap Build aanbieden. PhoneGap Build genereert dan de apps voor alle ondersteunde mobiele besturingssystemen. Voordeel van het ontwikkelen in een IDE ten opzichte van Adobe Build zijn de configuratie en debug faciliteiten, die het ontwikkelen een stuk gemakkelijker maken.


APEX en PhoneGap

Natuurlijk is het niet mogelijk een APEX-applicatie in de PhoneGap App Container onder te brengen, want Application Express pagina’s worden immers dynamisch opgebouwd op basis van definities en condities die we in een repository vastleggen. Het is wel mogelijk om de WebView van de PhoneGap app als browser voor onze APEX-applicatie te gebruiken en we hebben hiermee ook de beschikking over de PhoneGap API. Omdat de APEX-applicatie niet lokaal te benaderen is maar op een, voor het mobiele toestel externe server draait, moet er het een en ander geconfigureerd worden.

In het volgende voorbeeld wil ik deze configuratiestappen voor een hybride Android app doorlopen. Ik volg hierbij de 'Getting Started with Android' tutorial op de PhoneGap website en zal dus niet alle stappen in detail herhalen, maar daar waar van belang voor het integreren van een APEX-applicatie van extra commentaar voorzien.


Installatie Eclips en Android SDK

Als eerste moeten we alle benodigde componenten en applicaties installeren. Voor Android dient eerst het Android SDK en Eclipse met het bijbehorende ADT (Android Developer Tools) Plugin gedownload en geïnstalleerd te worden. Ik ga ervan uit, dat de geïnteresseerde lezer dit zonder problemen met de op de PhoneGap en Android Developer website aanwezige instructies kan uitvoeren.

Volgende stap is het downloaden en installeren van PhoneGap. Ik wil natuurlijk de meest recente versie van PhoneGap gebruiken, dus ga ik release 2.7 downloaden. In het vervolg van dit artikel ga ik niet de 'Getting Started with Android' voor deze versie volgen, maar die van een eerdere versie: 2.1. De reden: in de nieuwere versies van PhoneGap (voor Android) wordt beschreven hoe je met behulp van scripts een eerste Android App project in Eclipse kan aanmaken. Hiervoor word een kant-en-klare voorbeeldapplicatie geïnstalleerd, en niet meer zozeer uitgelegd welke onderdelen waar ingebracht en aangepast moeten worden. En dat is nou juist wat we voor het koppelen van APEX met PhoneGap nodig hebben. De 'Getting Started with Android' van versie 2.1 legt stap voor stap uit hoe een Android PhoneGap app in te richten is, welke bestanden in welke map geplaatst worden en welke bestanden voor de configuratie van belang zijn. Die kennis komt goed van pas als we een app uiteindelijk in Adobe PhoneGap Build willen aanbieden, want ook hier worden deels dezelfde (configuratie-)bestanden gevraagd.


Configuratie voor PhoneGap en APEX

Na installatie van Eclipse en de ADT Plugin kunnen we met behulp van de 'New Project' wizard een Android Application Project aanleggen. Resultaat is een standard Android 'app project boom' met alle componenten en default configuratie. Vervolgens worden de PhoneGap specifieke componenten aan het project toegevoegd. Hiervoor worden twee additionele mappen in het project aangemaakt:

  • /libs
  • /assets/www

De PhoneGap Javascript library (cordova-2.7.0.js) kopiëren we in de map /assets/www, de PhoneGap Java library naar /libs. Let op: de Java library moet eventueel nog handmatig aan de Build Path van het project toegevoegd worden. Als laatste kopiëren we de xml map uit de PhoneGap distributie in de map /res van het Eclipse project.

De Android app wordt vanuit een Java class opgestart. Voor een PhoneGap app betekent dit, dat naast het toevoegen van de PhoneGap library de standaardaanroep (setContentView) in de MainActivity class vervangen moet worden door het laden van een lokaal HTML bestand:

 

super.loadUrl('file:///android_asset/www/index.html');

 

Zo te zien wordt dit bestand in de reeds aangemaakte map /assets/www verwacht. Omdat we uiteindelijk een APEX-applicatie willen aanroepen, zouden we hier ook direct de URL van de applicatie kunnen opvoeren. Alleen, deze aanpak zal later niet in Adobe PhoneGap Build werken, want de MainActivity is Android- specifiek en PhoneGap Build verwacht altijd een index.html als initiële URL. Vervolgens moet nog geconfigureerd worden voor welke API-functies de te ontwikkelen app toegang mag hebben. In het 'Getting Started' voorbeeld worden alle toestemmingen aan het configuratiebestand (/AndroidManifest.xml) toegevoegd. Voor een daadwerkelijke app zal de lijst natuurlijk alleen die functies bevatten die daadwerkelijk door de applicatie gebruikt worden.

Nu leggen we in de map /assets/www het eerder genoemde index.html aan. Hier zullen we dan ook afwijken van de PhoneGap documentatie en i.p.v. het 'Hello World' voorbeeld een redirect naar een apex applicatie inrichten:

 
<!DOCTYPE html>
<html>
<head>
<title>Redirect</title>
</head>
<body onload="window.location.href='http://apex.oracle.com/pls/apex/f?p=phonegapdemo';">
</body>
</html>
Listing 1: index.html met redirect naar APEX-applicatie

Domain Whitelisting

Omdat Android/PhoneGap eigenlijk verwacht lokale resources (file:///android_asset/www/...) te benaderen is de security instelling van de app default beperkt. Links naar externe domeinen zijn dus initieel niet toegestaan. We moeten de server waarop onze APEX-applicatie draait daarom expliciet vrijgeven. Dit kan met behulp van een 'Domain Whitelist' declaratie in het bestand /res/xml/config.xml (en niet cordova.xml, zoals in de 2.1 handleiding van PhoneGap wordt vermeld). Een whitelist regel gebruikt een <access> element met het origin attribuut:

 
	<access origin='...' />

De waarde voor het origin attribuut kan placeholders bevatten en zo relatief flexibel toegepast worden:

Toegang tot oracle.com:

	<access origin='http://oracle.com.com' />

Toegang tot secure oracle.com:

	<access origin='https://oracle.com' />

Toegang tot de subdomein apex.oracle.com:

	<access origin='http://apex.oracle.com' />

Toegang tot alle subdomeinen van oracle.com:

	<access origin='http://*.oracle.com' />

Toegang tot alle domeinen (b.v. google.com of themes4apex.com):

	<access origin='*' />

De syntax en ondersteuning van whitelist definities kan per besturingssysteem verschillen. Zo is het op Android mogelijk om toegang tot subdomains m.b.v. een extra attribuut definiëren:

	<access origin='http://oracle.com' subdomains='true' />

Er kunnen meerdere <access> declaraties in het bestand config.xml opgenomen worden.

Mijn voorbeeldapplicatie staat, zoals aan de URL te herkennen op apex.oracle.com, dus voeg ik de volgende regel aan het bestand config.xml toe:

	<access origin='http://apex.oracle.com*' />

App deployen

Met de wijzigingen die we hebben aangebracht, kunnen we de Android app nu naar de, met het SDK mee geïnstalleerde simulator of direct naar een Android smartphone deployen. Mijn ervaring is, dat de simulator nogal traag is en niet alle functies ondersteund. Bovendien, ook als ik op mijn Android smartphone test wordt alle activiteit en logging in de Eclipse console weer gegeven, wat bijzonder handig is tijdens het ontwikkelen van de app. Om de app op Android te kunnen installeren, moet wel de Debugging optie in de instellingen van het toestel aangezet worden.

Als de app gestart wordt zal onze APEX-applicatie gestart worden, alsof deze in de browser geopend wordt. Maar, omdat deze nu in de PhoneGap WebView draait, kunnen we gebruik maken van de PhoneGap API.


PhoneGap API in APEX

Als we een APEX-applicatie alleen voor een specifiek smartphone OS, hier Android, zouden ontwerpen, dan was het voldoende om de bijbehorende PhoneGap Javascript library in ons APEX page template op te nemen. Zoals eerder vermeld: de PhoneGap Javascript API heeft voor iedere platform een aparte implementatie, omdat deze met de native API van de PhoneGap container communiceert. Maar uiteindelijk willen we de mobile applicatie natuurlijk op alle, of in ieder geval de meest gangbare smartphones kunnen draaien. Om ervoor te zorgen, dat de passende PhoneGap library geladen wordt, op het moment dat de APEX-applicatie geladen wordt, plaats ik de volgende Javascript code in de header van het APEX page template:


<script>
(function loadCordova() {
//Initialize our user agent string to lower case.
var uagent = navigator.userAgent.toLowerCase();
if (uagent.search('android') > -1) {
document.write('<script type="text/javascript" '+
'src="#WORKSPACE_IMAGES#cordova.android.js"></'+
'script>');
} else if (uagent.search('iphone') > -1) {
document.write('<script type="text/javascript" '+
'src="#WORKSPACE_IMAGES#cordova.ios.js"></'+
'script>');
}
})();
</script>
Listing 2: platformafhankelijk laden van PhoneGap Javascript API

Het script bepaalt het platform aan de hand van de HTTP_USER_AGENT environment variabele en voegt de referentie van de bijbehorende library toe aan de header. Deze code wordt tijdens het laden van het HTML document uitgevoerd en de library dus geladen alsof rechtstreeks opgenomen in de template. In dit voorbeeld heb ik alleen de Android en iOS versie van de Javascript libraries uit de PhoneGap distributie gekopieerd, de namen van de bestanden aangepast en als Static Files in mijn APEX-applicatie opgenomen. Deze opzet kan natuurlijk naar wens voor andere platformen uitgebreid worden.

De mobile APEX-applicatie maakt gebruik van jQuery Mobile. Dan is het van belang dat mijn PhoneGap library eerder geladen wordt dan die van jQuery Mobile, om te voorkomen dat de initialisatie van de twee frameworks elkaar in de weg gaan zitten. Ik plaats het stukje code daarom voor de #APEX_JAVASCRIPT# substitution string in de template code.



Afbeelding 2: PhoneGap API Feature overzicht

Aanroepen van PhoneGap API functies vanuit een APEX-applicatie gebeurt, natuurlijk, met Javascript. De Javascript code kan op verschillende manieren in een APEX-pagina geïmplementeerd worden. In het volgende simpele voorbeeld gebruik ik een link om een PhoneGap API aanroep te starten. Hiervoor ga ik eerst een stukje code in de Javascript > Function and Global Variable Declaration van mijn APEX pagina opnemen:


	<script type='text/javascript' charset='utf-8'>
// Wait for Cordova to load
//
document.addEventListener('deviceready', onDeviceReady, onDeviceFailed);
// Cordova is ready
//
function onDeviceReady() {
// Empty
}
// Cordova failed to initialize
//
function onDeviceFailed() {
console.log('Device not inititialized: message');
}
// Vibrate for 2 seconds
//
function vibrate() {
navigator.notification.vibrate(2000);
}
</script>
Listing 3: initialiseren en aanroepen PhoneGap API

Het wordt aanbevolen altijd te valideren dat het PhoneGap framework geïnitialiseerd is. Hiervoor wordt een listener/trigger op het deviceready event gedeclareerd, die een functie aanroept (onDeviceReady) als dit het geval is, waarin de voor sommige API aanroepen nodige initialisaties uitgevoerd worden. Zo niet wordt een exception functie (onDeviceFail) aangeroepen. De vibrate functie die ik hier wil gebruiken heeft geen initialisatie nodig.

De functie vibrate() roept alleen de PhoneGap API functie voor het laten trillen van de telefoon aan.

Deze functie kan ik nu bij voorbeeld in een Dynamic Action of rechtstreeks als HTML link in mijn pagina opnemen. Hieronder heb ik de link variant in een standaard APEX 4.2 mobile pagina (Theme 50) op een HTML region geplaatst:



Afbeelding 3: APEX Region definitie met API functieaanroep in link

Hier het resultaat, zoals het op een smartphone in de PhoneGap WebView getoond wordt:



Afbeelding 4: APEX jQuery Mobile pagina voorbeeld

Voilà, de APEX PhoneGap app is klaar. Open de app die je op je smartphone gedeployed hebt en met een tap op de link wordt de trilfunctie geactiveerd. Toegegeven, het is een erg simpele toepassing, maar het principe is met dit voorbeeld hopelijk duidelijk geworden.


Adobe PhoneGap Build

Tot nu toe hebben we een app specifiek voor Android gebouwd. Met Adobe PhoneGap Build is het, door toepassen van de zelfde technieken, mogelijk apps voor alle door PhoneGap ondersteunde platformen te laten genereren.

Eerst moeten we een account voor PhoneGap Build aanmaken. Adobe biedt een gratis registratie voor de service aan, maar het account is wel beperkt tot één project. Er zijn ook andere abonnement-opties mogelijk, gratis of tegen een redelijke bijdrage, die meer apps/projecten toestaan.

Verder hebben we alleen het bestand index.html en het config.xml uit ons Eclipse project nodig. Deze worden samen in een ZIP bestand op de Adobe PhoneGap Build geüpload en vervolgens worden hiervan de apps voor alle platformen gegenereerd. Omdat er kleine verschillen in syntax en opbouw tussen het Android en PhoneGap Build config.xml bestand kunnen bestaan, is het raadzaam de korte handleiding op de site even door te nemen.



Afbeelding 5: Adobe PhoneGap Build web interface

Samengevat

Voor wie zijn mobile APEX-applicatie tot smartphone app wil promoveren biedt PhoneGap een geschikt raamwerk. De initiële inrichting en configuratie van de omgeving en de componenten kost even enige moeite, maar is grotendeels een eenmalige inspanning. Met PhoneGap is het mogelijk een Application Express applicatie met 'native' functionaliteit uit te breiden. Maar de met APEX en PhoneGap gebouwde app blijft natuurlijk een webapplicatie, dus blijft een internetverbinding nodig om de app aan te roepen.

Bij de in dit artikel beschreven opzet wordt de PhoneGap library in de APEX-pagina opgenomen en moet dus ook vanuit de webserver geladen worden. Er bestaat ook de mogelijkheid de PhoneGap resources in de PhoneGap container te gebruiken en vanuit een APEX-applicatie aan te roepen, zonder deze in de APEX-template op te nemen. In het reeds verschenen boek 'Oracle Application Express for Mobile Web Applications' wordt dit alternatief ook beschreven.


Referenties