Identifying a web application functioning as a homescreen app within the Android Stock Browser

We are in the process of developing a web application that needs to function as a standalone or homescreen app. While we can identify if it is being accessed from Chrome or Safari using window.navigator.standalone or

window.matchMedia('(display-mode: standalone)')
, these methods do not seem to work with the default Android stock browser/Samsung Internet. Additionally, we cannot utilize start_url in manifest.json because we need to provide a unique user token to the homescreen app.

Is there a way to determine if the app is launched from the homescreen when added via the Android stock browser?

Related

Answer №1

It seems impossible to directly detect whether an app is running in the Samsung Browser or as a standalone app within the browser. The only indicator I could find is the difference in window.innerHeight, which does not account for the address bar. By comparing it to window.screen.height, one might be able to estimate a ratio. However, this method is not foolproof due to the variety of devices the browser can be used on. Standalone apps should have a larger window.innerHeight, but without knowing the specific dimensions of each, it's difficult to determine.

// Possible solution, albeit imperfect
if ((window.innerHeight / window.screen.height) > 0.9) {
  // There is a chance this is a standalone app.
}

Another approach I came across involves dynamically setting the manifest file using JavaScript, enabling the inclusion of a unique token in the start URL for individual users. Despite its potential benefits, this method has its drawbacks. Creating a manifest file through JavaScript is technically unsupported and may prevent your app from being installed as a web APK. Firefox does not support dynamically generated manifest files, while iOS caching may lead to its own set of issues. Additionally, Chrome DevTools may not always accurately reflect the content of your manifest file. Some inspiration for this can be drawn from this Medium article.

// This approach comes with several limitations. Proceed with caution.
import manifestBase from '../manifest.json';

const myToken = window.localStorage.getItem('myToken');
const manifest = { ...manifestBase };
manifest.start_url = `${window.location.origin}?standalone=true&myToken=${myToken}`;
const stringManifest = JSON.stringify(manifest);
const blob = new Blob([stringManifest], {type: 'application/json'});
const manifestURL = URL.createObjectURL(blob);
document.querySelector('meta[rel=manifest]').setAttribute('href', manifestURL);

To address the issue with FireFox, it's recommended to set a sensible default value for the href attribute of your manifest meta tag. On the other hand, if the unique information in your start URL changes frequently, handling it in iOS may pose challenges. If your start URL remains static, it's advised not to use JavaScript to set the manifest file, but rather include distinguishing information (like the standalone=true query string) that differentiates a standalone app from a regular browser URL.


Furthermore, altering the browser mode to settings such as fullscreen does not resolve the issue with the Samsung Browser. It always retains the display mode of a browser, making it tricky to differentiate it from other modes based on this criterion alone.

Answer №2

After struggling to find a solution, I finally devised a new approach this evening. It seems that many of the suggested methods simply didn't do the trick. My idea was to calculate the difference between the screen height and the inner height of the html document. If this difference is 30px or less, then it must be due to the task bar on mobile phones. In such cases, I implemented fullscreen display rules...

     var flScrn = (screen.height - innerHeight );
        if (flScrn < 30) {
            $('.installprompt').css("display","none");
        }

Answer №3

Encountered a similar issue where providing parameters in the start_url parameter of the manifest did not yield results due to certain limitations:

  • The parameters specified in the manifest's start_url are disregarded (although documentation on this is scarce, my tests consistently confirmed this)
  • In cases where the url parameter was passed to the PWA, storing that value exclusively for the PWA within a cookie proved challenging as Chrome on Android shares cookies between the browser and the PWA, creating a hurdle elucidated here:

[How to separate PWA session and cookie with a standalone browser?][1] [1]: How to separate PWA session and cookie with a standalone browser? ( PWA as private tab )

(Exploring workarounds involving subdomains or similar approaches is futile as PWAs only function seamlessly on the domain they are installed from).

A workaround I devised also proved effective on the Android Stock Browser (verified on Samsung Galaxy S7 Edge):

  1. To bypass the ignored url parameter, assign a distinct start URL for the PWA in the manifest:

    ...
    "start_url": "/pwa_start",
    ...
    
  2. On the designated start_url page, implement a redirect to your desired page along with an url parameter (exemplified in PHP):

    <?php header('location: /?is_pwa=1'); ?>
    
  3. Utilize the following JavaScript function as the sole method to determine if the site is operating as a PWA, ensuring its presence across all website pages and execution at least once on the redirected page:

    // Return true if the website is currently running as PWA-App, otherwise false
    function isPWA() {
        // Try default method (only true if supported and running as PWA)
        if (window.matchMedia('(display-mode: standalone)').matches) return true;
    
        // Alternative method:
        if (!(window.sessionStorage || false)) return false; // Session storage not supported
        if (self.location.href.indexOf('?is_pwa=1') >= 0) {
            window.sessionStorage.setItem('isPWA', '1');
        }
        return window.sessionStorage.getItem('isPWA') == '1';
    }
    
  4. Additionally, you can dynamically add a CSS class to the <body> tag using JavaScript to differentiate content display between the PWA and browser by utilizing class names like "hidden-pwa" or "visible-pwa".

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The variable _spPageContextInfo has not been defined, resulting in a ReferenceError

