From 9a5b5e2e4fa388870385ab4c9436b95fe8bbc343 Mon Sep 17 00:00:00 2001
From: Benoit Lavenier <benoit.lavenier@e-is.pro>
Date: Thu, 13 Aug 2020 19:45:10 +0200
Subject: [PATCH] [enh] Handle URI used to launch the app

---
 config.xml                                    |   6 +-
 gradle/wrapper/gradle-wrapper.properties      |   5 -
 gulpfile.js                                   |   5 +
 package.json                                  |   8 +-
 .../android/build/app/build-extras.gradle     |   9 --
 .../java/fr/duniter/cesium/MainActivity.java  | 130 ------------------
 www/js/controllers/home-controllers.js        |  93 +++----------
 www/js/platform.js                            | 116 +++++++++++++---
 www/js/services/device-services.js            |  44 +++++-
 www/js/services/http-services.js              |   4 +-
 10 files changed, 177 insertions(+), 243 deletions(-)
 delete mode 100644 gradle/wrapper/gradle-wrapper.properties
 delete mode 100644 resources/android/build/app/src/main/java/fr/duniter/cesium/MainActivity.java

diff --git a/config.xml b/config.xml
index 5900c5d31..15fe32f64 100644
--- a/config.xml
+++ b/config.xml
@@ -55,9 +55,9 @@
     </feature>
     <platform name="android">
         <preference name="AndroidXEnabled" value="true" />
-        <preference name="cdvMinSdkVersion" value="16" />
-        <preference name="cdvCompileSdkVersion" value="29" />
-        <preference name="cdvBuildToolsVersion" value="29.0.2" />
+        <preference name="AndroidLaunchMode" value="singleTask" />
+        <preference name="android-minSdkVersion" value="16" />
+        <preference name="android-targetSdkVersion" value="29" />
         <hook src="scripts/hooks/before_prepare.js" type="before_prepare" />
         <hook src="scripts/hooks/after_prepare.js" type="after_prepare" />
         <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index ee93d6819..000000000
--- a/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https://services.gradle.org/distributions/gradle-6.5.1-all.zip
diff --git a/gulpfile.js b/gulpfile.js
index be0a2d265..672c8fe31 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1264,6 +1264,11 @@ function cdvAndroidManifest() {
     // add <uses-sdk> (tools:overrideLibrary)
     .pipe(replace(/(<\/manifest>)/, '    <uses-sdk tools:overrideLibrary="org.kaliumjni.lib,org.apache.cordova" />\n$1'))
 
+    // Add URI scheme web+june
+    // Patch invalid intent-filter (should be a bug of cordova-plugin-customurlschema)
+    // FIXME : this cause too many intent-filter are appended, on each build
+    //.pipe(replace('<data android:host=" " android:pathPrefix="/" android:scheme=" " />', '<data android:scheme="web+june" />'))
+
     .pipe(gulp.dest(srcMainPath));
 }
 
diff --git a/package.json b/package.json
index 4b72ac018..d5feb51e5 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
     "lint": "node scripts/node/jshint.js",
     "install-platforms": "ionic cordova prepare",
     "start": "ionic serve",
-    "start:firefox": "gulp webExtCompile && web-ext run --source-dir ./dist/web/ext/",
+    "start:webExt": "gulp webExtCompile && web-ext run --source-dir ./dist/web/ext/",
     "start:android": "ionic cordova run android --color",
     "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",
@@ -203,9 +203,9 @@
       "cordova-plugin-androidx-adapter": {},
       "cordova-plugin-customurlscheme": {
         "URL_SCHEME": "june",
-        "ANDROID_SCHEME": " ",
-        "ANDROID_HOST": " ",
-        "ANDROID_PATHPREFIX": "/"
+        "ANDROID_SCHEME": "http",
+        "ANDROID_HOST": "g1.duniter.org",
+        "ANDROID_PATHPREFIX": "/wallet"
       },
       "cordova-plugin-secure-storage-android10": {},
       "cordova-plugin-minisodium": {}
diff --git a/resources/android/build/app/build-extras.gradle b/resources/android/build/app/build-extras.gradle
index 00875ab7b..63ce8dcd9 100644
--- a/resources/android/build/app/build-extras.gradle
+++ b/resources/android/build/app/build-extras.gradle
@@ -9,12 +9,3 @@ dependencies {
     exclude group: 'com.android.support', module:'support-v4'
   }
 }
