Screen.dpi on android

Before Unity 5.1 Screen.dpi value was calculated as the average of xdpi and ydpi. However on some devices these values are not correct, because they have to be set by device manufacturer and they are not used by Android OS, so they are not guaranteed to be correct. Instead Android OS uses densityDpi value for UI scaling. In Unity 5.3 we’ve changed Screen.dpi implementation to return the average of xdpi and ydpi only when the value is not too far from densityDPI, otherwise we were returning densityDPI. This was an attempt to return better dpi value on devices where xdpi and ydpi are totally wrong. However this turned out to be quite inconsistent because more devices than expected have xdpi (and ydpi) different than densityDpi.

We are going to make Screen.dpi to always return densityDpi value. This may change how your existing games look. If you’ll encounter this issue, or if you need a more precise dpi value, you can use the following C# script to get dpi value in an old way. Just keep in mind that on some devices xdpi and ydpi values are totally wrong.

float GetDPI()
{
    AndroidJavaClass activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    AndroidJavaObject activity = activityClass.GetStatic<AndroidJavaObject>("currentActivity");

    AndroidJavaObject metrics = new AndroidJavaObject("android.util.DisplayMetrics");
    activity.Call<AndroidJavaObject>("getWindowManager").Call<AndroidJavaObject>("getDefaultDisplay").Call("getMetrics", metrics);

    return (metrics.Get<float>("xdpi") + metrics.Get<float>("ydpi")) * 0.5f;
}
5 Likes

When you say “some devices xdpi and ydpi values are totally wrong”, can you elaborate? Can you please provide some examples of deviceModels that have wrong values and how far off they are?
Also do any of the iOS devices suffer from Screen.dpi being incorrect that you are aware of?
Our app is very dependent on drawing elements on the screen in an exact physical size so having an accurate dpi is crucial.

We haven’t done thorough testing on all devices and the testing was done 2 years ago, but the results we got back then were as follows:
Everything is fine, issue not reproducible: Google Nexus 5 (Android 5.1), LG G3 (Android 4.4.2), Samsung Galaxy S4 (Android 4.4.2), Motorola Droid Bionic (Android 2.3.4), LG Optimus L7 (Android 4.0.3), Sony Xperia U (Android 2.3.7).
Issue reproducible: Redmi returned dpi 160 should be ~312, Samsung Galaxy M Style (Android 2.3.7 K) returned dpi 160 should be ~233, HTC One X (Android 4.2.2) returned dpi 213 should be ~312, LG Optimus L5 II (Android 4.1.2) returned dpi 240 should be ~233.
As I’ve said, the testing was done 2 years ago. Some of those devices might have this issue fixed with Android updates as well as new devices might not have this issue at all, but we don’t have any data on this. You will have to do your own testing to find that out.

All iOS devices should return correct dpi value, because there are just a few dpi variants in iOS ecosystem and Apple has standardised them there.

Hi! I have run into the same problem. I need to know the total screensize. Can say that Screen.dpi returns a wrong value on Nexus 5X resulting in the screen precived as a 6.2" screen becoming 33% bigger than the actual screen area. Using the function above makes it work fine :slight_smile:

xdpi and ydpi fields also return incorrect values (e.g. on my Samsung Galaxy Mini 3). These values are set by the manufacturer and aren’t used by anything in the OS. See Google’s explanation on this here:
https://groups.google.com/forum/#!topic/android-developers/g56jV0Hora0
As Google says, the physical size simply can’t be known. It will always be wrong on some devices. And it doesn’t matter if Screen.dpi in Unity is used or any kind of direct polling from Android thru Java is used. This platform isn’t capable of returning its screen size.

1 Like

While this is misleading for anyone who wants exact physical sizing it otherwise sounds sensible as it will follow the manufacturers guidelines - on android I read the screen.dpi and render UI at a virtual 160dpi resolution thus I seem to get exactly what android material wants in terms of sizing - using their dp sizing recomendations.

