diff --git a/bower.json b/bower.json
deleted file mode 100644
index 7a42c76f9462f6e0fe439d8c560e792434b354c4..0000000000000000000000000000000000000000
--- a/bower.json
+++ /dev/null
@@ -1,62 +0,0 @@
-{
-  "name": "cesium",
-  "private": "false",
-  "dependencies": {
-    "ionic": "driftyco/ionic-bower#1.3.5",
-    "ionic-material": "0.4.2",
-    "angular-messages": "^1.5.11",
-    "robotodraft": "1.1.0",
-    "angular": "^1.5.11",
-    "angular-moment": "^1.3.0",
-    "angular-animate": "^1.5.11",
-    "angular-sanitize": "^1.5.11",
-    "angular-resource": "^1.5.11",
-    "angular-bind-notifier": "^1.1.11",
-    "angular-image-crop": "duniter-cesium/angular-image-crop#^2.0.2",
-    "angular-ui-router": "^0.2.18",
-    "angular-cache": "^4.6.0",
-    "ng-idle": "HackedByChinese/ng-idle#1.3.2",
-    "chart.js": "2.9.3",
-    "leaflet.awesome-markers": "2.0.2",
-    "leaflet-search": "2.7.2",
-    "angular-leaflet-directive": "duniter-cesium/angular-leaflet-directive#^0.10.1",
-    "Leaflet.EasyButton": "^2.4.0",
-    "leaflet.loading": "Leaflet.loading#^0.1.24",
-    "ui-leaflet": "^2.0.0",
-    "leaflet.markercluster": "Leaflet.markercluster#0.5.0",
-    "Leaflet.FeatureGroup.SubGroup": "0.1.2",
-    "ion-digit-keyboard": "skol-pro/ion-digit-keyboard#973b90c2fc",
-    "angular-expose-api": "duniter-cesium/angular-expose-api#0.3.1",
-    "angular-screenmatch": "^1.0.1",
-    "angular-file-saver": "^1.1.3",
-    "angular-simple-logger": "^0.1.7",
-    "angular-chart.js": "jtblin/angular-chart#1.1.1",
-    "angular-fullscreen-toggle": "duniter-cesium/angular-fullscreen-toggle#1.0.4",
-    "leaflet": "0.7.7",
-    "moment": "^2.24.0",
-    "numeral": "1.5.6",
-    "underscore": "1.10.2",
-    "aes-js": "ricmoo/aes-js#3.1.2",
-    "ngCordova": "0.1.27-alpha",
-    "js-scrypt": "1.2.0",
-    "js-nacl": "1.3.2",
-    "angular-translate": "^2.18.2",
-    "socket.io": "socketio/socket.io#^1.7.4",
-    "socket.io-client": "^1.7.4"
-  },
-  "resolutions": {
-    "angular": "1.5.11",
-    "angular-animate": "^1.5.11",
-    "angular-sanitize": "^1.5.11",
-    "angular-ui-router": "0.2.18",
-    "angular-messages": "1.5.11",
-    "angular-expose-api": "0.3.1",
-    "Leaflet.EasyButton": "~1.3.2",
-    "leaflet": "0.7.7",
-    "leaflet.markercluster": "0.5",
-    "Leaflet.FeatureGroup.SubGroup": "0.1.2",
-    "moment": ">=2.8.0 <2.24.0",
-    "numeral": "1.5.6",
-    "ionic": "1.3.5"
-  }
-}
diff --git a/config.xml b/config.xml
index d4ec8238818dfcf53f2ab3cbfeafb76996fc6154..7356932293311090112fc8f765bfd7ddb0b4a664 100644
--- a/config.xml
+++ b/config.xml
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='utf-8'?>
-<widget android-versionCode="106070" id="fr.duniter.cesium" ios-CFBundleIdentifier="org.duniter.cesium" version="1.6.7" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+<widget android-versionCode="106070" id="fr.duniter.cesium" ios-CFBundleIdentifier="org.duniter.cesium" version="1.6.7" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
     <name>Cesium</name>
     <description>
     An simple App for Duniter wallet
@@ -15,9 +15,13 @@
     <allow-intent href="sms:*" />
     <allow-intent href="mailto:*" />
     <allow-intent href="geo:*" />
+    <allow-intent href="june://*/*" />
+    <allow-intent href="web+june://*/*" />
     <allow-navigation href="http://*/*" />
     <allow-navigation href="https://*/*" />
     <allow-navigation href="data:*" />
+    <allow-navigation href="june://*/*" />
+    <allow-navigation href="web+june://*/*" />
     <icon src="www/img/logo_96px.png" />
     <preference name="webviewbounce" value="false" />
     <preference name="UIWebViewBounce" value="false" />
@@ -109,6 +113,9 @@
         <edit-config file="*-Info.plist" mode="merge" target="NSPhotoLibraryUsageDescription">
             <string>To choose a profile picture</string>
         </edit-config>
