How to detect if web app running standalone on Chrome mobile - javascript

Chrome mobile has recently added the ability to add to home screen, similar to iOS. This is cool but it doesn't support it as well as iOS - it doesn't support window.navigator.standalone so you can't detect whether you are running as a standalone app.
The reference says:
How can I detect if the app is running as an installed app?
You can’t, directly.
Notice it says "directly". My question is can we do it indirectly? Is there some tricky way to make an educated guess?

This answer comes with a huge delay, but I post it here just for other people who are struggling to find a solution.
Recently Google has implemented the CSS conditional display-mode: standalone, so there are two possible ways to detect if an app is running standalone:
Using CSS:
#media all and (display-mode: standalone) {
/* Here goes the CSS rules that will only apply if app is running standalone */
}
Using both CSS and Javascript:
function isRunningStandalone() {
return (window.matchMedia('(display-mode: standalone)').matches);
}
...
if (isRunningStandalone()) {
/* This code will be executed if app is running standalone */
}
If you need more information, Google Developers has a page dedicated to this topic: https://developers.google.com/web/updates/2015/10/display-mode

iOS and Chrome WebApp behaves different, thats the reason i came to following:
isInWebAppiOS = (window.navigator.standalone === true);
isInWebAppChrome = (window.matchMedia('(display-mode: standalone)').matches);
Same as here: Detect if iOS is using webapp

For the IOS we have window.navigator.standalone property to check..
But for Chrome on Android, it doesn't support this property. Only way to check this is by calculating screen width and height.
Below is the code to check that:
navigator.standalone = navigator.standalone || (screen.height-document.documentElement.clientHeight<40)
I got reference from below link:
Home Screen Web Apps for Android Thanks to Chrome 31

An old question but significantly better solutions available today for Chrome Android.
One of the ways(cleanest IMO). You may add Web App Manifest with a 'start_url' key with a value that adds a query string parameter to your usual homepage.
ex:- if homepage url is https://www.example.com.
in Web App Manifest set
"start_url": "https://www.example.com/?user_mode=app"
Google's guide about Web app manifest:https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/

With IOS, localstorage for the standalone and web mode are different. With android KitKat and Chrome, when you save a value in localstorage on the web version, you're able to retrieve it in standalone mode.
So, you just have to save document.documentElement.clientHeight to localstorage when user is browsing the web version (before adding to homescreen). Then, just compare the current value of document.documentElement.clientHeight with localstorage value. If the current value is >, it's standalone mode.
I tried it on several devices.

For Google Chrome (Android) from version 39 onwards with web application manifest (json) and in case, it is a single page application, I use this 'trick':
In the manifest.json I put: "start_url": "standalone.html".
Normally it is (in my case index.html), but from index.html I make an identical clone: standalone.html (or whatever you fancy).
Then, to check, I use Javascript like this:
var locurl = document.location.href;
if (locurl.indexOf("standalone.html") != -1) {
console.log("app is running standalone");
} else {
console.log("app is running in normal browser mode");
}
That works.
It might work too in Google Chrome (mobile) version 31-38 with this meta-tag:
<meta name="application-url" content="http://my.domain.com/standalone.html">.
Not tested, yet.

There is no 'proper' way to do it on Android, hence no API support yet.
The workaround we used in my company -
On first login, store screenheight in localstorage.
By screenHeight i mean document.documentElement.clientHeight before page loads, since then doc grows and its not accurate measure for real available screen height.
Whenever user logs in next time - check current screenheight vs stored - if it becomes bigger, the user has gone fullscreen.
There is no scenario upon which it can become smaller, IF YOU DO store FIRST TIME value for screen height.
Caveat - user that will clean cash.
We chose to ignore that, since most users don't do it or do it relatively rarely, and it will suffice(in our opinion) for some time until there will be API fix for this( hopefully there will be :) )
Option b - check available screenheight vs device screen height, the matchup for a few test devices & browser modes showed some pattern( available height < 70 in webapp) - i strongly believe a wider comparison can get values that will suffice for 90% of devices in 90% cases.
All of this is a workaround, of course, i m not pretending its a solid stable way to get to desired functionality - but for us it worked, so i hope this helps somebody else who fought this bug(cant call missing such crucial api feature other way). Good luck :)

