Apache Cordova

De Banane Atomic
Aller à la navigationAller à la recherche

Définition

Apache Cordova permet la création d'applications pour téléphones en utilisant les technologies web HTML5, Typescript, Angular et CSS3 pour les plateformes Android et iOS.

Environnements
MacOS
  • compilation pour Android
  • compilation pour iOS
  • compilation pour Windows avec virtualisation
Windows
  • compilation pour Android, si Hyper-V ou Virtual Extension n'est pas lancé
    car ils n'autorisent pas d'autres plateforme de virtualisation comme l'émulateur Android
  • pas de compilation pour iOS sans Mac
  • compilation pour Windows
Linux
  • compilation pour Android
  • pas de compilation pour iOS sans Mac
  • compilation pour Windows avec virtualisation

Installation

  • Node.js
Bash.svg
npm -g install cordova
# Tools for Apache COrdova
npm -g install taco-cli
npm -g install ionic
  • Android Studio
    • Start a new project
    • AVD Manager (Android Virtual Device)
    • Create a Virtual Device
  • XCode
  • Visual Studio Code
    • Extension Cordova Tools

Créer une application

Bash.svg
# créer un dossier myApp avec la structure de fichier pour une application Cordova
cordova create myApp
cd myApp

# lister les palteformes installées et disponibles
cordova platforms ls

# ajouter des plateformes
cordova platform add browser
cordova platform add android

# tester si l'environnement est prêt
cordova requirements

# build pour android
cordova build android --release

# test dans un emulateur android
cordova emulate android

# test dans un téléphone physique
cordova run android --device
# lister les targets (devices et emulateurs)
cordova run android --list
# cibler une target (devices ou emulateurs)
cordova run android --target=<target_name>

Signer le paquet *.apk

Powershell.png
# créer la clé keystore
& 'C:\Program Files\Java\jdk1.8.0_162\bin\keytool.exe' -genkey -v -keystore myapp.keystore -alias MyApp -keyalg RSA -keysize 2048 -validity 10000

# signer le paquet apk avec la clé keystore
& 'C:\Program Files\Java\jdk1.8.0_162\bin\jarsigner.exe' -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore .\myapp.keystore .\android-release-unsigned.apk MyApp
Bash.svg
/c/Program\ Files/Java/jdk1.8.0_162/bin/keytool.exe -genkey -v -keystore myapp.keystore -alias MyApp -keyalg RSA -keysize 2048 -validity 10000

cd MobileApp/platforms/android/build/outputs/apk/release
# Signer le fichier apk
/c/Program\ Files/Java/jdk1.8.0_162/bin/jarsigner.exe -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore myapp.keystore android-release-unsigned.apk MyApp

/c/Users/llobera/AppData/Local/Android/Sdk/build-tools/27.0.3/zipalign.exe -v 4 android-release-unsigned.apk myapp.apk

Plugins

Recherche de plugins

Bash.svg
cordova plugin add <plugin-name>
cordova plugin remove <plugin-name>
package.json
"dependencies": {
    "cordova-plugin-device": "^2.0.1"
},
"cordova": {
    "plugins": {
        "cordova-plugin-device": {}
    }
}
cordova-plugin-device information sur le device

Hook

config.xml
<hook type="after_prepare" src="scripts/myScript.js" />
scripts/myScript.js
module.exports = function(ctx) {
    // make sure android platform is part of build
    if (ctx.opts.platforms.indexOf('android') < 0) {
        return;
    }

    // deferral.reject('Operation failed');
    // deferral.resolve();

    return deferral.promise;
};

Requêtes HTTP

Javascript.svg
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.setRequestHeader('Authorization', 'Bearer ' + accessToken);

req.onload = function(e) {
    if (e.target.status >= 200 && e.target.status < 300) {
        console.log("ok");
        return;
    }
    console.log('Data request failed: ' + e.target.response);
};

req.onerror = function(e) {
    console.log('Data request failed: ' + e.target.status);
}

req.onreadystatechange = function (oEvent) {  
    if (req.readyState === 4) {  
        if (req.status === 200) {  
            console.log(req.responseText)
        } else {  
            console.log("Error", req.statusText);  
        }  
    }  
}; 

req.send();

Plugin Cordova Advanced HTTP

Javascript.svg
cordova.plugin.http.get(myUri, null, 
{ Authorization: 'Bearer ' + accessToken }, 
function(response) {
    console.log("Ok: " + response.status);
    data = JSON.parse(response.data);
}, function(response) {
    console.log("Ko: " + response.error);
});
Bash.svg
cordova plugin add cordova-plugin-advanced-http