+        <edit-config file="*-Info.plist" mode="merge" target="CFBundleURLSchemes">
+            <string>june</string>
+        </edit-config>
         <config-file mode="add" parent="ITSAppUsesNonExemptEncryption" target="*-Info.plist">
             <false />
         </config-file>
@@ -122,30 +129,41 @@
         <icon height="512" src="resources/osx/icon-512.png" width="512" />
         <icon height="1024" src="resources/osx/icon-1024.png" width="1024" />
     </platform>
+    <plugin name="cordova-plugin-minisodium" spec="git+https://github.com/duniter-cesium/cordova-plugin-minisodium.git#v1.0.1" />
+    <plugin name="cordova-plugin-secure-storage-android10" spec="git+https://github.com/duniter-cesium/cordova-plugin-secure-storage-android10#6.0.4" />
     <plugin name="cordova-plugin-camera" spec="^4.1.0">
+        <variable name="CAMERA_USAGE_DESCRIPTION" value="Add picture to the user profile" />
+        <variable name="PHOTOLIBRARY_USAGE_DESCRIPTION" value="Take a picture for the user profile" />
         <variable name="ANDROID_SUPPORT_V4_VERSION" value="28.+" />
     </plugin>
-    <plugin name="cordova-plugin-ionic-webview" spec="^4.1.3">
+    <plugin name="cordova-plugin-ionic-webview" spec="^4.2.1">
         <variable name="ANDROID_SUPPORT_ANNOTATIONS_VERSION" value="28.+" />
     </plugin>
     <plugin name="cordova-plugin-device" spec="^2.0.3" />
     <plugin name="cordova-plugin-dialogs" spec="^2.0.2" />
     <plugin name="cordova-clipboard" spec="^1.3.0" />
-    <plugin name="cordova-plugin-secure-storage-android10" spec="git+https://github.com/duniter-cesium/cordova-plugin-secure-storage-android10#6.0.4" />
-    <plugin name="cordova-plugin-splashscreen" spec="^5.0.3" />
+    <plugin name="cordova-plugin-splashscreen" spec="^5.0.4" />
     <plugin name="cordova-plugin-statusbar" spec="^2.4.3" />
     <plugin name="cordova-plugin-file" spec="^6.0.2" />
     <plugin name="cordova-plugin-vibration" spec="^3.1.1" />
     <plugin name="cordova-plugin-websocket" spec="^0.12.2" />
     <plugin name="cordova-plugin-whitelist" spec="^1.3.4" />
     <plugin name="cordova-plugin-x-toast" spec="^2.7.2" />
-    <plugin name="cordova-plugin-ionic-keyboard" spec="^2.2.1" />
-    <plugin name="cordova-plugin-minisodium" spec="git+https://github.com/duniter-cesium/cordova-plugin-minisodium.git#v1.0.1" />
+    <plugin name="cordova-plugin-ionic-keyboard" spec="^2.2.0" />
     <plugin name="phonegap-plugin-barcodescanner" spec="^8.1.0">
         <variable name="CAMERA_USAGE_DESCRIPTION" value="To scan QRCode" />
         <variable name="ANDROID_SUPPORT_V4_VERSION" value="28.+" />
     </plugin>
+    <plugin name="cordova-plugin-customurlscheme" spec="^5.0.2">
+        <variable name="URL_SCHEME" value="june" />
+        <variable name="ANDROID_SCHEME" value=" " />
+        <variable name="ANDROID_HOST" value=" " />
+        <variable name="ANDROID_PATHPREFIX" value="/" />
+    </plugin>
+    <plugin name="ionic-plugin-keyboard" spec="^2.2.1" />
+    <plugin name="cordova-plugin-androidx" spec="^1.0.2" />
+    <plugin name="cordova-plugin-androidx-adapter" spec="^1.1.0" />
     <engine name="ios" spec="git+https://github.com/duniter-cesium/cordova-ios.git#5.1.0" />
     <engine name="osx" spec="^5.0.0" />
-    <engine name="android" spec="^8.1.0" />
+    <engine name="android" spec="^9.0.0" />
 </widget>
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..b6517bb1d165022c80a76e51260106186d93bed6
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
diff --git a/gulpfile.js b/gulpfile.js
index cf4ded7556f9414107b716269e4117d76a7f1ea8..f1b30e3a4f3262cbd81e29becc6170928a0534be 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -16,6 +16,7 @@ const gulp = require('gulp'),
   ngAnnotate = require('gulp-ng-annotate'),
   zip = require('gulp-zip'),
   del = require('del'),
+  debug = require('gulp-debug'),
   useref = require('gulp-useref'),
   filter = require('gulp-filter'),
   uglify = require('gulp-uglify-es').default,