The code in my JavaScript file looks like this: var configNews = { url:_spPageContextInfo.webAbsoluteUrl, newsLibrary: 'DEMONews', listId: '' }; // Attempting to retrieve the List ID $.ajax({ url: configNews.url + "/_a ...

The server-side PHP script may not always successfully respond to an Ajax call from the client

Whenever I try to access my index.html site, there are times when the Ajax request is made to call the PHP on the server in order to display the dynamic content. But for some reason, it only works intermittently when I manually refresh the page using the a ...

Encountering the error message "Unexpected token. Did you mean {'>'} or &gt;?" when trying to use an arrow function in React

I recently started learning React and Javascript. I encountered an error message that said: "Unexpected token. Did you mean {'>'} or &gt;?", specifically in relation to the "=>" part of the code below. This issue arose while I was worki ...

Using React Bootstrap to conditionally render columns within an array map

Within my React application, I am currently utilizing the map function to generate Bootstrap columns in the JSX code of the render method. One specific attribute within the array object I'm mapping is named "taken." Depending on whether this attribute ...

Customize Magento pop-up close function on click event

I developed a unique module with a Magento pop-up feature. I am looking to customize the close event for the pop-up. <div onclick="Windows.close(&quot;browser_window_updatecc&quot;, event)" id="browser_window_updatecc_close" class="magento_clos ...

What is the procedure for eliminating an event listener that does not directly invoke a function?

I have implemented an event listener in my JS file following a successful AJAX request: var pageButtonsParentElement = document.getElementById("page-buttons"); pageButtonsParentElement.addEventListener('click', event => { ...

Which framework should be used: client-side or server-side?

I am working on a project similar to craiglist, where users can post announcements for everyday items like cars and flats. I have already developed a significant portion of the backend using a RESTful API in three-tier architecture with Java, connecting to ...

Using Google Maps to Cluster Markers on Your Website

I need to display numerous markers on a map for my website. My inquiry is whether the google maps api for javascript still supports clustering for websites? I attempted using marker cluster and it seems like it's not functioning properly. If so, can ...

Fetching Date and Time from the Internet using PHP

While I understand that similar questions have been asked numerous times before, I have yet to find a solution that fits my specific needs. My question is regarding how to retrieve the current date and time from the internet rather than relying on the loc ...

Generating a dropdown selection list using data from a JSON file

package com.example.root.myapplication; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Button; import android.wi ...

What is the best way to transfer data from R to JS in a Shiny app and incorporate it into the user interface?

I am looking to add a custom news button to the header of my shinyapp, which will display information from a specific dataset. I want the button to show the number of news items available, similar to how shinyDashboard::notificationItem() works but with mo ...

Avoid using single quotes in Postgres queries for a more secure Node.js application

Snippet from my node js code: var qry = 'INSERT INTO "sma"."RMD"("UserId","Favourite") VALUES (' + req.body.user + ',' + JSON.stringify(req.body.favourite) + ')' My problem is inserting single quotes before JSON.stringify(r ...

Encountering an issue with Material-UI and Next.js: "TypeError: theme.spacing is not a function

Encountering an issue after modifying _app.js to dynamically generate a material UI theme. I've been following the implementation example provided by the material-ui team at: https://github.com/mui-org/material-ui/tree/master/examples/nextjs. To summ ...

Is it possible to establish a delay for requestAnimationFrame()?

My webpage has a very long layout with text in one column and a floating element in another. I want the floating element to follow the scroll of the window and return to its original offset once scrolling stops. The current code I have is: var ticking = ...

Is there a way to take a snapshot of an HTML Canvas frame and showcase it on a Bootstrap modal?

I have a page where users must grant permission for their webcam or camera. Once granted, a webmoji will move according to the user's face position. Below is a button that will turn blue and become active once a face is detected. When clicked, I want ...

Is it necessary to call Dispose() after every FindViewById call?

Is it necessary to call Dispose() after using FindViewById in Android Xamarin to prevent memory leaks? For example: void SetTextSomewhereInMyView() { var myTextView = FindViewById<TextView>(Resouce.Id.myTextView); myTextView.Text = "This is my Text ...

Output of ngResource compilation

Is there a way to retrieve a single result from my array $scope.trailers? I am encountering an issue where accessing the first index using $scope.trailers[0] returns undefined. The API call is made using ngResource. function getTrailers(pageNo){ ...

The drop-down menu is failing to display the correct values in the second drop-down

Having trouble with the update feature in this code. There are 2 textboxes and 2 dropdowns, but when selecting a course code, the corresponding values for the subject are not being posted. Can anyone assist? view:subject_detail_view <script type="te ...

Caution: Highlighting Non-ASCII Characters in Your Django Form

Looking to implement client-side Ajax validation for my Django form. The goal is to alert users in real-time if any non-ascii characters are detected as they type in a field. Originally considered using python to check for ascii characters in the form&apo ...

Toggle a Vue.js method to display responses for a particular question

Currently, I am in the process of developing a simple toggle feature for a FAQ section. The idea is that when a user clicks on an icon associated with a specific question, only that question's answer should be displayed. Although the function is oper ...