-
-// Overrides the value of minSdkVersion set in AndroidManifest.xml. Useful when creating multiple APKs based on SDK version
-ext.cdvMinSdkVersion=16
-
-  // Overrides the automatically detected android.compileSdkVersion value
-ext.cdvCompileSdkVersion=29
-
-  // Overrides the automatically detected android.buildToolsVersion value
-ext.cdvBuildToolsVersion='29.0.2'
diff --git a/resources/android/build/app/src/main/java/fr/duniter/cesium/MainActivity.java b/resources/android/build/app/src/main/java/fr/duniter/cesium/MainActivity.java
deleted file mode 100644
index 3287e5747..000000000
--- a/resources/android/build/app/src/main/java/fr/duniter/cesium/MainActivity.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
- */
-
-package fr.duniter.cesium;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import org.apache.cordova.*;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-
-import java.net.URLEncoder;
-import java.util.Locale;
-
-public class MainActivity extends CordovaActivity
-{
-    @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-
-        // enable Cordova apps to be started in the background
-        Bundle extras = getIntent().getExtras();
-        if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
-            moveTaskToBack(true);
-        }
-
-        // Set by <content src="index.html" /> in config.xml
-        loadUrl(launchUrl);
-    }
-
-  @Override
-  protected void onNewIntent(Intent intent) {
-    super.onNewIntent(intent);
-
-    String action = intent.getAction();
-    if ("android.intent.action.VIEW".equals(action)) {
-      Uri data = intent.getData();
-      loadFromUri(data);
-      setResult(Activity.RESULT_OK);
-    }
-  }
-
-  protected void loadFromUri(Uri uri) {
-    List<String> pathSegments;
-    final String scheme = uri.getScheme();
-    if (scheme == null) return; // Skip if no scheme
-
-    if ("http".equals(scheme) || "https".equals(scheme)) {
-      pathSegments = uri.getPathSegments();
-    } else if ("web+june".equals(scheme) || "june".equals(scheme)) {
-      pathSegments = new ArrayList<String>();
-      // Use the host as first path segment, if any
-      if (uri.getHost() != null) {
-        pathSegments.add(uri.getHost());
-      }
-      // Or use
-      else if (uri.getEncodedSchemeSpecificPart() != null) {
-        pathSegments.add(uri.getEncodedSchemeSpecificPart());
-      }
-      if (uri.getPathSegments() != null) pathSegments.addAll(uri.getPathSegments());
-    } else {
-      return; // Skip
-    }
-
-    if (pathSegments.size() == 0) return; // Skip
-
-    // Create the URI expected by Cesium
-    String fixedUri = "june://" + join(pathSegments, "/");
-    if (uri.getQuery() != null) {
-      fixedUri += uri.getQuery();
-    }
-    String url = getLaunchUrlNoHash() + "#/app/home?uri=" + URLEncoder.encode(fixedUri);
-
-    if (appView == null) {
-      init();
-    }
-    runOnUiThread(new Runnable() {
-      public void run() {
-        MainActivity.this.appView.loadUrlIntoView(url, false);
-      }
-    });
-  }
-
-  protected String getLaunchUrlNoHash() {
-    String url = "http://localhost/";//this.launchUrl;
-    // Remove hash path
-    int hashIndex = url.indexOf('#');
-    if (hashIndex != -1) {
-      url = url.substring(0, hashIndex);
-    }
-    return url;
-  }
-
-
-  protected String join(List<String> items, String separator) {
-      StringBuilder sb = new StringBuilder();
-      for (int i = 0; i<items.size(); i++) {
-        sb.append(items.get(i));
-        sb.append(separator);
-      }
-      // Remove last separator
-      sb.setLength(sb.length() - separator.length());
-
-      return sb.toString();
-  }
-}
diff --git a/www/js/controllers/home-controllers.js b/www/js/controllers/home-controllers.js
index 8c0bd8980..9fd026656 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, BMA,
+function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $http, $q, UIUtils, BMA,
                         csConfig, csCache, csPlatform, csCurrency, csSettings, csHttp) {
   'ngInject';
 
@@ -46,21 +46,17 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
     }
 
     if (state && state.stateParams && state.stateParams.uri) {
-      return $scope.redirectFromUri(state.stateParams.uri);
+
+      return csPlatform.uri.open(state.stateParams.uri)
+        .then(function() {
+          $scope.loading = false;
+        });
     }
     else if (state && state.stateParams && state.stateParams.error) { // Error query parameter
       $scope.error = state.stateParams.error;
       $scope.node = csCurrency.data.node;
       $scope.loading = false;
-      $ionicHistory.nextViewOptions({
-        disableAnimate: true,
-        disableBack: true,
-        historyRoot: true
-      });
-      $state.go('app.home', {error: undefined}, {
-        reload: false,
-        inherit: true,
-        notify: false});
+      $scope.cleanLocationHref(state);
     }
     else {
       // Wait platform to be ready
@@ -176,69 +172,24 @@ function HomeController($scope, $state, $timeout, $ionicHistory, $translate, $ht
     }
   };
 
