Which part of Android is in charge of picking a correct resource profile?
Asked Answered
A

3

8

I have a weird problem.

Before you come to an idea to lash out on me, I am working on a custom Jelly Bean. Therefore the "usual nice approaches" might not work here, and dirty workarounds have to be made.

I have an APK which contains the following in assets:

layout
layout-mdpi
layout-land
layout-large-mdpi
layout-large-land-mdpi
layout-large-hdpi
layout-large-xhdpi

And some other metrics code returned this:

D/AppDemo( 2091): measured width: 1920 PE width: 1920 scaleFactor = 1.0
D/AppDemo( 2091): [ANDROID] measured width: 1920 measured height: 1080
D/AppDemo( 2091): [ANDROID] scale for resources: 2.0 xdpi: 320.0 ydpi: 320.0
D/AppDemo( 2091): [ANDROID] screen density: xhdpi
D/AppDemo( 2091): [ANDROID] screen size: large
D/AppDemo( 2091): [ANDROID] using layout: layout-mdpi

So, looking at the metrics, why isn't layout-large-xhdpi being loaded?

Please, tell me where I can look this up. I really need to find a way to force the Layout/Resource/AssetManager to load a specific layout.

I am aware the most popular comment on this issue is "you do not need / why do you have layout-xhdpi, you should have drawable-xhdpi and layout-large" but, bear with me.

I would very appreciate even small hints as to where to look at, and what to look for. So far, AssetManager seems like the place to start digging/logging.

When I omit layout-mdpi, the application crashes on me, with missing resources. The bug seems to be that even though the code returns xhdpi, it assumes mdpi somewhere else. I need to find this, and fix it so my apps look as nice as they did on ICS :)

I am figuring out which layout is loaded in a simple manner - all root layouts have a android:tag element, and when I setContentView(R.layout.main_layout) I grab the tag on the root element and know which folder got loaded. Apart from the visual feedback, this will eventually have to match with my device configuration.

Thanks in advance.

Amazonas answered 26/2, 2013 at 11:9 Comment(2)
Although you are not fishing for you don't need that responses, I believe a rough explanation of your final goal may help us think of ways to get what you need, without going a difficult route.Blackburn
@Phil: Ok, the problem is that because of a discrepancy somewhere, all apps show only the upper-left quarter on the whole screen. Instead of being smaller (if scaling worked properly) they are much bigger. In any case, I got reassigned from this, so there's the end of that.Amazonas
S
3

Interesting problem. I had a look, but it all goes a bit down the rabbit hole. No answer as such, but maybe my analysis will set you in the right direction.

I looked at what happened from setContentView forwards. Obviously at that point you are talking in terms of a density-agnostic layout reference (e.g. R.layout.main_layout) and then later it will be turned into a reference to a specific file in the APK. When and where is your question.

I used landscape/portrait qualifiers so that I could change the quality at runtime, and I used a debugger with the Android source attached.

Here's a flow, starting some way into it.

  1. Resources.getLayout(int)
  2. Resources.loadXmlResourceParser(int, String)
  3. Resources.getValue(int, TypedValue, boolean)
  4. AssetManager.getResourceValue(int, int, TypedValue, boolean)
  5. StringBlock.get(int)
  6. StringBlock.nativeGetString(int,int)

Let's work backwards.

Step 6 is a native (C) method that returns the qualified reference, e.g. /res/layout-land/yourview.xml. Its parameter is an index, however, and this changes based on whether we are in landscape or portrait.

To see where that came from we have to go back to step 4. The index is a field within the TypedValue, but it is not initially set correctly when it is passed in to this method.

Step 4 calls another native method, AssetManager.loadResourceValue(), which alters the passed in TypedValue and sets the index appropriately.

Maybe you can start looking at the C for that and see how you get on.

Stamm answered 6/3, 2013 at 19:38 Comment(7)
In particular you might want to start with ResourceTypes.cpp, in which you can see density-related config items, but it quickly gets too complicated for my liking.Stamm
ResourceTypes looks interesting and seems to strike closest to home, congrats on winning the bounty :)Amazonas
Thanks. You can see there's a method 'isBetterThan' which does the comparison between candidate configs, and likely includes the logic you're after. I didn't get as far as working out where those candidates come from though.Stamm
Nope, that one doesn't work for me either. The candidates come from the ResMap in the APK.Amazonas
Line 1770 is where some density work begins, but if that doesn't return anything, it will fall through to the method at line 1533. I can't be sure but it appears that the density returned by your metrics is irrelevant; the actual choice is made anew in this code.Stamm
let us continue this discussion in chatStamm
looks like the NDK provides access to the isBetterThan logic. Does anyone know anywhere closer to java land I might access this logic from?Radferd
H
3

There are so many hacked devices in the market like Micromax Funbook which have a screen size large but uses the mdpi resources trust me I have worked with them and it was very frustrating.

As you mentioned your app is working great with every other device it's just not working with this praticular tablet you may need to implement such kinda solution posted here

http://android-developers.blogspot.in/2011/07/new-tools-for-managing-screen-sizes.html

You must read the last segment on this page it will surely help.

to summarize it here the suggested approach is to create a seperate layout with the different name using the different resources altogether.

You are driving the resource picking process here not the system. which may be time consuming but will surely help.

public class MyActivity extends Activity {
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate();

        Configuration config = getResources().getConfiguration();
        if (config.smallestScreenWidthDp >= 600) {
            setContentView(R.layout.main_activity_tablet);
        } else {
            setContentView(R.layout.main_activity);
        }
    }
}  
Harridan answered 7/3, 2013 at 5:41 Comment(2)
The problem is that my layout folders were layout-sw600dp and layout-sw1000dp but none of them "fire". Something is messed up, but the problem is out of my hands so it seems. But I see the approach being used here, having a unified layout folder and picking the right one manually. I see.Amazonas
But these res/layout-xlarge/main_activity.xml # For pre-3.2 tablets res/layout-sw600dp/main_activity.xml # For 3.2 and up tablets simply don't work and don't get picked.Amazonas
G
1

I don't know if i get you right, you are building a custom AndroidOS and you want this custom AndroidOS to always load the same layout resource, in your case layout-large-xhdpi?

If so, i think you have customize the Configuration Class. If you want it quick and dirty, you could maybe override the int-Constants on line 72 and below.

Concrete, change:

....
100     public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
108     public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;

to:

....
100     public static final int SCREENLAYOUT_SIZE_LARGE = 0x04;
108     public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;

Other way could be to override the Constructor and the setToDefaults()-Method so it always loads the same screenlayout.

I didn't tried it out, I do not know if it is working, and I have no credible and/or official sources (Excluded offical Android Code), but i hope i could help you.

Graces answered 1/3, 2013 at 9:51 Comment(1)
Please look at the gathered data. Those were gathered using the configuration class, and match my device. It registers a good screen and right density, but picks wrong layout folder. I'm looking for a quick dirty fix to the layout-picker.Amazonas

© 2022 - 2024 — McMap. All rights reserved.