How can I set custom user-agent or custom header in a Trusted Web Activities (TWAs) app which is built using pwabuilder.com website?

94 views Asked by At

I'm developing a Trusted Web Activity (TWA) app using PWABuilder and am facing a challenge with user permissions and notifications. I need to differentiate between users accessing the app through the native TWA and those using the web app directly. This is crucial for:

Requesting notification permissions appropriately (APN for iOS, WebPush for Android). Saving subscription details to the database accurately. I've explored setting a custom user agent or custom headers within the TWA to achieve this, but haven't found a reliable solution yet. I've tried modifying the manifest.json without success.

Here's a sample React snippet illustrating my approach:

{isIOSApp() && (
            <APNPermission />
          )}
          {isAndroidApp() && (
            <WebPush />
          )}

I've been able to set a custom user agent in my iOS code using the WebView component, but I haven't found a similar direct solution for Android within the TWA context. I've explored the following approaches without success:

Relying on document.referrer: This proved unreliable, especially when loading the app from external links or after Google login.

Modifying manifest.json: Appending a query parameter to start_url isn't feasible due to existing query parameters in links from emails and other sources.

Tweaking LauncherActivity: I investigated overriding methods within the com.google.androidbrowserhelper.trusted.LauncherActivity class, but I'm unsure which method would be appropriate for setting custom data.

I've analyzed the com.google.androidbrowserhelper.trusted.LauncherActivity class and experimented with overriding methods to potentially set custom data. Here are my findings:

Overridden Methods:

onCreate(Bundle savedInstanceState): I added log statements to track intent extras and saved instance state, but this method doesn't seem directly relevant for setting headers or user agents.

getLaunchingUrl(): I logged the launching URL, but this method primarily retrieves it rather than modifying it.

createTwaLauncher(): I logged the TWA launcher creation process, but I haven't found a way to customize headers within this method.

launchTwa(): I logged the launch process, but this method doesn't offer direct control over headers.

getCustomTabsCallback(): I created a custom CustomTabsCallback implementation (MyQualityEnforcer) to explore potential interactions, but I haven't successfully sent messages to the web app yet.

Below are the functions which i wrote to test the sequences.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Intent i = super.getIntent();
        i.putExtra("my app", "company app 2");
        Bundle iExtras = i.getExtras();
        if (savedInstanceState == null) {
            Log.d(tag, "2.onCreate Intent.getExtras " + i.getExtras().toString());
            super.onCreate(iExtras);
        } else {
            Log.d(tag, "2.onCreate savedInstanceState " + savedInstanceState.toString());
            super.onCreate(savedInstanceState);
        }
        Log.d(tag, "2.onCreate launcher activity last line");
    }

    @Override
    protected Uri getLaunchingUrl() {
        Log.d(tag, "4.getLaunchingUrl");
        // Get the original launch Url.
        Uri uri = super.getLaunchingUrl();
        Log.d(tag, "4.getLaunchingUrl Launching URL: " + uri);
        return uri;
    }

    @Override
    protected TwaLauncher createTwaLauncher() {
        Log.d(tag, "5.createTwaLauncher twa launcher created");
        TwaLauncher l = super.createTwaLauncher();
        Log.d(tag, "5.createTwaLauncher twa launcher created " + l.getProviderPackage());
        return l;
    }

    @Override
    protected void launchTwa() {
        Log.d(tag, "3.launchTwa main fn starting");
        super.launchTwa();
        Log.d(tag, "3.launchTwa executed");
    }

    @Override
    protected CustomTabsCallback getCustomTabsCallback() {
        Log.d(tag, "6.CustomTabsCallback ");
        CustomTabsCallback q = new MyQualityEnforcer();
        Bundle b = new Bundle();
        b.putString("myapp", "company app");
        q.onMessageChannelReady(b);
        Log.d(tag, "6.CustomTabsCallback " + q.toString());
        return q;
    }


I also tried to override the QualityEnforcer to check if I can send a message to the webapp javascript, but couldn't figure it out how yet. Below is the class

public class MyQualityEnforcer extends QualityEnforcer {

    private final String tag = "MyLauncherActivity";

    @Override
    public void onMessageChannelReady(Bundle extras) {
        // Handle the URL update event here
        Log.i(tag, "URL updated: " + extras.toString());
    }
}

Logs

---------------------------- PROCESS STARTED (9972) for package com.company.app.twa ----------------------------
2024-01-10 16:29:56.370  9972-9972  MyLauncherActivity      com.company.app.twa               D  1. hello worldWed Jan 10 16:29:56 GMT+11:00 2024
2024-01-10 16:29:56.604  9972-9972  MyLauncherActivity      com.company.app.twa               D  2.onCreate Intent.getExtras Bundle[{my app=company app 2}]
2024-01-10 16:29:56.617  9972-9972  MyLauncherActivity      com.company.app.twa               D  3.launchTwa main fn starting
2024-01-10 16:29:56.621  9972-9972  MyLauncherActivity      com.company.app.twa               D  4.getLaunchingUrl
2024-01-10 16:29:56.622  9972-9972  MyLauncherActivity      com.company.app.twa               D  4.getLaunchingUrl Launching URL: https://amazing-domain.example.com/
2024-01-10 16:29:56.623  9972-9972  MyLauncherActivity      com.company.app.twa               D  5.createTwaLauncher twa launcher created
2024-01-10 16:29:56.639  9972-9972  MyLauncherActivity      com.company.app.twa               D  5.createTwaLauncher twa launcher created com.android.chrome
2024-01-10 16:29:56.639  9972-9972  MyLauncherActivity      com.company.app.twa               D  6.CustomTabsCallback 
2024-01-10 16:29:56.641  9972-9972  MyLauncherActivity      com.company.app.twa               I  URL updated: Bundle[{myapp=company app}]
2024-01-10 16:29:56.650  9972-9972  MyLauncherActivity      com.company.app.twa               D  6.CustomTabsCallback com.company.app.twa.MyQualityEnforcer@a6145b7
2024-01-10 16:29:57.339  9972-9972  MyLauncherActivity      com.company.app.twa               D  3.launchTwa executed
2024-01-10 16:29:57.347  9972-9972  MyLauncherActivity      com.company.app.twa               D  2.onCreate launcher activity last line

I will be very happy if someone can give me some clue to detect the android device running as Trusted Web Activity using JavaScript.

Thanks.

1

There are 1 answers

0
Judah Gabriel Himango On

One way you could do this is:

  1. Change the URL you use on PWABuilder to identify that it's the PWA launched from Android. For example, if your PWA is located at https://example.com, in PWABuilder specify your URL as https://example.com?utm_source=androidtwa
  2. In your JS code, when launching an HTTP request, check if the URL has the androidtwa query string, and if so, append the custom header to your request.

This way you don't need to modify any of PWABuilder's Android code and instead you keep the logic in your app's code.