web-dev-qa-db-fra.com

Comment revenir sur le marché lorsque Android schéma d'URL personnalisé non géré?

Nous avons une application qui gère un schéma d'URL personnalisé (vstream: //). Lorsqu'une personne accède à une page Web contenant du contenu vstream: //, nous devons la rediriger vers le magasin si notre application n'est pas installée.

Sous iOS, nous faisons ceci:

setTimeout(function() {
  window.location =
    "itms://iTunes.Apple.com/us/app/kaon-v-stream/id378890806?mt=8&uo=4";
}, 25);

window.location = "vstream:view?code=...stuff...";

Si la window.location L'affectation échoue, le délai d'attente saute sur l'App Store avant que la boîte de dialogue ne s'affiche. (J'ai trouvé cette technique ici: Est-il possible d'enregistrer un schéma d'URL basé sur le domaine http + pour les applications iPhone, comme YouTube et Maps? .)

Malheureusement, cette astuce ne fonctionne pas sous Android. Nous détectons le côté serveur de l'appareil et écrivons ceci au lieu de la ligne itms:

"market://details?id=com.kaon.Android.vstream";

Le problème est que, alors qu'iOS génère une erreur lorsque vous accédez à un schéma d'URL non géré, Android va à une page générée. Par conséquent, le délai d'expiration n'a jamais une chance de s'exécuter.

Existe-t-il un moyen sur une page Web de tester explicitement si un schéma d'URL personnalisé est géré, ou quelqu'un peut-il suggérer un hack comme celui-ci qui fonctionnera sous Android? (Bien sûr, je suppose que j'ai besoin d'un hack qui fonctionnera quel que soit le navigateur qu'ils utilisent, ce qui est probablement un défi de taille ...)

MISE À JOUR: Les approches ci-dessous ne fonctionnent pas dans Jelly bean sur un Nexus 7. Le nouveau Chrome navigateur ne va pas vers une page générée (donc l'iFrame n'est pas nécessaire), mais il n'apparaît pas pour savoir si le schéma d'URL a été géré. Si tel était le cas, le délai d'expiration se déclenche quand même. S'il n'a pas été géré, le délai d'expiration se déclenche. Si j'utilise un gestionnaire onload et un iframe, le gestionnaire onload ne se déclenche jamais (si le l'application est installée ou non). Je mettrai à jour si jamais je trouve comment savoir si le schéma a été géré ...

J'ai supprimé mon "Résolu" sur ma solution précédente, car il ne fonctionne plus.

MISE À JOUR 2: J'ai maintenant une bonne solution multiplateforme qui fonctionne sur iOS, Android 4.1 avec Chrome et Android pre-Chrome. Voir ci-dessous ..) .

Mise à jour 3: Google a tout cassé à nouveau avec des intentions. Découvrez la très belle solution que j'ai acceptée par amit_saxena là-bas quelque part /

25
Joshua Smith

Vous trouverez ci-dessous un extrait de code fonctionnel pour la plupart des navigateurs Android):

<script type="text/javascript">
    var custom = "myapp://custom_url";
    var alt = "http://mywebsite.com/alternate/content";
    var g_intent = "intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.Android;end";
    var timer;
    var heartbeat;
    var iframe_timer;

    function clearTimers() {
        clearTimeout(timer);
        clearTimeout(heartbeat);
        clearTimeout(iframe_timer);
    }

    function intervalHeartbeat() {
        if (document.webkitHidden || document.hidden) {
            clearTimers();
        }
    }

    function tryIframeApproach() {
        var iframe = document.createElement("iframe");
        iframe.style.border = "none";
        iframe.style.width = "1px";
        iframe.style.height = "1px";
        iframe.onload = function () {
            document.location = alt;
        };
        iframe.src = custom;
        document.body.appendChild(iframe);
    }

    function tryWebkitApproach() {
        document.location = custom;
        timer = setTimeout(function () {
            document.location = alt;
        }, 2500);
    }

    function useIntent() {
        document.location = g_intent;
    }

    function launch_app_or_alt_url(el) {
        heartbeat = setInterval(intervalHeartbeat, 200);
        if (navigator.userAgent.match(/Chrome/)) {
            useIntent();
        } else if (navigator.userAgent.match(/Firefox/)) {
            tryWebkitApproach();
            iframe_timer = setTimeout(function () {
                tryIframeApproach();
            }, 1500);
        } else {
            tryIframeApproach();
        }
    }

    $(".source_url").click(function (event) {
        launch_app_or_alt_url($(this));
        event.preventDefault();
    });
</script>

Vous devez ajouter source_url class à la balise d'ancrage.

J'en ai blogué plus ici:

http://aawaara.com/post/88310470252/smallest-piece-of-code-thats-going-to-change-the

15
amit_saxena

MISE À JOUR: Google a cassé cela. Consultez plutôt la nouvelle réponse acceptée.