-  /**
-   * Parse an URI (see g1lien)
-   * @param uri
-   * @returns {*}
-   */
-  $scope.redirectFromUri = function(uri) {
-    console.debug("[home] Detecting external uri: ", uri);
-    var parts = csHttp.uri.parse(uri);
-
-    var state,stateParams;
-
-    if (parts.protocol === 'g1:' || parts.protocol === 'june:') {
-      console.debug("[home] Applying uri...", parts);
-
-      // 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]};
-      }
-
-      // 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)) {
-        state = 'app.wot_lookup.tab_search';
-        stateParams = {q: parts.hostname};
-      }
-    }
-
-    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);
-    }
+  // remove '?uri&error' from the location URI, and inside history
+  $scope.cleanLocationHref = function(state) {
+    if (state && state.stateParams) {
+      var stateParams = angular.copy(state.stateParams);
+      delete stateParams.uri;
+      delete stateParams.error;
 
-    // Open the state
-    if (state) {
-      $ionicHistory.clearHistory();
+      // Update location href
       $ionicHistory.nextViewOptions({
-        disableAnimate: false,
-        disableBack: true,
-        historyRoot: true
-      });
-      return $state.go(state, stateParams, {
-        reload: true,
-        inherit: false,
-        notify: true
+        disableAnimate: true,
+        disableBack: false,
+        historyRoot: false
       });
-    }
-    else {
-      console.error("[home] Unknown URI format: " + uri);
-      this.loading = false;
-      return UIUtils.alert.error("ERROR.UNKNOWN_URI_FORMAT", uri);
+      return $state.go(state.stateName, stateParams, {
+          reload: false,
+          inherit: true,
+          notify: false
+        });
     }
   };
 
diff --git a/www/js/platform.js b/www/js/platform.js
index b0fffc3e6..24be8e508 100644
--- a/www/js/platform.js
+++ b/www/js/platform.js
@@ -100,7 +100,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
   })
 
 