Angular

Code

Html.svg
<!DOCTYPE html>

<html>
    <head>
        <base href=".">
        <title>Hello World</title>
        <meta charset="UTF-8">
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    </head>

    <body>
        <app>
            Loading ...
        </app>
        <script src="cordova.js"></script>
        <script src="dist/bundle.js"></script>
    </body>
</html>

Installation

Bash.svg
# dependencies
npm install --save @angular/common @angular/compiler @angular/core @angular/forms @angular/forms @angular/http @angular/platform-browser @angular/platform-browser-dynamic @angular/router @angular/upgrade angular-in-memory-web-api core-js reflect-metadata rxjs systemjs zone.js

# dev dependencies
npm install --save-dev @types/core-js @types/node concurrently lite-server typescript systemjs-plugin-text
package.json
{
    "scripts": {
        "start": "tsc && cordova run android",
        "tsc": "tsc"
    },
    "dependencies": {
        "@angular/common": "^5.2.9",
        "@angular/compiler": "^5.2.9",
        "@angular/core": "^5.2.9",
        "@angular/forms": "^5.2.9",
        "@angular/http": "^5.2.9",
        "@angular/platform-browser": "^5.2.9",
        "@angular/platform-browser-dynamic": "^5.2.9",
        "@angular/router": "^5.2.9",
        "@angular/upgrade": "^5.2.9",
        "angular-in-memory-web-api": "^0.6.0",
        "core-js": "^2.5.5",
        "reflect-metadata": "^0.1.12",
        "rxjs": "^5.5.8",
        "systemjs": "^0.21.2",
        "zone.js": "^0.8.26"
    },
    "devDependencies": {
        "@types/core-js": "^0.9.46",
        "@types/node": "^9.6.2",
        "concurrently": "^3.5.1",
        "lite-server": "^2.3.0",
        "systemjs-plugin-text": "0.0.11",
        "typescript": "^2.8.1"
    }
}
tsconfig.json
{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "none",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": true,
        "noImplicitAny": false,
        "allowJs": false,
        "forceConsistentCasingInFileNames": true
    },
    "exclude": [
        "plugins",
        "platforms",
        "node_modules",
        "hooks"
    ]
}

Android Studio

  • SDK build tools %LocalAppData%\Android\Sdk\build-tools\27.0.3
  • SDK platform tools %LocalAppData%\Android\Sdk\platform-tools
  • JDK C:\Program Files\Java\jdk1.8.0_162\bin
  • Android Virtual Device Manager: Bar de menu → droite → AVD Manager

Copier un fichier sur l'émulateur

Glisser-déposer le fichier à copier.

VScode

Bash.svg
# installer les dépendances npm
npm install

# Run app on target platform device or simulator
npm run cordova:run <ios/android>

# Erreur (node:18008) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): CordovaError: No platforms added to this project. Please use `cordova platform add <platform>`.
cordova platform add android

# bash: cordova: command not found
npm install -g cordova
  • Installer Java SE Development Kit, puis ajouter C:\Program Files\Java\jdk1.8.0_162\bin au PATH
  • Installer Android Studio

Debug Android

  1. Chrome → chrome://inspect → inspect link
  2. Sources → file:// → android_asset/www → js → index.js → ajouter un breakpoint

TACO - Tools for Apache COrdova

Debugger Android ou iOS device ou emulateur avec Visual Studio Code:

  1. VSCode → Extensions → Cordova Tools
  2. Debug → icône engrenage → Cordova (créé un fichier .vscode/launch.json)
  3. Debug → Run Android on emulator
  4. DEBUG CONSOLE

Exemple

www/index.html
<button id="populateDetailsButton">Populate</button>
<div id="deviceDetails"></div>
www/js/index.js
var app = {
    initialize: function() {
        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);

        document.getElementById("populateDetailsButton").addEventListener(
            "click", this.populateDeviceDetails
        );
    },

    populateDeviceDetails: function() {
        var deviceDetails = "";

        deviceDetails += "<br/>Cordova:" + device.cordova;
        deviceDetails += "<br/>Model:" + device.model;
        deviceDetails += "<br/>Platform:" + device.platform;

        document.querySelector("#deviceDetails").innerHTML = deviceDetails;
    },

Permissions

config.xml
<widget xmlns:android="http://schemas.android.com/apk/res/android">
    <platform name="android">
        <allow-intent href="market:*" />
        <config-file parent="/*" target="AndroidManifest.xml">
            <uses-permission android:name="android.permission.GET_ACCOUNTS" />
        </config-file>
    </platform>