Il s'avère que la clé est la propriété document.webkitHidden. Lorsque vous définissez window.location sur un schéma d'URL personnalisé et qu'il s'ouvre, le navigateur continue de fonctionner, mais cette propriété devient false. Vous pouvez donc le tester pour déterminer si le schéma d'URL personnalisé a été géré.

Voici un exemple, que vous pouvez voir en direct

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Starting App...</title>
<script>

var URL = "kaonkaon://product.html#malvern;6";
var MARKET = "market://details?id=com.kaon.Android.lepton.kaon3d";
var iTunes = "itms://iTunes.Apple.com/us/app/kaon-interactive-3d-product/id525051513?mt=8&uo=4";
var QR = "http://goo.gl/gz07g"; // this should be a shortened link back to this page

function onLoad() {

    if (navigator.userAgent.match(/Android/)) {

        if (navigator.userAgent.match(/Chrome/)) {

            // Jelly bean with Chrome browser
            setTimeout(function() {
                if (!document.webkitHidden)
                    window.location = MARKET;
            }, 1000);

            window.location = URL;

        } else {

            // Older Android browser
            var iframe = document.createElement("iframe");
            iframe.style.border = "none";
            iframe.style.width = "1px";
            iframe.style.height = "1px";
            var t = setTimeout(function() {
                window.location = MARKET;
            }, 1000);
            iframe.onload = function () { clearTimeout(t) };
            iframe.src = URL;
            document.body.appendChild(iframe);

        }

     } else if (navigator.userAgent.match(/iPhone|iPad|iPod/)) {

         // IOS
         setTimeout(function() {
             if (!document.webkitHidden)
                 window.location = iTunes;
         }, 25);

         window.location = URL;

     } else {

         // Not mobile
         var img = document.createElement("img");
         img.src = "https://chart.googleapis.com/chart?chs=300x300&cht=qr&chl="+encodeURIComponent(QR);
         document.body.appendChild(img);
     }
}
</script>
  </head>
  <body onload="onLoad()">
  </body>
</html>
18
Joshua Smith

C'est la réponse qui vous sauvera tous!

https://developers.google.com/chrome/mobile/docs/intents

<a href="intent://scan/#Intent;scheme=zxing;package=com.google.zxing.client.Android;end"> Take a QR code </a>

Votre URL me lancera si l'application est installée ou dans le cas contraire, elle lancera le marché au package indiqué

14
Christopher-BZC

@jesmith, c'est une version propre qui corrige la double action sur Android.

if (navigator.appVersion.indexOf('iPhone') > -1) {
  setTimeout(function noapp() { window.location="http://iTunes.Apple.com/app/id378890806?mt=8"; }, 25);
  window.location = 'vstream:';
}
else if (navigator.userAgent.indexOf('Android') > -1) {
  var iframe = document.createElement('iframe');
  iframe.style.visibility = 'hidden';
  iframe.src = 'vstream:';
  iframe.onload = function noapp() { window.location="market://details?id=com.kaon.Android.vstream"; };
  document.body.appendChild(iframe);
}
6
vmus

Résolu! L'astuce consiste à ouvrir mon application dans un IFRAME, au lieu de définir l'emplacement:

setTimeout(function() {
  window.location =
    "market://details?id=com.kaon.Android.vstream";
}, 1000);

document.write('<iframe style="border:none; width:1px; height:1px;" src="vstream:view?code='+code+'"></iframe>');

Notez que j'ai augmenté le délai d'expiration à 1000, car Android effectue en fait les deux actions dans tous les cas (pas idéal, mais pas terrible), et ce délai d'expiration plus long est nécessaire pour s'assurer que Market ne le fait pas. finissent par être ce que l'utilisateur voit lorsque je suis déjà installé.

(Et oui, bien sûr, utiliser document.write est tellement du siècle dernier, mais je suis de la vieille école comme ça :)

4
Joshua Smith

Pour certaines raisons, la solution finale ne fonctionne pas pour moi sur Android (Est-ce juste moi? !!). La clé est que la fonction iframe.onload n'est PAS exécutée lorsque votre application est installée, et il IS exécuté lorsque votre application n'est PAS installée.

La solution devient en fait un peu plus simple. Voici le segment de la partie "Older Android Browser"):

    } else {

        // Older Android browser
        var iframe = document.createElement("iframe");
        iframe.style.border = "none";
        iframe.style.width = "1px";
        iframe.style.height = "1px";
        iframe.onload = function () { window.location = MARKET; };
        iframe.src = URL;
        document.body.appendChild(iframe);

    }
2
Jun Sun

Intégrez un serveur http dans votre application en tant que service, écoutez un port local (supérieur à 1024, ex: 8080), envoyez une requête du navigateur à 127.0.0.1:8080. Si votre application est installée (et que le service est en cours d'exécution), démarrez-la, si la demande échoue, accédez à Google Play.

1
javamonk