On iOS is there not something similar where UI is a little bigger or smaller or it does weird scaling depending on device, with its native UI?

I really don’t like the docs on Screen.dpi which say: “Note that on Android this returns densityDpi which is a logical bucket containing a range of dpi values”
What does that even mean? Cut the logical bucket stuff and link to android docs maybe

2 Likes

There is actually a major issue on Android if you do not consider it - the screen zoom setting (part of android) affects the dpi returned by Screen.dpi so it is by no means consistent on a given device!!
If you scale your UI based on Screen.dpi and the user changes the android zoom level then everything is going to be bigger or smaller the next time they run your app or you check the dpi (you really need to check for dpi changes while the app is running in case they change this setting!!)

All of this means reproducing the native UI scaling is easier, but not exactly well documented on Unity’s side…
It also means Screen.dpi is not at all reliable on android if you really need actual physical size.

1 Like

hello, is there still a not reliable way to determine physical screen size in inches/cm as per September 2018

P.S. Is this solution relevant:

What bothers me most is that xDPI (density of pixels in horizontal direction) might be different from yDPI (density of pixels in vertical direction). First time I hear there is such thing!

Another P.S:
does current Unity versions 2018.x.x use ‘densityDpi’ property or the average of ‘xDpi’ and ‘yDpi’ as mentioned above. Seems like ‘densityDpi’ is much more reliable

One thing to consider is devices, such as the newer ones in the Samsung S / Note series, have a fullscreen mode. Basically, you may be running with a 16:10 resolution or 18.5:9, depending on user preference.

heightPixels and widthPixels return the 16:10 resolution values.

http://answers.unity.com/answers/927187/view.html will allow getting the 18.5:9 values.

I would love to have Screen.dpi report correct values for iOS. It’s wrong even as of Unity 2018.

I need to get the ppi for my android device so that I can display the same size picture on the different screen. For example, the same picture displayed on the ppi screen is 0.32mm.

The current official document,

https://docs.unity3d.com/cn/2018.2/ScriptReference/Screen-dpi.html

And https://discussions.unity.com/t/632358

The test results are different from the actual results.

The test method is to measure the distance of 3cm horizontally and vertically, so as to obtain the number of pixels of 3cm horizontally and vertically, and finally calculate ppi.

If the number of pixels 3cm across and 3cm up and down is 558, the ppi can be calculated by 558 squared +558 squared =A, and then the square root of the value A is to obtain the value B, and finally the corresponding ppi can be calculated.

I believe that what I measured is the correct value. After verification, I found that using the measured value can also display pictures with the same visual size on the different screen.

My question is, is there a better way to get this ppi than by measuring it with a ruler?

public static float GetPPI(string horizontal, string vertical)
    {
        float ppi = 0;

        if (float.TryParse(horizontal, out float h) && float.TryParse(vertical, out float v))
        {
            var standardInch = Mathf.Sqrt(Mathf.Pow(3.0f / CM_INCH, 2) + Mathf.Pow(3.0f / CM_INCH, 2));

            ppi = Mathf.CeilToInt(Mathf.Sqrt(Mathf.Pow(h, 2) + Mathf.Pow(v, 2)) / standardInch);
        }

        if (ppi <= 0)
        {
            Debug.LogError($"ppi<=0 horizontal:{horizontal},vertical:{vertical}");
            ppi = 287;
        }

        return ppi;
    }

any solution for this?

Do we have any more reliable way for this?

Is it impossible for unity to have xdpi and ydpi property for every platform? In fact, is it impossible for unity to have a property IsPhysicalSizeAvailable and PhysicalScreenSize as Vector2 in DisplayInfo?

We can check for availability and fallback to width|height / DPI. But in various OS and platform there are api to get physical monitor size and I wish this api should be implemented in unity directly. It would be crucial in MR and spatial computing

1 Like