We are trying to build an parameterized entry webpage for our app in which about 50% of users will have our app installed, 50% not.
Originally, we had been using a specialized URL scheme that our app had registered; ie,
myapp://dothing?specialParameters=5
However, we have difficulty detecting cases where users either don't have our app installed, or have an earlier version of our app that doesn't support URL schemes. In Chrome, or the Android Browser, the user is navigated to a browser-generated Error page since it couldn't locate the server.
In iOS Safari, and Android Firefox, we can resolve this through the use of a setTimeout before navigation;
function timeoutFn() {
var timeout = setTimeout(function() {
window.location = //iOS Appstore location;
}, 1000);
window.addEventListener("pagehide", function(evt) {
clearTimeout(timeout);
});
window.location = "myApp://dothing?specialParameters=5";
}
My solution for Chrome/Android Browser is to use Google's recommended system of Intents, described here:
https://developers.google.com/chrome/mobile/docs/intents
I've tested this out, and it actually seems to work well - however, Firefox does not know how to handle links beginning with "intent://". We can still use the above JavaScript function for Firefox, but I'm very reluctant to start writing user-agent-sniffing code for it, especially since I'd think it likely any other web browsers on Android will similarly lack Intent support.
So, back to the title question: Chrome, and the Android browser, are capable of opening apps through "intent://" links - is there any way, using JavaScript or similar methods, to use feature detection to find this support?
This is what we are currently using, however, it's far from ideal ...
function isItentsSupported() {
var isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;
if (isChrome) {
var version = parseInt(window.navigator.appVersion.match(/Chrome\/(\d+)\./)[1], 10);
return version >= 25;
} else {
return false;
}
}
Chrome version number based on Google documentation
Related
I have a PWA where Android users are prompted to Add the PWA to their home screen, but I would also like to show a tooltip for Safari users on iOS showing how to add the PWA to their Home screen since it's not a supported feature for them.
I was thinking about using feature detection but I can't find a way to do it, something like:
if ("a2hs" in navigator) {
// Do something for browsers supporting A2HS feature
} else {
// Show the tooltip if the browser does not support A2HS
}
I also thought I could use something like this:
const isIos = () => {
return window.navigator.vendor === 'Apple Computer, Inc.';
};
but this is also returning true for users of Chrome on iOS :(
Any idea how I can detect if a user is using Safari on iOS?
Note: I also want to avoid using user agent sniffing. I think I read almost all topics related to this subject on SO but so far I couldn't find any solution not using user agent sniffing.
a partial solution:
if('BeforeInstallPromptEvent' in window)
alert('Chrome-style PWA install experience supported!');
if('standalone' in navigator) // only available on iOS - but also on alternative browsers on iOS without Add-to-Homescreen support
alert('iOS Safari-style PWA Add-to-Homescreen maybe supported!');
Open problems:
avoid false positives on iOS - if you could accept using the user agent for that, you could use navigator.userAgent.match(/(iOS|OPT|Brave|GSA|DuckDuckGo|Snapchat)\//) to detect most alternative browsers; For explanation: iOS in the regex should detect CriOS|FxiOS|EdgiOS|OPiOS (Chrome/Firefox/Edge/Opera mini); Does anbody have a way to detect alternative browsers on iOS without using the user agent?
it does not detect Firefox on Android; and I don't know if other browsers based on Firefox for Android support PWAs (e.g. QuantMobile, PrivacyWall, Fennec, KAIOS - all tokens that appear together with Firefox and Android in the user agent string)
Addition (untested & unreliable):
I did some more research:
A sure sign that you are in a webview on iOS is (or was?) that navigator.mediaDevices.getUserMedia or maybe navigator.mediaDevices is undefined. But I also read that Apple implemented it some months ago - so there will be webviews for which this isn't true.
Another hint might that if navigator.doNotTrack is undefined, it could mean that you are in a webview (or that Apple decided to remove this deprecated property from Safari...)
We created extensions for Chrome, Firefox and Safari. We want to open a new tab when uninstalling our extension. I found this code for Chrome, but is it possible to do it in Safari and Firefox?
switch (platform) {
case 'chrome':
try {
chrome.runtime.setUninstallURL(uninstall_url);
} catch(e) {
console.error(e);
}
break;
}
In place of manifest.json Safari extensions use Info.plist which is typically generated with ExtensionBuilder. As you can see from the provided screenshot, no 'uninstallURL' is available. I suggest testing a beforeUnload listener in the global page but this will probably also be triggered during other events like updates, browser closing etc.
As for Firefox, it all depends on the actual API your add-on is based on. You maybe out of luck if you have used the Addon SDK or created a legacy XUL overlay extension. However, Restartless bootstrapped extensions use low-level APIs and the uninstall function in bootstrap.js receives a reason parameter:
function uninstall(data, reason) {
if (reason === ADDON_UNINSTALL) {
let win = Services.wm.getMostRecentWindow('navigator:browser');
win.gBrowser.selectedTab = win.gBrowser.addTab(url);
}
}
Good news is a new WebExtensions API is coming to Firefox. It is basically identical to the Chrome API. See the relevant bug
From addon sdk you would use this https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Listening_for_load_and_unload
That shows you how to listen for load and unload and detect the reason for load/unload solution is from here - https://stackoverflow.com/a/31497334/1828637
I have a webpage, lets call it entry.html.
When a user enters this page, a javascript code (see below) is attempting to deep-link the user to the native iOS / Android app.
If the deep-link fails (probably if the app isn't installed on device), user should "fall back" to another page- lets call it fallback.html.
here is the javascript code that is running on entry.html:
$(function(){
window.location = 'myapp://';
setTimeout(function(){
window.location = 'fallback.html';
}, 500);
});
this is a standard deep-linking method that is recommended all over the network; try to deep-link, and if the timeout fires it means that deep-link didn't occur- so fallback.
this works fine, as long app is installed on device.
but if the app isn't installed, this is the behaviour when trying to deep-link:
Mobile Safari: I see an alert message saying "Safari cannot open this page..." for a moment, and then it falls-back properly to fallback.html- which is the expected behaviour.
Mobile Chrome is my problem.
when the app isn't installed, browser is actually redirected to the myapp:// url, which is of course, invalid- so i get a "not found" page, and fall-back doesn't occur.
Finally- my question is:
How can I fix my code so FALL-BACK WILL OCCUR on mobile Chrome as well? just like mobile Safari?
note: i see that LinkedIn mobile website does this properly, with Safari & Chrome, with or without the app installed, but i couldn't trace the code responsible for it :(
note2: i tried appending an iframe instead of window.location = url, this works only on Safari, mobile Chrome doesn't deep-link when appending an iFrame even if app is installed.
Thanks all!
UPDATE:
i found a decent solution, and answered my own question. see accepted answer for my solution.
for whoever is interested, i managed to find a decent solution to solve these issues with deeplinking Chrome on Android.
i abandoned the myapp:// approach, i left it functioning only in cases of an iOS device.
for Android devices, i'm now using intents which are conceptually different than the myapp:// protocol.
I'm mainly a web developer, not an Android developer, so it took me some time to understand the concept, but it's quite simple. i'll try to explain and demonstrate MY solution here (note that there are other approaches that could be implemented with intents, but this one worked for me perfectly).
here is the relevant part in the Android app manifest, registering the intent rules (note the android:scheme="http" - we'll talk about it shortly):
<receiver android:name=".DeepLinkReceiver">
<intent-filter >
<data android:scheme="http" android:host="www.myapp.com" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
now, after this is declared in the app manifest, i'm sending myself an email with "http://www.myapp.com" in the message.
when link is tapped with the Android device, a "chooser" dialog comes up, asking with which application i want to open the following? [chrome, myapp]
the reason this dialog came up upon tapping on a "regular" url, is because we registered the intent with the http scheme.
with this approach, the deeplink isn't even handled in the webpage, it's handled by the device itself, when tapping a matching link to an existing intent rule defined in the Android app manifest.
and yes, as i said, this approach is different by concept than the iOS approach, which invokes the deeplink from within the webpage, but it solves the problem, and it does the magic.
Note: when app isn't installed, no chooser dialog will come up, you'll just get navigated to the actual web page with the given address (unless you have more than 1 browser, so you'll need to choose one... but lets not be petty).
i really hope that this could help someone who's facing the same thing.. wish i had such an explanation ;-)
cheers.
It is very important to make sure that when you try to open a deeplink URL with JavaScript that the URL is properly formatted for the device and browser. (If you do not use the appropriate deeplink URL for the browser/platform, a user may be redirected to a “Page Not Found”, which is what you experience.)
Now you must note that Chrome on Android has a different URL format than the old standard Android browser 1! You need to annotate the deep links using href="android-app://" in the HTML markup of your web pages. You can do this in the section for each web page by adding a tag and specifying the deep link as an alternate URI.
For example, the following HTML snippet shows how you might specify the corresponding deep link in a web page that has the URL example://gizmos.
<html>
<head>
<link rel="alternate"
href="android-app://com.example.android/example/gizmos" />
...
</head>
<body> ... </body>
For more details, see the references here:
https://developer.chrome.com/multidevice/android/intents
https://developers.google.com/app-indexing/webmasters/server
https://developer.android.com/training/app-indexing/enabling-app-indexing.html#webpages
And here's a deep link testing tool for Android: https://developers.google.com/app-indexing/webmasters/test.html
Hope that helps.
1 Since the old AOSP browser was replaced by chromium, this is now the default way to handle deep links for recent Android versions. Nonetheless, Android still requires a conditional soltion, because older OS versions still use the AOSP browser.
I have created a Javascript plugin, which supports most of the modern browsers on mobile. But it requires to have deep linking landing pages to be hosted on cross domain(different than universal link url) to work on ios9 Facebook using universal linking. There is also different way to get that working on the Facebook iOS9 using Facebook SDK. I am sharing this if anyone might find this helpful. Currently it does not fallback option, but if falls back to the App Store.
https://github.com/prabeengiri/DeepLinkingToNativeApp
I am Using this Code to for deeplinking.
If the app is installed the app will open up..
If the app is not installed then this remains as it is..
If you wish to add any other condition for app no install then just uncomment the setTimeout code .
<script>
var deeplinking_url = scootsy://vendor/1;
$(document).ready(function(){
call_me_new(deeplinking_url);
});
var call_me_new = function(deeplinking_url){
if(deeplinking_url!=''){
var fallbackUrl ='http://scootsy.com/';
var iframe = document.createElement("iframe");
var nativeSchemaUrl = deeplinking_url;
console.log(nativeSchemaUrl);
iframe.id = "app_call_frame";
iframe.style.border = "none";
iframe.style.width = "1px";
iframe.style.height = "1px";
iframe.onload = function () {
document.location = nativeSchemaUrl;
};
iframe.src = nativeSchemaUrl; //iOS app schema url
window.onload = function(){
document.body.appendChild(iframe);
}
//IF the App is not install then it will remain on the same page.If you wish to send the use to other page then uncomment the below code and send a time interval for the redirect.
/*
setTimeout(function(){
console.log('Iframe Removed...');
document.getElementById("app_call_frame").remove();
window.location = fallbackUrl; //fallback url
},5000);*/
}
};
</script>
setTimeout(function () { if (document.hasFocus()) { window.location = 'URL WILL BEHERE';} }, 2000);
window.location = 'app://';
Need to check document.hasFocus() here because if app is open then playstore url is also open in browser
I also had similar issue, there is a possible alternative for this. If the app is not installed on user's device we can redirect that to some other url.To know more about it Check Here
Example:
Take a QR code
In my case its working fine in opera and chrome browser my deeplink url is
"intent://contentUrl + #Intent;scheme=" +envHost +;package="+envHost+";end";
For other browser create iframe and append the url.
Note -: iframe url append having issue with old device and in firefox its opening app dialog .
Im creating a website using MVC 4, and I have a need to within a javascript open an application, using an url-scheme (surfing the site using a mobile device) If the application is not installed, I need the javascript to redirect to google play.
I have looked and tried all various methods mentioned in threads like this:
How to fall back to marketplace when Android custom URL scheme not handled?
Many of those solutions relies on a timeout-event, which will redirect to another website if the app is not installed. The problem I am encountering is that after I have used the window.location = URL; in my javascript, any timeouts I have created seems to just dissapear. If i skip the window.location = URL; my timeouts fire just fine and redirects to google play or whatever. Does anyone understand why i am having this problem?
As I said im using MVC 4, and executes a javascript within a simple view. I have tried many different codes, opening iframes etc, all resulting in the same problem mentioned above.
I just tried opening the app, and delaying an ITunes redirect on a iPhone-device using Safari, it seems to work. Can the same be achieved with android/chrome?
I would appreciate any help I could get considering this.
Opening an app using iframe is no longer supported by Google Chrome (v25 onwards). You can refer to the example on Android Guidelines for opening the app (if installed) or redirecting to market.
I love the Skype solution:
var element = document.createElement('iframe');
element.id = '_the_frame_' = ((new Date()).getTime();
document.appendChild(element);
var r = document.getElementById(element.id);
var u = true;
window.addEventListener("pagehide", function () { u = false; }, false);
if (r !== null) {
r.src = _customSchemaUrl;
}
setTimeout(function() {
if (u) {
window.location = _downloadIosUrl;
}
}, 2000);
What is the most efficient and reliable method to detect if an app is running in phonegap, or simply inside of a mobile/desktop browser with JavaScript? I am attempting to eliminate any of the issues that prevent me from testing/debugging my phonegap apps in any browser (desktop or mobile), and create a truly universal code base for my apps.
I intend on structuring my functions with phonegap specific calls like so:
if (phonegapisrunning) {
// phonegap specific javascript calls here
}
else {
// standard javascript calls here
}
While searching for a solution I came across this thread:
PhoneGap: Detect if running on desktop browser.
While this thread discusses this issue, I do not see a clear answer to which method is the most efficient/reliable. Should I bind to the onDeviceReady() event? Should I check window.device? Is there a more efficient or reliable way to check if an app is running in phonegap via JavsScript?
And this thread which mentions the Ripple Chrome Plugin:
Phonegap web app in regular desktop browsers
The Ripple tools looks like it could be a valuable tool for testing. But I am trying to make my phonegap apps run in a desktop browser without a plugin.
If it is determined that the app is not running in phonegap, I would then use useragent sniffing to determine if browser is desktop or mobile, and further separate any code if needed.
I've seen many answers about checking the user agent. Though those are useful for comparing which platform on which a page was loaded, they still do not differentiate whether running within a cordova app's browser or within a regular web browser. After a whole bunch of digging in the android cordova javascript file, I found that the following variable is set when running in a cordova app:
window._cordovaNative
Looking through the ios cordova javascript, I found:
window._nativeReady
Throw these alerts in your page before you ever load any cordova javascripts or check any user agents, etc. and compare results between loading from a web browser and loading from a cordova app that gets dynamic content:
alert("Android: " + window._cordovaNative);
alert("iOS: " + window._nativeReady);
I guess the other devices' phonegap files have different global variables, but for now, this is going to work great for me -- I hope it works well for you!
My suggestion is to create/call your javascript functions outside of the onDeviceReady Phonegap call.
Or maybe check what version of Cordova / Phonegap is running e.g.:
var string = device.cordova; // or device.phonegap
if (string == null) {
//do non phonegappy stuff here
} else {
//do phonegappy stuff
}
While it may not be the cleanest solution, a simple and reliable method is to create/set a global variable on deviceready:
var isCordovaReady = true;
Then:
if (isCordovaReady) {
// do cordova/phonegap stuff
}
else {
// do non cordova/phonegap stuff here
}
I posted the top answer for: PhoneGap: Detect if running on desktop browser
Although this isn't heavily documented and somewhat controversial I've been able to use this chunk of code for all my projects:
if (navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|IEMobile)/)) {
document.addEventListener("deviceready", onDeviceReady, false); //phone
} else {
onDeviceReady(); //this is the browser
}
You can modify it a bit to work for your projects like so:
var phonegapisrunning = false;
if (navigator.userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry|IEMobile)/)) {
document.addEventListener("deviceready", onDeviceReady, false); //phone
//change to true
phonegapisrunning = true;
} else {
onDeviceReady(); //this is the browser
}
Hope this helps !