myApp\platforms\android\app\src\main\AndroidManifest.xml
<uses-permission android:name="android.permission.GET_ACCOUNTS" />

Copy - Paste / Copier - Coller

Bash.svg
cordova plugin add cordova-plugin-clipboard2
Javascript.svg
cordova.plugins.clipboard.copy(text);
cordova.plugins.clipboard.paste(function (text) { alert(text); });

ADAL

Azure AD Cordova getting started

Bash.svg
# necessary for invoking the Graph API
cordova plugin add cordova-plugin-whitelist
cordova plugin add cordova-plugin-ms-adal
Javascript.svg
// Shows the user authentication dialog box if required
authenticate: function (authCompletedCallback) {
    app.context = new Microsoft.ADAL.AuthenticationContext(authority);
    app.context.tokenCache.readItems().then(function (items) {
        if (items.length > 0) {
            authority = items[0].authority;
            app.context = new Microsoft.ADAL.AuthenticationContext(authority);
        }
        // Attempt to authorize the user silently
        app.context.acquireTokenSilentAsync(resourceUri, clientId)
        .then(authCompletedCallback, function () {
            // We require user credentials, so this triggers the authentication dialog box
            app.context.acquireTokenAsync(resourceUri, clientId, redirectUri)
            .then(authCompletedCallback, function (err) {
                document.querySelector("#signInDetails").innerHTML = "Failed to authenticate: " + err;
                app.error("Failed to authenticate: " + err);
            });
        });
    });
},

// Makes an API call to receive the user profile
requestData: function (authResult, searchText) {
    var req = new XMLHttpRequest();
    var url = resourceUri + "/v1.0/me";

    req.open("GET", url, true);
    req.setRequestHeader('Authorization', 'Bearer ' + authResult.accessToken);

    req.onload = function(e) {
        if (e.target.status >= 200 && e.target.status < 300) {
            app.renderData(JSON.parse(e.target.response));
            return;
        }
        app.error('Data request failed: ' + e.target.response);
    };
    req.onerror = function(e) {
        app.error('Data request failed: ' + e.error);
    }

    req.send();
}

Authentification avec Azure AD, InTune et un broker

SetUseBroker

Javascript.svg
var redirectUri = "msauth://io.cordova.myapp/xxx-Signature-xxx",

Microsoft.ADAL.AuthenticationSettings.setUseBroker(true)
.then(function() {
    app.authenticate(function(authResponse) {
        app.log("Token acquired!<br/>Token will expire on: " + authResponse.expiresOn);
        app.requestData(authResponse);
    });
});
Pour connaitre les redirectUri utiliser le script brokerRedirectPrint.ps1.

Il génère 2 uris, une uri de debug correspondant au keystore android C:\Users\$env:USERNAME\.android\debug.keystore

et une uri de release correspondant au keystore de l'application.

Microsoft Intune App SDK Cordova Plugin

com.microsoft.intune.mam.apppackager.utils.AppPackagerException: Failed to update this app's supported SDK versions.
com.microsoft.intune.mam.apppackager.utils.AppPackagerException: This app's target SDK is above the MAM target SDK. Please upgrade to the newest version of the App Wrapping Tool.

Wrapping failed for android

config.xml
<platform name="android">
    <preference name="android-targetSdkVersion" value="24" />

Erreurs

Error during template compile of 'ɵa'

ERROR in Error during template compile of 'ɵa'
  Function calls are not supported in decorators but 'ɵmakeDecorator' was called in 'Injectable'
    'Injectable' calls 'ɵmakeDecorator'.

npm ERR! code ELIFECYCLE
npm ERR! errno 1
tsconfig.app.json
{
  "compilerOptions": {
    "paths": { "@angular/*": ["../node_modules/@angular/*"] }
  }
}

list-devices is not recognized as an internal or external command

Available browser devices:
'myApp\platforms\browser\cordova\lib\list-devices' is not recognized as an internal or external command,
operable program or batch file.
An unexpected error has occured while running list-devices with code 1: Error: cmd: Command failed with exit code 1
Available browser virtual devices:
'myApp\platforms\browser\cordova\lib\list-emulator-images' is not recognized as an internal or external command,
operable program or batch file.
An unexpected error has occured while running list-emulator-images with code 1: Error: cmd: Command failed with exit code 1
Bash.svg
# Spécifier la plateforme android, par défaut c'est browser qui es tutilisé
cordova run android --list

ERROR: TypeError: Cannot read property 'semver' of null

Bug qui se produit avec les images android > 8.0 dans l'émulateur.
Supprimer ces images corrige le problème.