-  .factory('csPlatform', function (ionicReady, $rootScope, $q, $state, $translate, $timeout, UIUtils,
+  .factory('csPlatform', function (ionicReady, $rootScope, $q, $state, $translate, $timeout, $ionicHistory, UIUtils,
                                    BMA, Device, csHttp, csConfig, csCache, csSettings, csCurrency, csWallet) {
 
     'ngInject';
@@ -113,8 +113,8 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       removeChangeStateListener;
 
     // Fix csConfig values
-    csConfig.demo = csConfig.demo === true || csConfig.demo === 'true' || false;
-    csConfig.readonly = csConfig.readonly === true || csConfig.readonly === 'true' || false;
+    csConfig.demo = csConfig.demo === true || csConfig.demo === 'true' || false;
+    csConfig.readonly = csConfig.readonly === true || csConfig.readonly === 'true' || false;
 
     function disableChangeState() {
       if (removeChangeStateListener) return; // make sure to call this once
@@ -123,11 +123,10 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
         if (!event.defaultPrevented && next.name !== 'app.home' && next.name !== 'app.settings') {
           event.preventDefault();
           if (startPromise) {
-            startPromise.then(function() {
+            startPromise.then(function () {
               $state.go(next.name, nextParams);
             });
-          }
-          else {
+          } else {
             UIUtils.loading.hide();
           }
         }
@@ -162,12 +161,12 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       }
 
       // Try to get summary
-      return csHttp.get(fallbackNode.host, fallbackNode.port, '/node/summary', fallbackNode.port==443 || BMA.node.forceUseSsl)()
-        .catch(function(err) {
+      return csHttp.get(fallbackNode.host, fallbackNode.port, '/node/summary', fallbackNode.port == 443 || BMA.node.forceUseSsl)()
+        .catch(function (err) {
           console.error('[platform] Could not reach fallback node [{0}]: skipping'.format(newServer));
           // silent, but return no result (will loop to the next fallback node)
         })
-        .then(function(res) {
+        .then(function (res) {
           if (!res) return checkBmaNodeAlive(); // Loop
 
           // Force to show port/ssl, if this is the only difference
@@ -175,14 +174,13 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
           if (messageParam.old === messageParam.new) {
             if (BMA.port != fallbackNode.port) {
               messageParam.new += ':' + fallbackNode.port;
-            }
-            else if (BMA.useSsl == false && (fallbackNode.useSsl || fallbackNode.port==443)) {
+            } else if (BMA.useSsl == false && (fallbackNode.useSsl || fallbackNode.port == 443)) {
               messageParam.new += ' (SSL)';
             }
           }
 
           return $translate('CONFIRM.USE_FALLBACK_NODE', messageParam)
-            .then(function(msg) {
+            .then(function (msg) {
               return UIUtils.alert.confirm(msg);
             })
             .then(function (confirm) {
@@ -220,7 +218,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
               };
             }
           })
-          .catch(function(err) {
+          .catch(function (err) {
             // silent (just log it)
             console.error('[platform] Failed to get Cesium latest version', err);
           })
@@ -229,7 +227,71 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       return $q.when();
     }
 
-    function registerProtocols() {
+    /**
+     * Parse an external URI (see g1lien), and open the expected state
+     * @param uri
+     * @returns {*}
+     */
+    function openUri(uri) {
+      if (!uri) return $q.when(); // Skip
+
+      console.info('[platform] Detecting external uri: ', uri);
+
+      var parts = csHttp.uri.parse(uri),
+        state, stateParams;
+
+      // G1 lien
+      if (parts.protocol === 'june:') {
+        console.debug("[home] Applying uri...", parts);
+
+        // 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]};
+        }
+
+        // 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)) {
+          state = 'app.wot_lookup.tab_search';
+          stateParams = {q: parts.hostname};
+        }
+      }
+
+      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);
+      }
+
+      if (state) {
+        // Open the state, after cleaning current location URI
+        return $state.go(state, stateParams, {
+          reload: true
+        })
+          .then(function () {
+            // This is need to make back button working again
+            return $timeout(function () {
+              if ($ionicHistory.backView()) $ionicHistory.removeBackView();
+            }, 400);
+          });
+      } else {
+        console.error("[home] Unknown URI format: " + uri);
+        return UIUtils.alert.error("ERROR.UNKNOWN_URI_FORMAT", uri);
+      }
+    }
+
+    function registerProtocolHandlers() {
       var protocols = ['web+june'/*, 'web+g1', 'g1'*/];
 
       _.each(protocols, function(protocol) {
@@ -246,7 +308,10 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
     function addListeners() {
       listeners = [
         // Listen if node changed
-        BMA.api.node.on.restart($rootScope, restart, this)
+        BMA.api.node.on.restart($rootScope, restart, this),
+
+        // Listen for new intent
+        Device.api.intent.on.new($rootScope, handleOpenUri, this)
       ];
     }
 
@@ -275,7 +340,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       // Avoid change state
       disableChangeState();
 
-      registerProtocols();
+      registerProtocolHandlers();
 
       // We use 'ionicReady()' instead of '$ionicPlatform.ready()', because this one is callable many times
       startPromise = ionicReady()
@@ -302,6 +367,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
         .then(function(){
           enableChangeState();
           addListeners();
+          processLaunchUri();
           startPromise = null;
           started = true;
         })
@@ -332,6 +398,20 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       }, 500);
     }
 
+    /**
+     * Process launch intent, as it could have been triggered BEFORE addListeners()
+     * @returns {*}
+     */
+    function processLaunchUri() {
+      return Device.intent.last()
+        .then(function(intent) {
+          if (intent) {
+            Device.intent.clear();
+            return openUri(intent);
+          }
+        });
+    }
+
     return  {
       disableChangeState: disableChangeState,
       isStarted: isStarted,
@@ -341,6 +421,9 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       stop: stop,
       version: {
         latest: getLatestRelease
+      },
+      uri: {
+        open: openUri
       }
     };
   })
@@ -423,6 +506,7 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
         if ($ionicHistory.backView()) {
           return $ionicHistory.goBack();
         }
+
         event.preventDefault();
         return UIUtils.alert.confirm('CONFIRM.EXIT_APP')
           .then(function (confirm) {
diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js
index 478842435..5479c3b19 100644
--- a/www/js/services/device-services.js
+++ b/www/js/services/device-services.js
@@ -2,7 +2,7 @@ var App;
 
 angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.settings.services'])
 
-  .factory('Device', function($rootScope, $translate, $ionicPopup, $q,
+  .factory('Device', function($rootScope, $translate, $ionicPopup, $q, Api,
       // removeIf(no-device)
       $cordovaClipboard, $cordovaBarcodeScanner, $cordovaCamera,
       // endRemoveIf(no-device)
@@ -14,6 +14,8 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
           MAX_HEIGHT: 400,
           MAX_WIDTH: 400
         },
+        that = this,
+        api = new Api(this, "Device"),
         exports = {
           // workaround to quickly no is device or not (even before the ready() event)
           enable: true
@@ -143,6 +145,35 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
         }
       };
 
+      function getLastIntent() {
+        var deferred = $q.defer();
+        window.plugins.launchmyapp.getLastIntent(
+          deferred.resolve,
+          deferred.reject);
+        return deferred.promise;
+      }
+
+      function handleOpenUri(intent) {
+        if (intent) {
+          console.info('[platform] Received new intent: ', intent);
+          cache.lastIntent = intent; // Remember, for last()
+          api.intent.raise.new(intent);
+        }
+      }
+
+      exports.intent = {
+        enable: false,
+        last: function() {
+          return $q.when(cache.lastIntent);
+        },
+        handle: handleOpenUri,
+        clear: function() {
+          cache.lastIntent = undefined;
+        }
+      };
+
+      window.handleOpenURL = handleOpenUri; // Need by cordova-plugin-customurlscheme
+
       // Numerical keyboard - fix #30
       exports.keyboard.digit = {
         settings: {
@@ -242,16 +273,18 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
               exports.keyboard.enable = cordova && cordova.plugins && !!cordova.plugins.Keyboard;
               exports.barcode.enable = cordova && cordova.plugins && !!cordova.plugins.barcodeScanner && !exports.isOSX();
               exports.clipboard.enable = cordova && cordova.plugins && !!cordova.plugins.clipboard;
+              exports.intent.enable = window && !!window.plugins.launchmyapp;
 
               if (exports.keyboard.enable) {
                 angular.extend(exports.keyboard, cordova.plugins.Keyboard);
               }
 
-              console.debug('[device] Ionic platform ready, with [camera: {0}] [barcode scanner: {1}] [keyboard: {2}] [clipboard: {3}]'
-                .format(exports.camera.enable, exports.barcode.enable, exports.keyboard.enable, exports.clipboard.enable));
+              console.debug('[device] Ionic platform ready, with {camera: {0}, barcode: {1}, keyboard: {2}, clipboard: {3}, intent: {4}}'
+                .format(exports.camera.enable, exports.barcode.enable, exports.keyboard.enable, exports.clipboard.enable, exports.intent.enable));
 
               if (cordova.InAppBrowser) {
                 console.debug('[device] Enabling InAppBrowser');
+                window.open = cordova.InAppBrowser.open;
               }
             }
             else {
@@ -265,6 +298,11 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
         return startPromise;
       };
 
+      api.registerEvent('intent', 'new');
+
+      // Export the event api (see ngApi)
+      exports.api = api;
+
       return exports;
     })
 
diff --git a/www/js/services/http-services.js b/www/js/services/http-services.js
index dc6f6b173..0d4e08b39 100644
--- a/www/js/services/http-services.js
+++ b/www/js/services/http-services.js
@@ -320,9 +320,9 @@ angular.module('cesium.http.services', ['cesium.cache.services'])
     var protocol, hostname;
 
     // G1 URI (see G1lien)
-    if (uri.startsWith('g1:') || uri.startsWith('june:') || uri.startsWith('web+june:')) {
+    if (uri.startsWith('june:') || uri.startsWith('web+june:')) {
       protocol = 'june:';
-      var path = uri.replace(/^(g1|web\+june|june):(\/\/)?/, '');
+      var path = uri.replace(/^(web\+june|june):(\/\/)?/, '');
 
       // Store hostname here, because parse will apply a lowercase
       hostname = path;
-- 
GitLab