Retrieve User-Agent programmatically
Asked Answered
C

6

46

Is there a way to retrieve Browser's user-agent without having a WebView in activity?

I know it is possible to get it via WebView:

WebView view = (WebView) findViewById(R.id.someview);
String ua = view.getSettings().getUserAgentString() ;

But in my case I don't have/need a webview object and I don't want to create it just for retrieving user-agent string.

Charters answered 2/9, 2010 at 10:9 Comment(0)
M
70

If you don't have one you can try taking it like this

String ua=new WebView(this).getSettings().getUserAgentString();

Edit-

The doc for getUserAgentString() says

Return the WebView's user-agent string.

So i don't think you can get it unless you declare one. Some one correct me if i am wrong

Manche answered 2/9, 2010 at 10:21 Comment(2)
Thanks, it works. Would be nice to get around it without creating an object, but seems it is not really possible...Charters
Mind that Webview is not available on some devices, then this method throws exception.Joyner
S
66

There is a much simpler way if you are on Android 2.1 or above. Granted this isn't the exact same User Agent string that a webview would return, but might serve you well enough for your purposes.

As an additional advantage to pulling from web view, you can use this from any thread (not just the UI thread).

There is a system property called http.agent, which can be used to retrieve the User-Agent string.

String userAgent = System.getProperty("http.agent");

See Programmatically get User-Agent String for more details.

Stein answered 7/9, 2012 at 8:13 Comment(3)
This is not Browser's user agent, which is what was asked for. It works well for many purposes, though.Insured
This is the User-Agent String that the WebView will use to identity itself to the host of the current loaded page in WebView. Just in case you need to connect via HTTP Transport to any remote host, than this is the most elegant way to identity your self as Android WebView.Baccalaureate
Very good solution for calls with i.e. retrofit/okhttp. I just tried it on Android 6 and Android 9. What you get is a UserAgent like Dalvik/2.1.0 (Linux; U; Android 9; SM-G398FN Build/PPR1.180610.011)Contrecoup
M
39

I used to use solution proposed by DeRagan. But it turned out that creating a single WebView instance starts a thread "WebViewCoreThread" which stays on the background until application is terminated by the system. Maybe it doesn't consume too much resources but I don't like it anyway. So I use slightly different method now, which tries to avoid WebViewCoreThread creation:

// You may uncomment next line if using Android Annotations library, otherwise just be sure to run it in on the UI thread
// @UiThread 
public static String getDefaultUserAgentString(Context context) {
  if (Build.VERSION.SDK_INT >= 17) {
    return NewApiWrapper.getDefaultUserAgent(context);
  }

  try {
    Constructor<WebSettings> constructor = WebSettings.class.getDeclaredConstructor(Context.class, WebView.class);
    constructor.setAccessible(true);
    try {
      WebSettings settings = constructor.newInstance(context, null);
      return settings.getUserAgentString();
    } finally {
      constructor.setAccessible(false);
    }
  } catch (Exception e) {
    return new WebView(context).getSettings().getUserAgentString();
  }
}

@TargetApi(17)
static class NewApiWrapper {
  static String getDefaultUserAgent(Context context) {
    return WebSettings.getDefaultUserAgent(context);
  }
}

It creates WebSettings instance directly using package-visible constructor and if that is not available for some reason (e.g. due to API changes in future Android versions) - silently falls back to "WebView-like" solution.

UPDATE

As pointed by @Skywalker5446, starting from Android 4.2/API 17, there is a public static method to get default user agent value. I've updated my code to use that method on the supported platforms.

Mittel answered 10/3, 2011 at 14:56 Comment(4)
This method will fail on Android 4.2, WebSettings is now abstract, though there is a WebSettingsClassic, calling private APIs is always not an elegant way, it will just fail in future and you can not fix it without an upgrade.Thermopile
@Skywalker5446 Thanks for useful info, I'll update my answer accordingly.Mittel
WebSettings is abstract even on 4.1.1.Geometrize
With WebSettings.class.getDeclaredConstructor(Context.class, WebView.class) I'm now getting the lint warning "Cannot resolve constructor with specified argument types"Washedup
B
9

Since Android 2.1 you should use System.getProperty("http.agent");

You also dont need to create a WebView first AND , thats the advantage, you can use it inside a non-uithread.

greetings steve

Bushcraft answered 5/11, 2013 at 18:9 Comment(2)
This is not the answer to question. This gives device's user ganet, not the webview's.Helve
This will return something like "Dalvik\/2.1.0 (Linux; U; Android 8.0.0; Android SDK built for x86 Build\/OSR1.180418.026)" which is not what you would want. I think the correct answer is 'WebSettings.getDefaultUserAgent(context)' which does not require any instance of WebViewThese
G
2

This is an updated solution based on previous answers that works when you compile for KitKat. Now the WebSettings class is abstract and the WebSettingsClassic class has been removed.

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static String getUserAgent(final Context context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        return WebSettings.getDefaultUserAgent(context);
    }
    else {
        try {
            final Class<?> webSettingsClassicClass = Class.forName("android.webkit.WebSettingsClassic");
            final Constructor<?> constructor = webSettingsClassicClass.getDeclaredConstructor(Context.class, Class.forName("android.webkit.WebViewClassic"));
            constructor.setAccessible(true);
            final Method method = webSettingsClassicClass.getMethod("getUserAgentString");
            return (String) method.invoke(constructor.newInstance(context, null));
        }
        catch (final Exception e) {
            return new WebView(context).getSettings()
                    .getUserAgentString();
        }
    }
}
Geometrize answered 2/6, 2014 at 8:21 Comment(0)
S
1

Thanks to Idolon's answer my app could process this in the background.

But somehow on HTC Inspire 4G from AT&T that runs 2.3.3, it goes to the catch statement and it can be no longer run on the background thread. My solution for this is the following:

public static String getUserAgent(Context context) {
    try {
        Constructor<WebSettings> constructor = WebSettings.class.getDeclaredConstructor(Context.class, WebView.class);
        constructor.setAccessible(true);
        try {
            WebSettings settings = constructor.newInstance(context, null);
            return settings.getUserAgentString();
        } finally {
            constructor.setAccessible(false);
        }
    } catch (Exception e) {
        String ua;
        if(Thread.currentThread().getName().equalsIgnoreCase("main")){
            WebView m_webview = new WebView(context);
            ua = m_webview.getSettings().getUserAgentString();
        }else{
            mContext = context;
            ((Activity) mContext).runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    WebView webview = new WebView(mContext);
                    mUserAgent = webview.getSettings().getUserAgentString();
                }

            });
            return mUserAgent;
        }
        return ua;
    }
}

(suppose you have mContext and mUserAgent in the field)

Simpatico answered 2/9, 2011 at 21:50 Comment(1)
You probably want to either quit() the Looper and join() the thread or use wait() and notify()/notifyAll() so that mUserAgent is guaranteed to be modified. Currently, the method might return before the child thread updates mUserAgent.Asafetida

© 2022 - 2024 — McMap. All rights reserved.