在 Android Stock Browser 上将 Web 应用程序 运行 检测为主屏幕应用程序

Detect web app running as homescreen app on Android Stock Browser

我们正在构建一个必须用作独立/主屏幕应用程序的网络应用程序。在 Chrome 和 Safari 中,我们可以检测它是从浏览器还是从具有 window.navigator.standalonewindow.matchMedia('(display-mode: standalone)') 的类似本机的浏览器容器中查看的。这两个选项似乎都不适用于默认的 Android stock browser/Samsung Internet。此外,我们也无法在 manifest.json 中使用 start_url,因为我们需要将令牌传递给每个用户唯一的主屏幕应用程序。

是否可以检测在使用 android stock 浏览器添加应用程序时是否从主屏幕打开该应用程序?

相关

据我所知,无法直接检测应用程序在三星浏览器中是 运行,还是在三星浏览器中作为独立应用程序。我能找到的唯一区别是 window.innerHeight,因为它不包括地址栏。使用 window.screen.height 可以计算出一个比率。由于此浏览器可以在许多不同的设备上使用,因此这不一定对您有帮助。 window.innerHeight 对于独立应用程序来说应该更大,但您不一定知道与浏览器体验相比,独立应用程序有多大。

// Imperfect solution
if ((window.innerHeight / window.screen.height) > 0.9) {
  // Some probability of this being a standalone app.
}

我发现的一个不同的解决方案是通过 javascript 设置清单文件,允许我们在 url 开始为每个单独的用户设置唯一的令牌。但是,这种方法有几个缺点。通过 javascript 设置清单文件在技术上不受支持,当您以这种方式创建清单文件时,您的应用将永远不会安装为 Web apk。 Firefox 根本不支持动态生成的清单文件,而 ios 缓存清单文件本身可能会导致问题。 Chrome devtools 也不会总是显示清单文件中的内容。以下部分来自this medium article.

// This approach has many caveats. Be aware of all of them before using this solution
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);

您可以通过将清单元标记的 href 属性设置为合理的默认值来解决 FireFox 的问题。如果您的独特信息经常变化,您将无法解决 ios 中的问题。如果您的开始 url 不是动态的,则根本不要通过 javascript 设置清单文件,而是使用一些信息(例如 standalone=true 查询字符串设置开始 url上面)允许您区分独立应用程序和浏览器 url.


我发现的另一件事是将浏览器模式设置为任何其他模式,例如 fullscreen 不会 "fix" 三星浏览器的错误。它永远不会将显示模式设置为浏览器以外的任何模式,因此也无法通过这种方式检测到它。

这就是我今晚想到的,我的头撞到了所有 "solutions" 我发现到处都是不起作用的东西。我的理论是任何 phone 上的任务栏都是 30px 或更少,所以从屏幕高度减去 html 的内部高度,如果剩下 30px 或更少,创建全屏规则, 例如...

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

我遇到了同样的问题。在清单的 start_url 参数中提供参数的所有解决方案都不起作用,因为:

  • 清单 start_url 中的参数将被忽略(没有找到记录的地方,但在我的所有测试中都是这种情况)
  • 如果 url 参数无论如何都传递给了 PWA,在 Android 上的 Chrome 中,您无法将该值存储在 cookie 中以仅对 PWA 可用,因为 PWA 的 cookie 将与浏览器共享,如下所述:

[独立浏览器如何分离PWA会话和cookie?][1] [1]:How to separate PWA session and cookie with a standalone browser? ( PWA as private tab )

(忘记使用子域或类似方法尝试解决方法,因为 PWA 只能在安装它的同一域上完全工作)。

我想出了一个解决方法,它也适用于 Android Stock 浏览器(在 Samsung Galaxy S7 Edge 上试过):

  1. 在清单中,为 PWA 指定一个唯一的开始 url(因为 url 参数将被忽略):

    ...
    "start_url": "/pwa_start",
    ...
    
  2. 在 start_url 中定义的页面上,使用这样的 url 参数提供到您要调用的实际页面的重定向([= 中的示例55=]):

    <?php header('location: /?is_pwa=1'); ?>
    
  3. 将以下 javascript 函数作为您用来检查站点是否 运行 作为 PWA 的唯一方法,并确保它在所有平台上都可用您网站的页面,并且它在您重定向到的页面上至少被调用一次:

    // 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. 如果页面是 运行 作为 PWA,您可以使用 javascript 向您的 标签进一步添加 css class。通过这种方式,您可以创建和使用 css class 等 "hidden-pwa" 或 "visible-pwa" 来在 PWA 和浏览器中显示不同的内容。