To detect if it's running on MIT AI2 webview:
if (typeof window.AppInventor!="undefined") { return true; }

you have to work with window.navigator.standalone, this API is only works on mobile IOS safari, not event on your macbook or iMac's safari...

Related

ios PWA How to open external link on mobile default safari(not In app browser)

I'm making a PWA in IOS 13.2.
I need to launch another PWA from my app by clicking an external link.
The problem is that my current PWA opens all external links in the in-app browser instead of regular safari and there is no add to Home screen option in the in-app browser.
How can I force the PWA to open an external link in regular safari instead of in-app safari?
Things that doesn't work
Changing the scope in the manifest-file
Using rel="noreferrer"
Using target="_blank"
It's a bit hard to answer this question (and all the comments) as the use case isn't overly clear, but here goes...
On mobile devices, an "in-app browser" is NOT the same thing as a Progressive Web App running in full-screen mode.
If an iOS app runs and then displays HTML content inside of it, it's utilizing UIWebView or WKWebView. However, in the case of a PWA it's already running in Safari as a "full screen" experience. Defining which you're trying to break links out of is extremely important as they function differently.
target="_blank" will typically break a link out of a page using WebView. I believe this is the default functionality for links outside the current domain as well.
An "installed" PWA is running in something called "Stand Alone" mode. This makes it full screen and removes navbars, etc. As of this writing, Safari doesn't support the fullscreen API that other browsers are implementing. Chrome uses the App manifest file to determine this functionality. Safari basically ignores the manifest in favor of proprietary meta tags.
In this case <meta name="apple-mobile-web-app-capable" content="yes"> tells Apple to make the page a stand-alone app. Try setting content="no" (Safari caches things heavily so you might need to force a refresh) on the pages that should break out of stand-alone mode. You can check to see what mode the page thinks it's in by using this javascript boolean window.navigator.standalone.
Or you can use javascript to force a "new window" in Safari as long as you're targeting a different subdomain or HTTP instead of HTTPS.
// if app is hosted from https://example.com
if (("standalone" in window.navigator) || window.navigator.standalone ) {
window.open('http://example.com/page', '_blank');
}
Finally, Apple uses some special URL strings to cause native apps to handle some actions like emails, phone numbers, and youtube videos. You might be able to "hack" that functionality to get safari to open your link.
After quite thorough investigations i believe this isn't possible currently (between iOS 13.X and iOS 14.1).
The following javascript API's will use in-app browser:
window.open()
window.location.href =
Using an anchor tag will also use the in-app browser no matter what attributes it is assigned. Changing the scope in the manifest also doesn't help.
BUT i did find a way to at least prompt the user that they are in an in-app browser to make the UX a little less confusing.
There are two ways (maybe more?) to detect if the browser is standalone: window.matchMedia("(display-mode: standalone)").matches and window.navigator.standalone . And here is the weird part:
// regular safari
window.navigator.standalone -> false
window.matchMedia("(display-mode: standalone)").matches -> false
// full screen apps (only pwas, weirdly enough this doesn't apply to pre-pwa web apps on iOS)
window.navigator.standalone -> true
window.matchMedia("(display-mode: standalone)").matches -> true
// in-app browsers launched from within a PWA
window.navigator.standalone -> true
window.matchMedia("(display-mode: standalone)").matches -> false
I assume window.navigator.standalone represents the parent context of the pwa and window.matchMedia("(display-mode: standalone)").matches represents the context of the in-app browser.
So a naive implemention to check if your app is running in the in-app browser on iOS:
function isIOSInAppBrowser() {
// Check if device is iPad, iPhone or iPod (this bit is naive and should probably check additional stuff)
if (Boolean(window.navigator.userAgent.match(/iPad|iPhone|iPod/)) === false) return false;
// Check if navigator is standalone but display-mode isn't
if (window.navigator.standalone === true && window.matchMedia("(display-mode: standalone)").matches === false) {
return true;
} else {
return false;
}
}
note that this implementation isn't reliable and can easily produce false positive in future version of iOS
Tested with latest iOS-12/iPhone 7
If anyone could test with different device and iOS versions.
iOS is opening <a> tag as external url, launching Safari.
So, create a hidden tag:
HTML
<a hidden id='openSafari' href=''>click me</a>
script
let openSafari = document.getElementById('openSafari');
openSafari.setAttribute('href', 'https://netflix.com');
openSafari.click()
The way your app will work based on what did you configured in manifest.json file.
In manifest file the scope key will determines which link will open inside PWA and which link will open in browser.
For example route under myapp will open inside PWA and other would be outside.
{
"name": "My App",
"start_url": "/myapp",
"scope": "/myapp",
"display": "standalone"
}
I would personally try the target="_blank" to the href link. I would also add a rel="noreferrer external" to ensure this is consistent across different webview implementations. There should also be a way to do this via the manifest file although I'm not entirely sure how that works in iOS.
#IBAction func openURL(_ sender: Any) {
// check if website exists
guard let url = URL(string: "https://apple.com") else {
return
}
let safariVC = SFSafariViewController(url: url)
present(safariVC, animated: true, completion: nil)
}
This will work perfectly on all iOS versions with PWA support. (I know this because I am a longtime iOS user):
Add target="_blank" to all the links that you have. That will open them in the mobile default safari.
For example:
Page

