http://slides.com/fingerproof/
polytech-2020/live
@fingerproof - @PhoneGapFR - YouTube
sebastien.pittion@viseo.com
VOUS AVEZ
QUATRE HEURES
DIX MINUTES
ÇA VA ÊTRE AWESOME, DUDES !
Des questions ?
/page.html
91.198.174.225
DNS
Encodage : UTF-8
Protocole
Nom d'hôte
Ressource
Client - local (navigateur)
URL
http://
fr.wikipedia.org
Serveur - distant
http://
/page.html
Requête HTTP
MIME : text/html
Réponse HTTP
And counting...
Moi et mon lance requêtes
Google Chrome
MDN ou webplatform, banissez w3school
<!DOCTYPE html>
<html lang="fr">
<head>
<!-- metadata -->
<meta charset="utf-8">
<title>Exemple de HTML</title>
</head>
<body>
<!-- content -->
Ceci est une phrase avec un <a href="cible.html">hyperlien</a>.
<p>Ceci est un paragraphe où il n’y a pas d’hyperlien.</p>
<img src="puppy.jpg" alt="Un bébé chien">
</body>
</html>
<link rel="stylesheet" href="styles.css">
<!-- or --->
<style>/* ... */</style>
.ninja {
visibility: hidden;
color: black;
}
<script src="script.js"></script>
<!-- or --->
<script>/* ... */</script>
Soumission d'un formulaire
* AJAX (Asynchronous JavaScript and XML)
// A simple template string.
var template = '<p>Hello <%= user %>!</p>';
// Parse using lodash's template utility.
var compiled = _.template(template);
// Pass data to the compiled function.
var result = compiled({ user: 'dude' });
// Use jQuery to turn the string into DOM.
var $element = jQuery(result);
// Append the fragment to the body element.
$element.appendTo(document.body);
Hello dude!
http://myapp.com/
#/details/3
// Regular expression, one capturing group.
var regex = /^#?\/details\/(\d+)$/;
// Use jQuery again to listen to the event.
$(window).on('hashchange', function (event) {
// Execute the regular expression.
var matches = regex.exec(location.hash);
// Do nothing if not a match.
if (!matches) { return; }
// Tell the browser to do nothing.
event.preventDefault();
// Call the showDetails function passing
// the clicked item id as a parameter.
showDetails(matches[1]);
});
@media (max-width: 320px) {
.column { display: block; }
}
<meta name="viewport" content="width=device-width, initial-scale=1">
$ pwd
$ cd
$ ls
$ command -?
$ command -h
$ command -H
$ command --help
man command
mkdir, rm, cp, mv, touch, chown, cat
$ npm --version
{
"private": true,
"name": "CHANGE ME!",
"version": "0.1.0",
"scripts": {
"start": "ng serve",
"build": "ng build"
},
"dependencies": {
"@angular/core": "^7.2.2",
"@ionic/angular": "^4.0.0"
...
},
"devDependencies": {
"@angular/cli": "~7.2.3",
"typescript": "~3.1.6"
...
}
}
function myFunction(a, b) {
return a + b;
}
const myOtherFunction = (a, b) => a + b;
class MyClass {
static prop1 = myFunction;
prop2 = null;
prop3 = undefined;
constructor() {
this.prop2 = myOtherFunction;
this.prop3 = this.add(3, 4);
}
add(a, b) {
return this.prop2(a, b);
}
}
console.log(MyClass.prop1, MyClass.prop1(1, 2));
const instance = new MyClass();
console.log(instance.add(5, 6));
const myConstant = 42;
let myVariable;
...
myVariable = 42;
myObject = {
prop1: true,
prop2: false,
prop3: myConstant * myVariable,
prop4: 3.14159,
prop5: 'hello',
'prop 6': [1, '2', false, [], ''],
'prop7': {}
};
console.log(myObject);
const promise1 = Promise.resolve();
const promise2 = Promise.reject();
promise1
.then(() => console.log('ok 1'))
.catch(() => console.log('error 1'))
.finally(() => console.log('finally 1'));
console.log('after promise 1');
promise2
.then(value => console.log('ok 2'))
.catch(error => console.log('error 2'))
.finally(() => console.log('finally 2'));
console.log('after promise 2');
// after promise 1
// after promise 2
// ok 1
// finally 1
// error 2
// finally 2
class MyClass {
value = 42;
method1() {
return this.value;
}
method2 = () => {
return this.value;
}
}
const instance = new MyClass();
instance.method1()); // 42
instance.method2()); // 42
const { method1, method2 } = instance;
method1(); // error
method2(); // 42
async function getData<T>(
observable: Observable<T>
): Promise<T> {
const data: T = await observable.toPromise();
// Maybe do stuff...
return data;
}
// home.component.ts
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Model } from './model';
@Component({
selector: 'app-home',
templateUrl: 'home.component.html'
})
export class HomePage {
models: Model[] = [];
constructor(
private readonly router: Router
) {
this.models.push({
name: 'Model name',
...
id: 1
});
this.log(this.models, this.router);
}
log(...args: any[]): void {
console.log(...args);
}
}
// model.ts
export interface Model {
id: number;
name: string;
isSelected: boolean;
imageUrl: string;
subModels: Model[];
}
<!-- home.component.html -->
<ul>
<li *ngFor="let model of models">
<h1>
{{ model.name | uppercase }}
</h1>
<img [src]="model.imageUrl" alt="...">
<p *ngIf="model.isSelected">
{{ model.subModels | json }}
<p>
<button
type="button"
(click)="log($event, model)">
Click here
</button>
</li>
</ul>
// home.component.ts
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Model } from './model';
@Component({
selector: 'app-home',
templateUrl: 'home.component.html'
})
export class HomePage {
models: Model[] = [];
constructor(
private readonly router: Router
) {
this.models.push({
name: 'Model name',
...
id: 1
});
this.log(this.models, this.router);
}
log(...args: any[]): void {
console.log(...args);
}
}
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
@Component({
...
})
export class HomePage {
constructor(
private readonly router: Router,
private readonly http: HttpClient
) {}
}
$ which git
$ npm install --global ionic
$ ionic start tp-pw blank
(Répondre non aux questions)
$ npm start
+
Wait a minute, what?!
Bootstrap, littéralement
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<widget
xmlns="http://www.w3.org/ns/widgets"
xmlns:cdv="http://cordova.apache.org/ns/1.0"
id="com.reverse.myapp"
version="1.0.0">
<name>My App</name>
<description>This app is so great you won't believe it!</description>
<content src="index.html"/>
<access origin="*"/>
<preference name="AutoHideSplashScreen" value="false"/>
<preference name="ShowSplashScreenSpinner" value="false"/>
<preference name="FadeSplashScreen" value="true"/>
<preference name="FadeSplashScreenDuration" value="250"/>
<preference name="Orientation" value="portrait"/>
<platform name="android">
<icon src="resources/android/icon/icon-hdpi.png" density="hdpi"/>
<icon src="resources/android/icon/icon-xhdpi.png" density="xhdpi"/>
<splash src="resources/android/splash/splash-port-hdpi.png" density="port-hdpi"/>
<splash src="resources/android/splash/splash-port-xhdpi.png" density="port-xhdpi"/>
</platform>
</widget>
$ cordova build ios --release
0836656565
$ cordova build android --release
All my friends on Google+
<gap:platform>
Preferences communes
(attention à la casse) :
phonegap-version, orientation, fullscreen
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www/w3.org/ns/widgets"
xmlns:gap="http://phonegap.com/ns/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="com.reverse.domain"
version="1.0.0">
<name>Mon application</name>
<description>Une description</description>
</widget>
Did I told you I don't like XML?
<config-file>
<preference name="orientation" value="portrait"/>
Je vous fais les niveaux avec ça ?
<!-- Equivalent Info.plist -->
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<!-- Personnalisation avancée PGB -->
<config-file platform="ios" mode="replace" parent="UISupportedInterfaceOrientations">
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</config-file>
<!-- Ou encore -->
<config-file platform="ios" mode="delete" parent="UISupportedInterfaceOrientations">
<array>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
</config-file>
<!-- Ou même seulement -->
<config-file platform="ios" mode="add" parent="UISupportedInterfaceOrientations">
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</config-file>
<config-file platform="android" mode="merge" parent="/manifest/application/">
<activity android:screenOrientation="portrait"/>
</config-file>
<icon src="icon.png"/>
<splash src="splash.png"/>
<plugin/>
<gap:plugin/>
<gap:plugin
name="com.phonegap.plugins.example"
version="1.0.2"
source="npm">
<param name="APIKey" value="12345678"/>
<param name="APISecret" value="12345678"/>
</gap:plugin>
<gap:plugin name="cordova-plugin-whitelist" source="npm"/>
Note : Adobe Air, Cordova desktop ?
$ which openssl
$ openssl genrsa -des3 -out mykey.key 2048
$ openssl req -new
-key mykey.key
-out ios.csr -subj "/emailAddress=EMAIL, CN=COMPANY-NAME, C=COUNTRY-CODE"
$ openssl x509 -in developer_identity.cer
-inform DER -out developer_identity.pem
-outform PEM
$ openssl pkcs12 -export
-inkey mykey.key -in developer_identity.pem
-out iphone_dev.p12
Keytool fourni avec Java
$ which keytool
$ keytool -genkey
-v -keystore keystore_name.keystore
-alias alias_name
-keyalg RSA -keysize
2048 -validity 10000
Uploader les fichiers
générés précédemment (onglet Signing Keys)
Les clés sont verrouillées
par défaut et une heure après saisie des mots
de passe (forts) associés
Vous pouvez maintenant déclencher de nouvelles compilations
J'ai failli attendre !
Onglet Plugins : vue d'ensemble des plugins installés et leurs versions
Onglet Collaborators :
gestion des droits des intervenants invités
Onglet Settings : présente certaines préférences du
fichier config.xml et l'accès
à Debugging et Hydration
$ phonegap help remote
$ phonegap remote login
$ phonegap remote build
I'm so Sublime
$ cat ~/.bash_profile
souce ~/.bash_profile
ADT_SDK=~/dev/adt/sdk
export PATH=$PATH:$ADT_SDK/platform-tools:$ADT_SDK/tools
$ java -version
&& javac -version
$ source .bash_profile
&& java -version && javac -version
ADT_SDK=~/dev/adt/sdk
export JAVA_HOME=~/dev/jdk
export PATH=$PATH:$ADT_SDK/platform-tools:$ADT_SDK/tools:$JAVA_HOME/bin
$ android
$ sudo npm install
--global ios-sim
ios-deploy
$ sudo npm install
--global cordova
$ sudo npm install
--global phonegap
$ cordova help
$ cordova create
$ cordova platform
$ cordova plugin
$ cordova prepare
$ cordova compile
$ cordova run
$ cordova serve
$ phonegap <help|info>
$ phonegap create
$ phonegap build
$ phonegap install
$ phonegap run
$ phonegap platform
$ phonegap plugin
$ phonegap template
$ phonegap serve
$ phonegap push
$ phonegap <local|remote>
$ phonegap prepare
$ phonegap compile
$ phonegap emulate
$ phonegap cordova
Hormis la prise en charge de PhoneGap Build, n'apporte
rien de nouveau
En général on utilise la CLI Cordova
$ cordova create <name>
$ cordova platform add ios
$ cordova plugin add <id>
$ cordova emulate ios
$ cordova serve
$ cordova prepare
$ chmod +x <script>
$ cordova serve
$ cordova platform add browser
$ sudo npm install --global weinre
$ weinre --boundHost
<ip> --httpPort 8080
http://<ip>:8080/client/#anonymous
<script src="http://<ip>:8080/target/target-script-min.js#anonymous"></script>
LiveReload (Mac),
CodeKit (Mac), Prepros (Mac,
Windows, Linux) + Extensions
Existe depuis longtemps
sur desktop (<3 Workflow)
Délicat dans le cadre
de Cordova (pre 3.0) :
phase de compilation
Déploiement simultané
sur plusieurs appareils,
simulateurs et plates-formes