@@ -302,10 +303,19 @@ function webCopyFiles() {
   log(colors.green('Preparing dist/web files...'));
   let htmlminOptions = {removeComments: true, collapseWhitespace: true};
 
+  const debugOptions = {
+    title: 'Copying',
+    minimal: true,
+    showFiles: argv.debug || false,
+    showCount: argv.debug || false,
+    logger: m => log(colors.grey(m))
+  };
+
   var tmpPath = './dist/web/www';
   return merge(
     // Copy Js (and remove unused code)
     gulp.src('./www/js/**/*.js')
+      .pipe(debug(debugOptions))
       .pipe(removeCode({"no-device": true}))
       .pipe(jshint())
       .pipe(gulp.dest(tmpPath + '/js')),
@@ -336,10 +346,12 @@ function webCopyFiles() {
 
     // Copy fonts
     gulp.src('./www/fonts/**/*.*')
+      .pipe(debug(debugOptions))
       .pipe(gulp.dest(tmpPath + '/fonts')),
 
     // Copy CSS
     gulp.src('./www/css/**/*.*')
+      .pipe(debug(debugOptions))
       .pipe(gulp.dest(tmpPath + '/css')),
 
     // Copy i18n
@@ -348,18 +360,22 @@ function webCopyFiles() {
       .pipe(jsonlint.reporter())
       .pipe(sort())
       .pipe(ngTranslate({standalone:true, module: 'cesium.translations'}))
+      .pipe(debug(debugOptions))
       .pipe(gulp.dest(tmpPath + '/js')),
 
     // Copy img
     gulp.src('./www/img/**/*.*')
+      .pipe(debug(debugOptions))
       .pipe(gulp.dest(tmpPath + '/img')),
 
     // Copy manifest.json
     gulp.src('./www/manifest.json')
+      .pipe(debug(debugOptions))
       .pipe(gulp.dest(tmpPath)),
 
     // Copy lib (JS, CSS and fonts)
     gulp.src(['./www/lib/**/*.js', './www/lib/**/*.css', './www/lib/**/fonts/**/*.*'])
+      .pipe(debug(debugOptions))
       .pipe(gulp.dest(tmpPath + '/lib')),
 
     // Copy license into HTML
@@ -517,18 +533,32 @@ function webUglify(done) {
   if (done) done();
 }
 
-function webIntegrity() {
+function webIntegrity(done) {
   const wwwPath = './dist/web/www';
 
-  log(colors.green('Add integrity hash to <script src> tag...'));
+  const enableIntegrity = argv.release || false;
+
+  if (enableIntegrity) {
+    log(colors.green('Add integrity hash to <script src> tag...'));
+
+    // Process index.html
+    return gulp.series(
+      gulp.src(wwwPath + '/index.html', {base: wwwPath})
+
+      // Add an integrity hash
+      .pipe(sriHash())
+
+      .pipe(rename({ extname: '.integrity.html' }))
+      .pipe(gulp.dest(wwwPath)),
 
-  // Process index.html
-  return gulp.src(wwwPath + '/index.html', {base: wwwPath})
+      gulp.src(wwwPath + '/index.html', {base: wwwPath})
+        .pipe(rename({ extname: '.test.html' }))
+        .pipe(gulp.dest(wwwPath))
+    )
+      .on('end', done);
+  }
 
-    // Add an integrity hash
-    .pipe(sriHash())
-    .pipe(rename({ extname: '.integrity.html' }))
-    .pipe(gulp.dest(wwwPath));
+  if (done) done();
 }
 
 function webApiUglify() {
@@ -591,7 +621,7 @@ function webApiUglify() {
   }
 
   else {
-    log(colors.red('API: Minify JS and CSS files. Skip') + colors.grey(' (missing options --release or --uglify)'));
+    log(colors.red('API: Skipping minify JS and CSS files') + colors.grey(' (missing options --release or --uglify)'));
 
     return gulp.src(tmpPath + '/*/index.html')
       .pipe(useref())             // Concatenate with gulp-useref
@@ -608,26 +638,36 @@ function webApiUglify() {
 
 function webCleanUnusedFiles(done) {
   log(colors.green('Clean unused files...'));
-  const cleanSources = argv.release || argv.uglify || false;
+  const enableUglify = argv.release || argv.useref || argv.uglify || false;
+  const cleanSources = enableUglify;
+  const debugOptions = {
+    title: 'Deleting',
+    minimal: true,
+    showFiles: argv.debug || false,
+    showCount: !argv.debug,
+    logger: m => log(colors.grey(m))
+  };
 
   const wwwPath = './dist/web/www';
 
   if (cleanSources) {
     return merge(
-      // Clean core JS + CSS
+      // Clean core JS
       gulp.src(wwwPath + '/js/**/*.js', {read: false})
-        .pipe(clean()),
-      gulp.src(wwwPath + '/css/**/*.css', {read: false})
+        .pipe(debug(debugOptions))
         .pipe(clean()),
 
       // Clean plugins JS + CSS
       gulp.src(wwwPath + '/plugins/**/*.js', {read: false})
+        .pipe(debug(debugOptions))
         .pipe(clean()),
       gulp.src(wwwPath + '/plugins/**/*.css', {read: false})
+        .pipe(debug(debugOptions))
         .pipe(clean()),
 
       // Unused maps/config.js.map
       gulp.src(wwwPath + '/maps/config.js.map', {read: false, allowEmpty: true})
+        .pipe(debug(debugOptions))
         .pipe(clean())
     )
       .on('end', done);
@@ -640,6 +680,13 @@ function webCleanUnusedFiles(done) {
 function webCleanUnusedDirectories() {
   log(colors.green('Clean unused directories...'));
   const enableUglify = argv.release || argv.uglify || false;
+  const debugOptions = {
+    title: 'Deleting',
+    minimal: true,
+    showFiles: argv.debug || false,
+    showCount: !argv.debug,
+    logger: m => log(colors.grey(m))
+  };
 
   // Clean dir
   const wwwPath = './dist/web/www';
@@ -676,7 +723,7 @@ function webCleanUnusedDirectories() {
   }
 
   return gulp.src(patterns, {read: false, allowEmpty: true})
-    //.pipe(debug({title: 'deleting '}))
+    .pipe(debug(debugOptions))
     .pipe(clean());
 }
 
diff --git a/hooks/after_prepare/010_add_platform_class.js b/hooks/after_prepare/010_add_platform_class.js
index 73302b26639c4317eeda8201d9a73bcfbc2fe2d9..4254793e15729ff5fda3e59a7cd277d214864dd8 100755
--- a/hooks/after_prepare/010_add_platform_class.js
+++ b/hooks/after_prepare/010_add_platform_class.js
@@ -76,7 +76,8 @@ if (rootdir) {
       const platform = platforms[x].trim().toLowerCase();
       let indexPath;
 
-      if(platform == 'android') {
+      if(platform === 'android') {
+        //indexPath = path.join(rootdir, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www', 'index.html');
         indexPath = path.join('platforms', platform, 'assets', 'www', 'index.html');
       } else {
         indexPath = path.join('platforms', platform, 'www', 'index.html');
diff --git a/hooks/after_prepare/020_remove_code.js b/hooks/after_prepare/020_remove_code.js
index bfc0b0bffab614dd74c9c8bd0a9224e99812f82f..6c2c29570595b629fd6316492873428fad49eea3 100755
--- a/hooks/after_prepare/020_remove_code.js
+++ b/hooks/after_prepare/020_remove_code.js
@@ -22,6 +22,7 @@ if (rootdir) {
 
     let wwwPath;
     if(platform === 'android') {
+      //wwwPath = path.join(rootdir, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
       wwwPath = path.join(rootdir, 'platforms', platform, 'assets', 'www');
     } else {
       wwwPath = path.join(rootdir, 'platforms', platform, 'www');
diff --git a/hooks/after_prepare/021_template_cache.js b/hooks/after_prepare/021_template_cache.js
index 2a0a27143dbf9b02873271cf3757ecf813123c3d..c5983a3d103745bb46e61daa91a379021b02f4e8 100755
--- a/hooks/after_prepare/021_template_cache.js
+++ b/hooks/after_prepare/021_template_cache.js
@@ -16,7 +16,8 @@ if (rootdir) {
     let platform = platforms[x].trim().toLowerCase();
 
     let wwwPath;
-    if(platform == 'android') {
+    if (platform === 'android') {
+      //wwwPath = path.join(rootdir, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
       wwwPath = path.join(rootdir, 'platforms', platform, 'assets', 'www');
     } else {
       wwwPath = path.join(rootdir, 'platforms', platform, 'www');
diff --git a/hooks/after_prepare/022_translate.js b/hooks/after_prepare/022_translate.js
index ba998762ad7a25c79767981c88969db5b40e5eb6..a4cf7b902baa181d9ccf320d5d4f07e59401276b 100755
--- a/hooks/after_prepare/022_translate.js
+++ b/hooks/after_prepare/022_translate.js
@@ -17,6 +17,7 @@ if (rootdir) {
 
     let wwwPath;
     if(platform === 'android') {
+      //wwwPath = path.join(rootdir, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
       wwwPath = path.join(rootdir, 'platforms', platform, 'assets', 'www');
     } else {
       wwwPath = path.join(rootdir, 'platforms', platform, 'www');
diff --git a/hooks/after_prepare/040_useref.js b/hooks/after_prepare/040_useref.js
index 0b8d93dccdfc086febb8634d25114846721571fb..9aa438ed007b61c95431e2441d50e44cc3eaab06 100755
--- a/hooks/after_prepare/040_useref.js
+++ b/hooks/after_prepare/040_useref.js
@@ -33,6 +33,7 @@ if (rootdir && !skip) {
 
     let wwwPath;
     if(platform === 'android') {
+      //wwwPath = path.join(rootdir, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
       wwwPath = path.join(rootdir, 'platforms', platform, 'assets', 'www');
     } else {
       wwwPath = path.join(rootdir, 'platforms', platform, 'www');
diff --git a/hooks/after_prepare/050_clean_unused_directories.js b/hooks/after_prepare/050_clean_unused_directories.js
index 8e0b1c37dc92adb232716d3978037e03a25475ab..31023fc4f14917ef1d496e319c71cc80a910bb9f 100755
--- a/hooks/after_prepare/050_clean_unused_directories.js
+++ b/hooks/after_prepare/050_clean_unused_directories.js
@@ -23,6 +23,7 @@ if (rootdir && !skip) {
 
     let wwwPath;
     if(platform === 'android') {
+      //wwwPath = path.join(rootdir, 'platforms', platform, 'app', 'src', 'main', 'assets', 'www');
       wwwPath = path.join(rootdir, 'platforms', platform, 'assets', 'www');
     } else {
       wwwPath = path.join(rootdir, 'platforms', platform, 'www');
diff --git a/hooks/after_prepare/060_prepare_android_manifest.js b/hooks/after_prepare/060_prepare_android_manifest.js
index fb6e20a198214d8de118ccfeaa6ca639f2867d7a..1769005180e812c82af2ee5a472c96e004ee6fab 100755
--- a/hooks/after_prepare/060_prepare_android_manifest.js
+++ b/hooks/after_prepare/060_prepare_android_manifest.js
@@ -15,9 +15,10 @@ if (rootdir) {
 
     let platform = platforms[x].trim().toLowerCase();
 
-    if(platform = 'android') {
-      let platformPath = path.join(rootdir, 'platforms', platform);
-      let androidManifestFile = path.join(platformPath, 'AndroidManifest.xml');
+    if (platform === 'android') {
+      //let srcMainPath = path.join(rootdir, 'platforms', platform, 'app', 'src', 'main');
+      let srcMainPath = path.join(rootdir, 'platforms', platform);
+      let androidManifestFile = path.join(srcMainPath, 'AndroidManifest.xml');
 
       // Clean unused directories
       console.log('-----------------------------------------');
@@ -38,7 +39,7 @@ if (rootdir) {
         // add <uses-sdk> (replace 'targetSdkversion' and add tools:overrideLibrary)
         .pipe(replace(/(<\/manifest>)/, '    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="29" tools:overrideLibrary="org.kaliumjni.lib" />\n$1'))
 
-        .pipe(gulp.dest(platformPath));
+        .pipe(gulp.dest(srcMainPath));
 
       console.log('-----------------------------------------');
 
diff --git a/hooks/after_prepare/061_copy_build_extras.js b/hooks/after_prepare/061_copy_build_extras.js
index 117a6c8327253d1682835a7fd3923cda58a3b83d..b648ed504ee05f7c4e093a7b1c7b498d1101d019 100755
--- a/hooks/after_prepare/061_copy_build_extras.js
+++ b/hooks/after_prepare/061_copy_build_extras.js
@@ -65,13 +65,13 @@ if (rootdir) {
           }
 
           const signing_file = build_dir + '/release-signing.properties';
-          if (!fs.existsSync(signing_file)) {
+          if (!fs.existsSync(signing_file) && !fs.existsSync(android_dir + '/release-signing.properties')) {
             log(colors.red(' File ' + signing_file + 'not found. Skipping copy to ' + android_dir));
             log(colors.red('   WARNING: Release APK files will not be signed !'));
           }
 
         } else {
-          log(colors.orange(' Directory ' + build_dir + 'not found. Skipping copy to ' + android_dir));
+          log(colors.red(' Directory ' + build_dir + 'not found. Skipping copy to ' + android_dir));
         }
       }
     } catch (e) {
diff --git a/package.json b/package.json
index c79fcb08071be4187bdb2fdcb51bc59fb1ea35cb..375dfb65483196e5bad21646a7aa110c15b4e9f0 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,8 @@
     "start:firefox": "gulp webExtCompile && web-ext run --source-dir ./dist/web/ext/",
     "docker:build": "sudo docker build . -t cesium/release",
     "docker:run": "sudo docker run -ti --rm -p 8100:8100 -p 35729:35729 -v .:/cesium:rw cesium/release",
-    "build:web": "gulp config --env default && gulp webBuild --release"
+    "build:web": "gulp config --env default && gulp webBuild --release",
+    "build:webExt": "gulp config --env default && gulp webExtBuild --release"
   },
   "keywords": [
     "duniter",
@@ -78,6 +79,14 @@
     "@bower_components/socket.io-client": "socketio/socket.io-client#^1.7.4",
     "@bower_components/ui-leaflet": "angular-ui/ui-leaflet#v2.0.0",
     "@bower_components/underscore": "jashkenas/underscore#1.10.2",
+    "through2": "^4.0.2",
+    "uuid": "3.2.1"
+  },
+  "devDependencies": {
+    "@ionic/cli": "^6.11.0",
+    "@ionic/v1-toolkit": "^3.2.0",
+    "@prantlf/gulp-jsonlint": "^2.4.0",
+    "ansi-colors": "^4.1.1",
     "cordova": "^8.1.2",
     "cordova-android": "^8.1.0",
     "cordova-clipboard": "^1.3.0",
@@ -91,10 +100,8 @@
     "cordova-plugin-dialogs": "^2.0.2",
     "cordova-plugin-file": "^6.0.2",
     "cordova-plugin-ionic-keyboard": "^2.2.0",
-    "cordova-plugin-ionic-webview": "^4.1.3",
-    "cordova-plugin-minisodium": "git+https://github.com/duniter-cesium/cordova-plugin-minisodium#v1.0.1",
-    "cordova-plugin-secure-storage-android10": "git+https://github.com/duniter-cesium/cordova-plugin-secure-storage-android10.git#6.0.4",
-    "cordova-plugin-splashscreen": "^5.0.3",
+    "cordova-plugin-ionic-webview": "^4.2.1",
+    "cordova-plugin-splashscreen": "^5.0.4",
     "cordova-plugin-statusbar": "^2.4.3",
     "cordova-plugin-vibration": "^3.1.1",
     "cordova-plugin-websocket": "^0.12.2",
@@ -102,28 +109,27 @@
     "cordova-plugin-x-toast": "^2.7.2",
     "ionic-plugin-keyboard": "^2.2.1",
     "phonegap-plugin-barcodescanner": "^8.1.0",
-    "uuid": "3.2.1"
-  },
-  "devDependencies": {
-    "@ionic/cli": "^6.11.0",
-    "@ionic/v1-toolkit": "^3.2.3",
-    "@prantlf/gulp-jsonlint": "git+https://github.com/prantlf/gulp-jsonlint#2.4.0",
+    "cordova-plugin-customurlscheme": "^5.0.2",
+    "cordova-plugin-minisodium": "git+https://github.com/duniter-cesium/cordova-plugin-minisodium.git#v1.0.1",
+    "cordova-plugin-secure-storage-android10": "git+https://github.com/duniter-cesium/cordova-plugin-secure-storage-android10.git#6.0.4",
     "cordova-uglify": "^0.3.4",
     "del": "^5.1.0",
     "delete-empty": "^0.1.3",
     "event-stream": "3.3.4",
+    "fancy-log": "^1.3.3",
     "glob": "^5.0.15",
     "graceful-fs": "^4.2.3",
-    "gulp": "4.0.2",
+    "gulp": "^4.0.2",
     "gulp-angular-templatecache": "^3.0.0",
     "gulp-angular-translate": "^0.1.4",
-    "gulp-base64": "^0.1.3",
     "gulp-base64-v2": "^1.0.4",
     "gulp-bump": "^3.1.3",
     "gulp-clean": "^0.4.0",
     "gulp-clean-css": "^4.3.0",
+    "gulp-cli": "^2.3.0",
     "gulp-css-base64": "^1.3.4",
     "gulp-csso": "^4.0.1",
+    "gulp-debug": "^4.0.0",
     "gulp-filter": "^6.0.0",
     "gulp-footer": "^2.0.2",
     "gulp-header": "^2.0.9",
@@ -150,13 +156,20 @@
     "node-sass": "^4.14.1",
     "playup": "^1.1.0",
     "vinyl-fs": "^3.0.3",
-    "yargs": "^5.0.0",
-    "web-ext": "^4.3.0"
+    "web-ext": "^4.3.0",
+    "yargs": "^5.0.0"
   },
   "peerDependencies": {
+    "gulp": "^4.0.2",
+    "gulp-cli": "^2.3.0",
     "leaflet": "Leaflet/Leaflet#v0.7.7",
     "nopt": "4.0.1"
   },
+  "resolutions": {
+    "gulp": "4.0.2",
+    "gulp-cli": "2.3.0",
+    "graceful-fs": "4.2.4"
+  },
   "cordovaPlugins": [
     "cordova-plugin-whitelist",
     "cordova-plugin-splashscreen",
@@ -173,7 +186,8 @@
     "cordova-plugin-minisodium",
     "phonegap-plugin-barcodescanner",
     "cordova-plugin-ionic-keyboard",
-    "cordova-plugin-ionic-webview@4.1.2"
+    "cordova-plugin-ionic-webview@4.1.2",
+    "cordova-plugin-customurlscheme"
   ],
   "cordovaPlatforms": [
     {
@@ -191,7 +205,8 @@
     "plugins": {
       "cordova-plugin-camera": {
         "CAMERA_USAGE_DESCRIPTION": "Add picture to the user profile",
-        "PHOTOLIBRARY_USAGE_DESCRIPTION": "Take a picture for the user profile"
+        "PHOTOLIBRARY_USAGE_DESCRIPTION": "Take a picture for the user profile",
+        "ANDROID_SUPPORT_V4_VERSION": "28.+"
       },
       "cordova-plugin-device": {},
       "cordova-plugin-dialogs": {},
@@ -215,7 +230,13 @@
       },
       "cordova-plugin-file": {},
       "cordova-plugin-androidx": {},
-      "cordova-plugin-androidx-adapter": {}
+      "cordova-plugin-androidx-adapter": {},
+      "cordova-plugin-customurlscheme": {
+        "URL_SCHEME": "june",
+        "ANDROID_SCHEME": " ",
+        "ANDROID_HOST": " ",
+        "ANDROID_PATHPREFIX": "/"
+      }
     },
     "platforms": [
       "ios",
@@ -224,7 +245,7 @@
     ]
   },
   "engines": {
-    "node": ">= 10.20.0",
+    "node": ">= 12.18.3",
     "yarn": ">= 1.22.0"
   }
 }
diff --git a/www/js/controllers/home-controllers.js b/www/js/controllers/home-controllers.js
index 9898e8766b9e93d6a5d7b00fd6a2eb5ebb8497a8..8c0bd8980ea036de7644a4bbb76d042672cc30ff 100644
--- a/www/js/controllers/home-controllers.js
+++ b/www/js/controllers/home-controllers.js
@@ -26,7 +26,7 @@ angular.module('cesium.home.controllers', ['cesium.platform', 'cesium.services']
   .controller('HomeCtrl', HomeController)
 ;
 
-function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $http, UIUtils,
+function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $http, UIUtils, BMA,
                         csConfig, csCache, csPlatform, csCurrency, csSettings, csHttp) {
   'ngInject';
 
@@ -185,39 +185,62 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
     console.debug("[home] Detecting external uri: ", uri);
     var parts = csHttp.uri.parse(uri);
 
-    if (parts.protocol === 'g1:') {
-      console.debug("[home] Applying g1 uri...", parts);
+    var state,stateParams;
 
-      // Transfer
-      if (parts.hostname && parts.search.indexOf('amount=') !== -1) {
-        return $state.go('app.new_transfer_pubkey', {
-          pubkey: parts.hostname
-        });
-      }
+    if (parts.protocol === 'g1:' || parts.protocol === 'june:') {
+      console.debug("[home] Applying uri...", parts);
 
       // Pubkey
-      else if (parts.hostname && BMA.regexp.PUBKEY.test(parts.hostname)) {
-        var pubkey = parts.hostname;
-        return $state.go('app.wot_identity', {
-          pubkey: pubkey
-        });
+      if (parts.hostname && BMA.regexp.PUBKEY.test(parts.hostname)) {
+        state = 'app.wot_identity';
+        stateParams = {pubkey: parts.hostname};
+      }
+      else if ((parts.hostname === 'wallet' || parts.hostname === 'pubkey') && BMA.regexp.PUBKEY.test(parts.pathSegments[0])) {
+        state = 'app.wot_identity';
+        stateParams = {pubkey: parts.pathSegments[0]};
       }
 
-      // Search by uid
+      // Block
+      else if (parts.hostname === 'block') {
+        state = 'app.view_block';
+        stateParams = {number: parts.pathSegments[0]};
+      }
+
+      // Otherwise, try to a wot lookup
       else if (parts.hostname && BMA.regexp.USER_ID.test(parts.hostname)) {
-        var uid = parts.hostname;
-        return $state.go('app.wot_lookup.tab_search', {
-          q: uid
-        });
+        state = 'app.wot_lookup.tab_search';
+        stateParams = {q: parts.hostname};
       }
     }
-    else {
-      console.error("[home] Unknown protocol, in URI: " + uri);
+
+    if (state === 'app.wot_identity' && parts.searchParams && (angular.isDefined(parts.searchParams.action) || angular.isDefined(parts.searchParams.amount))) {
+      stateParams = angular.merge(stateParams,
+        // Add default actions
+        { action: 'transfer'},
+        // Add path params
+        parts.searchParams);
     }
 
-    // Redirect to home
-    return $state.go('app.home');
-  }
+    // Open the state
+    if (state) {
+      $ionicHistory.clearHistory();
+      $ionicHistory.nextViewOptions({
+        disableAnimate: false,
+        disableBack: true,
+        historyRoot: true
+      });
+      return $state.go(state, stateParams, {
+        reload: true,
+        inherit: false,
+        notify: true
+      });
+    }
+    else {
+      console.error("[home] Unknown URI format: " + uri);
+      this.loading = false;
+      return UIUtils.alert.error("ERROR.UNKNOWN_URI_FORMAT", uri);
+    }
+  };
 
   // For DEV ONLY
   /*$timeout(function() {
diff --git a/www/js/controllers/wot-controllers.js b/www/js/controllers/wot-controllers.js
index 2fe87e0f932e3b2996811efc9d7021fffdd2dcaf..4f109795863360d862e3fc27cc79ae9892c4fcfc 100644
--- a/www/js/controllers/wot-controllers.js
+++ b/www/js/controllers/wot-controllers.js
@@ -43,7 +43,7 @@ angular.module('cesium.wot.controllers', ['cesium.services'])
       })
 
       .state('app.wot_identity', {
-        url: "/wot/:pubkey/:uid?action&block",
+        url: "/wot/:pubkey/:uid?action&block&amount&comment",
         views: {
           'menuContent': {
             templateUrl: "templates/wot/view_identity.html",
@@ -943,6 +943,8 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
 
     // Reset action param
     stateParams.action = null;
+    stateParams.amount = null;
+    stateParams.comment = null;
 
     // Update location href
     $ionicHistory.nextViewOptions({
@@ -959,10 +961,10 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
   };
 
   $scope.doAction = function(action, options) {
-    if (action == 'certify') {
+    if (action === 'certify') {
       return $scope.certify();
     }
-    if (action == 'transfer') {
+    if (action === 'transfer') {
       $scope.showTransferModal(options);
     }
   };
@@ -1057,7 +1059,7 @@ function WotIdentityViewController($scope, $rootScope, $controller, $timeout, $s
       $scope.doMotion();
       if (state.stateParams && state.stateParams.action) {
         $timeout(function() {
-          $scope.doAction(state.stateParams.action.trim());
+          $scope.doAction(state.stateParams.action.trim(), state.stateParams);
         }, 100);
 
         $scope.removeActionParamInLocationHref(state);
diff --git a/www/js/platform.js b/www/js/platform.js
index e081509882d99c2c7b92c71d03c567a79b5353fd..b0fffc3e6f3991030ecee97f1c7c702b07cefe41 100644
--- a/www/js/platform.js
+++ b/www/js/platform.js
@@ -229,14 +229,18 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       return $q.when();
     }
 
-    function registerProtocol() {
-      console.debug("[platform] Register protocol g1://")
-      try {
-        navigator.registerProtocolHandler("web+june", "#/app/home?uri=%s", "Cesium");
-      }
-      catch(err) {
-        console.error(err)
-      }
+    function registerProtocols() {
+      var protocols = ['web+june'/*, 'web+g1', 'g1'*/];
+
+      _.each(protocols, function(protocol) {
+        console.debug("[platform] Registering protocol '%s'...".format(protocol));
+        try {
+          navigator.registerProtocolHandler(protocol, "#/app/home?uri=%s", "Cesium");
+        }
+        catch(err) {
+          console.error("[platform] Error while registering protocol '%s'".format(protocol), err);
+        }
+      });
     }
 
     function addListeners() {
@@ -271,7 +275,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       // Avoid change state
       disableChangeState();
 
-      registerProtocol();
+      registerProtocols();
 
       // We use 'ionicReady()' instead of '$ionicPlatform.ready()', because this one is callable many times
       startPromise = ionicReady()
diff --git a/www/js/services/http-services.js b/www/js/services/http-services.js
index ad5f86cf5e75b4752c7e8c42f94ed53398e8e2ab..dc6f6b173a24393cae060095e14250f75c57bd25 100644
--- a/www/js/services/http-services.js
+++ b/www/js/services/http-services.js
@@ -317,12 +317,22 @@ angular.module('cesium.http.services', ['cesium.cache.services'])
 
   // See doc : https://gist.github.com/jlong/2428561
   function parseUri(uri) {
-    var protocol;
+    var protocol, hostname;
 
     // G1 URI (see G1lien)
-    if (uri.startsWith('web+june://') || uri.startsWith('g1://')) {
-      protocol = 'g1:';
-      uri = uri.replace(/^(g1|web+june):\/\//, 'http:');
+    if (uri.startsWith('g1:') || uri.startsWith('june:') || uri.startsWith('web+june:')) {
+      protocol = 'june:';
+      var path = uri.replace(/^(g1|web\+june|june):(\/\/)?/, '');
+
+      // Store hostname here, because parse will apply a lowercase
+      hostname = path;
+      if (hostname.indexOf('/') !== -1) {
+        hostname = hostname.substr(0, path.indexOf('/'));
+      }
+      if (hostname.indexOf('?') !== -1) {
+        hostname = hostname.substr(0, path.indexOf('?'));
+      }
+      uri = 'http://' + path;
     }
 
     var parser = document.createElement('a');
@@ -333,15 +343,33 @@ angular.module('cesium.http.services', ['cesium.cache.services'])
       pathname = pathname.substring(1);
     }
 
+    var searchParams;
+    if (parser.search && parser.search.startsWith('?')) {
+      searchParams = parser.search.substr(1).split('&')
+        .reduce(function(res, searchParam) {
+          if (searchParam.indexOf('=') !== -1) {
+            var key = searchParam.substr(0, searchParam.indexOf('='));
+            var value = searchParam.substr(searchParam.indexOf('=') + 1);
+            res[key] = value;
+          }
+          else {
+            res[searchParam] = true; // default value
+          }
+          return res;
+        }, {});
+    }
+
     var result = {
       protocol: protocol ? protocol : parser.protocol,
-      hostname: parser.hostname,
+      hostname: hostname ? hostname : parser.hostname,
       host: parser.host,
       port: parser.port,
       username: parser.username,
       password: parser.password,
       pathname: pathname,
+      pathSegments: pathname ? pathname.split('/') : [],
       search: parser.search,
+      searchParams: searchParams,
       hash: parser.hash
     };
     parser.remove();