Navigator vibrate break the code on ios browsers

I want to use navigator.vibrate on my page.
This is my code:
var canVibrate = "vibrate" in navigator || "mozVibrate" in navigator;
if (canVibrate && !("vibrate" in navigator))
{
navigator.vibrate = navigator.mozVibrate;
}
$(document).on('click', '.answer', function (eve) {
$this = $(this);
navigator.vibrate(222);
// some other code ...
This works on Android devices but on iOS (I tested on Firfox, Chrome and Safari on some iOS devices) the code will be broken at this line.
Why is that?
Apple's mobile web browser simply does not have support for it.
Firefox and Chrome for iOS are wrappers around Safari's rendering engine.
Quentin is correct that Apple devices do not support the API.
The given code fails to check for vibration support when actually calling the method. To avoid the vibrate function being caught undefined:
const canVibrate = window.navigator.vibrate
if (canVibrate) window.navigator.vibrate(100)
We don't want our app to break down and be unusable on iOS devices.
But we really want to use navigator.vibrate() on Android or wherever possible.
One thing you can do is you can create your own policy over browser policies. Ask "Can we make iOS devices ignore navigator.vibrate()"?
The answer is "Well, yes you can do that by using a user agent parser."
(Such as Faisal Salman's UAParser to detect if the user's device was an iOS or Mac OS device.)
In your code, wrap all the navigator.vibrate() calls inside conditions like,
if(nameOfUsersOS != "iOS" && nameOfUsersOS != "Mac OS") { navigator.vibrate(); }
Note: You must replace nameOfUsersOS with your own variable name.
Note: This is only one possible approach. Policy makers of Apple can and sometimes do change their minds. That means in the future they could allow the good Vibration API just like they allowed the Web Speech API recently. You must use the solution in kotavy's answer unless your policy is like "no vibration for Apple users forever".

Detect if page is viewed in samsung stock browser or as a standalone web app

As the title states, I’m trying to detect if my web page is being viewed as a web page in samsungs stock browser or if it is opened as a standalone web app saved on the homescreen. But the javascript-codes i’ve found for doing that only works for Safari and Chrome as far as I can tell.
Can someone please provide me with a good solution for this?
For some reason, window.matchMedia('(display-mode: standalone)').matches is false on a PWA installed by Samsung Internet, even if its display mode is indeed standalone.
However, you can follow the method provided in this answer, which works on Samsung Internet:
Set the start URL in the manifest file to include a query string parameter, ex:
"start_url": "./?mode=standalone"
Then in your JavaScript you are able to check for this query string parameter.
Provided you are in standalone mode, the address bar is not visible to the user, so it would not affect them.
I'm a newbie on PWA, so I had similar trouble with Samsung browser.
looks like it's been about 3 years since many people posted the problem, for me, it was still hard to find a better solution because "start_url" wasn't enough.
I googled and looked into ways to solve this problem and it seems like I did it. Finally, the post resolved my problem.
In my case, I only considered and tested chrome browser, Samsung browser and safari browser. You will be able to customize if you want as I commented on the source code.
const BROWSER_TAB = "browser-tab";
const STAND_ALONE = "standalone";
const getDisplayMode = () => {
let displayMode = BROWSER_TAB;
if (window.navigator.standalone || window.matchMedia("(display-mode: standalone)").matches) {
displayMode = STAND_ALONE; // PWA
} else if (
window.screen.height - document.documentElement.clientHeight <
100
) {
displayMode = STAND_ALONE; // PLAY_STORE, (maybe AppStore as well)
} else if (
new URLSearchParams(window.location.search).get("source") === "pwa" // "start_url": "/?source=pwa", from menifest.json
) {
displayMode = STAND_ALONE; // IOS or PWA or PLAY_STORE but it only works on start_url in menifest.json
}
return displayMode;
};
I hope this helps even though it's not a perfect solution.

this.platform.is("mobile") returning true for browser

I am running a ionic project on browser
script
"browser": "ionic-app-scripts serve --sourceMap source-map --iscordovaserve --wwwDir platforms/browser/www/ --buildDir platforms/browser/www/build"
and running npm run browser
I am trying this piece of code in app.component.ts
if (this.platform.is('ios') || this.platform.is('android') || this.platform.is('mobile'))
//execute certain function only on mobile
Problem
this.platform.is('mobile') and this.platform.is('android') is returning true to me in case of browser which is weird behaviour.
So , how to obtain the condition so that on browser i want to disable certain feature and why is the above code not working as expected.
Thanks.
There are two approaches i got.
As suggested By Sergey
let isWebApp = this.platform.url().startsWith('http');
Found on the Ionic Forum
!document.URL.startsWith('http');
These are mainly used for development as development process is faster is in browser as changes are reflected instantly.
I really don't think you need this but you can use:
if(this.platform.is('core') || this.platform.is('mobileweb')) {
// In Browser
} else {
// In Mobile
}
If you Enabled Toggle device toolbar from browser, It'll return mobile.
and if it is disabled then return desktop.
You can see it'll return desktop by disabling Toggle device toolbar from the browser:
You have to refresh(f5) the page after changing the state of Toggle device toolbar or it'll not work, because it was cached.
Thats some weird behavior of ionic platform. As per the documentation, platform.is('mobile') returns true only on a mobile device.
But
As Ionic is just gonna be used in the mobile devices, not in the browser, i don't think you need the logic to check whether the device is a mobile or not.
You can use other way to detect whether your app is web app or a cordova hybrid app:
let isWebApp = this.platform.url().startsWith('http');
In cordova (on device basically) depending on platform the app's "url" will start from "file://" or other but not http.

javascript redirect only pc users not mobile

I am trying to redirect pc users who are using adblock to a certain page.but i dont want to redirect mobile users.
here is my code
<script src="/assets/js/ads.js" type="text/javascript"></script>
//the bait for adblock
<script type="text/javascript">
if(document.getElementById('ElvJCLbfcHDP')){
alert('Blocking Ads: No');
} else {
alert('Blocking Ads: Yes');
}
as you can see this only shows if the ads are blocked or not.but what I want to do is check if users are coming from mobile or PC then redirect only PC adblock users to a certain page and let mobile users use site as it is.
i found this
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
// some code..
}
but as u can see it only detects if users is from mobile and then run the code.i want it to check if user is from PC and then run the redirect
Use ! logical not operator to alter the statement
if(!/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
// Desktop / pc
}
Don't try to redirect based on a test for a device.. You'll spend every moment of your time updating the list and wondering why some devices that are on your list are getting through. navigator.userAgent is notoriously unreliable.
From MDN:
Deprecated This feature has been removed from the Web standards. Though some browsers may still support it, it is in the process of
being dropped. Avoid using it and update existing code if possible;
see the compatibility table at the bottom of this page to guide your
decision. Be aware that this feature may cease to work at any time.
The NavigatorID.userAgent read-only property returns the user agent
string for the current browser.
The specification asks browsers to provide as little information via
this field as possible. Never assume that the value of this property
will stay the same in future versions of the same browser. Try not
to use it at all, or only for current and past versions of a
browser. New browsers may start using the same UA, or part of it, as
an older browser: you really have no guarantee that the browser agent
is indeed the one advertised by this property.
Also keep in mind that users of a browser can change the value of this field if they want (UA spoofing).
Typically, a desktop can be rooted out simply by the width of the window (as measured in CSS pixels, not hardware pixels).
if(window.innerWidth > 1280){
location.href = "desktop path";
} else {
location.href = "mobile path";
}

Categories

Resources