21

Background

I have been recently hired to maintain a very large program (only two Activities, about a hundred Fragments, and several hundred layouts). Furthermore, most of the content of the layouts (images and text) as well as the order that the layouts appear is dynamically determined via the company's Web API.

Unfortunately there is no documentation. No map, nada. The company hired a 3rd party to make this app long before they even had Android programmers. And the quality of the code is poor at best (even variable names are confusing and contradictory).

Consequently, I spend about 70% - 90% of my time simply searching for layouts and code just to change the background of Button.

Question

Is there a tool that one can run (perhaps in Android Studio's debugger?) that can somehow spit the names of the layout files that are currently displayed?

My superiors often say something like, "Change that background texture from black to light gray." And I'm thinking: the change is trivial, but finding the xml file could take an hour.

--Update--

The project that generated this question is no longer under my control, so this question has become moot. But it does seem to be a common issue, so I'm leaving this question open. Perhaps in the future some kind of tool/solution will be arise. I hope this thread is useful to other programmers in that situation.

SMBiggs
  • 11,034
  • 6
  • 68
  • 83
  • Interesting question. I can't help but think that the custom layout inflater option might be fun to implement. At least if the sample code I linked to actually works. Good luck with this - pls provide feedback if you found a working solution - I'm quite taken by this idea, and quite disappointed that Hierarchy Viewer doesn't give the XML name out of the box.. – Richard Le Mesurier Sep 30 '15 at 17:07
  • did you ever make any progress with finding a good related tool? – Richard Le Mesurier Oct 13 '15 at 05:13
  • 1
    Not yet. I'm resorting to making a gigantic spreadsheet with snapshots and related layouts/java files as I find them. It's a pain in the a$$, but is already useful even though I'm less than half done. – SMBiggs Oct 13 '15 at 19:23
  • 1
    I had to laugh at that. Sometimes simple old school solutions are the best ones. Pragmatism often beats technology in the real world. – Richard Le Mesurier Oct 14 '15 at 05:21
  • 2
    I created a feature request at https://issuetracker.google.com/issues/68237819 , kindly star/comment this issue if anyone interested. – 林果皞 Oct 25 '17 at 09:21
  • 1
    @林果皞, Thanks! And it looks like they are planning on adding this tool in AS v. 3.1, which will be soon. But don't hold your breath! – SMBiggs Oct 26 '17 at 17:56

7 Answers7

13

There is also the Developer Assistant app for Android which can inspect a view hierarchy at runtime and then display the most probable layout names visible on the screen. The heuristic is involved so it won't work 100% accurate, but still can be helpful and works completely offline (a disclosure: I made this app).

An example:

enter image description here

8

Hierarchy Viewer tool

In the Android Device Monitor, there is a button you can click on called "Dump View Hierarchy for UI Automator".

This will open a UI Hierarchy viewer in the Device Monitor, which you can use to see the resource IDs of each of your views.

While this does not actually give you the name of the inflated XML file, it could be very useful. If you are lucky, you can even grep for the resource IDs to narrow down the search for which XML files to look at.

And if you hover over the title of the viewer window, it will show you the path to the actual XML file that it created. This is a way to find all the resource IDs in one place.


Some more info on Hierarchy Viewer in case it helps:

The Hierarchy Viewer allows you to debug and optimize your user interface. It provides a visual representation of the layout's View hierarchy (the Layout View) and a magnified inspector of the display (the Pixel Perfect View).


Create a custom Layout Inflater in code

Here's a blog post that contains something interesting - a custom layout inflater that intercepts all view inflation:

It may be possible to use that sample code to intercept the inflate calls, and receive the resource ID of the XML file:

At this point, you will need to turn the resource id into a useful name. Quoting from this question (How to get Resource Name from Resource id), you can use:

getResources().getResourceEntryName(int resid);

Add some logging and this may give you each XML file as it is being inflated.

This could be quite a nice way to document your entire project.

Community
  • 1
  • 1
Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
  • 1
    I haven't found the hierarchy viewer to be useful for this problem; as you pointed out, it doesn't do much for finding the specific layout file. Your other tricks are appreciated (+1), but not much better than what I've been doing. You're right, making a custom LayoutInflator would definitely do the trick, but I'm loathe to add yet another layer to this already messy code (and since there are multiple packages, overriding is even more troublesome). And lastly, I'm pretty lazy; that's why I'm here on SO. :) – SMBiggs Oct 13 '15 at 19:17
  • 1
    @Scott I hear you - the custom inflator seems like taking a hammer to kill an ant - much more than required. – Richard Le Mesurier Oct 14 '15 at 05:20
4

Possible "Android/sdk/tools/uiautomatorviewer" can help you. It can take dump of current screen from device and shows you views/layouts with their details - also with id's

  • 1
    Sounds like a good idea, but I'm running Android Studio. It seems that uiautomator craps out when AS is running. Sigh. – SMBiggs Oct 13 '15 at 19:21
  • 1
    @ScottBiggs If you start Android Device Monitor from Android Studio, then Android Studio will automatically turn off its ADB integration allowing Android Device Monitor, along with its built in UI Automator support. Alternatively you can manually temporarily disable ADB integration (Tools -> Android -> Enable ADB Integration) and then uiautomatorviewer will work fine. – Jade Apr 26 '16 at 16:44
3

You can try this tool that I created: https://github.com/nekocode/ResourceInspector

It can inspect all used layout files of current Activity.

user812786
  • 4,302
  • 5
  • 38
  • 50
nekocode
  • 31
  • 2
  • Looks interesting (you clearly put a lot of work into this)! I'll have to try it when I have some free time. – SMBiggs Apr 16 '18 at 15:28
2

Inspired by @nekocode ResourceInspector, I figure out how to show the layout resource name in the screenshot captured by AS's layout inspector. show the layout resource name in the screenshot captured by AS's layout inspector

Firstly create a class LayoutIndicatorInflater

public class LayoutIndicatorInflater extends LayoutInflater {

    private LayoutInflater mOriginalInflater;
    private String mAppPackageName;

    protected LayoutIndicatorInflater(LayoutInflater original, Context newContext) {
        super(original, newContext);
        mOriginalInflater = original;
        mAppPackageName = getContext().getPackageName();
    }

    @Override
    public LayoutInflater cloneInContext(Context newContext) {
        return new LayoutIndicatorInflater(mOriginalInflater.cloneInContext(newContext), newContext);
    }

    @Override
    public void setFactory(Factory factory) {
        super.setFactory(factory);
        mOriginalInflater.setFactory(factory);
    }

    @Override
    public void setFactory2(Factory2 factory) {
        super.setFactory2(factory);
        mOriginalInflater.setFactory2(factory);
    }

    @Override
    public View inflate(int resourceId, ViewGroup root, boolean attachToRoot) {
        Resources res = getContext().getResources();

        String packageName = "";
        try {
            packageName = res.getResourcePackageName(resourceId);
        } catch (Exception e) {}


        String resName = "";
        try {
            resName = res.getResourceEntryName(resourceId);
        } catch (Exception e) {}

        View view = mOriginalInflater.inflate(resourceId, root, attachToRoot);

        if (!mAppPackageName.equals(packageName)) {
            return view;
        }

        View targetView = view;
        if (root != null && attachToRoot) {
            targetView = root.getChildAt(root.getChildCount() - 1);
        }

        targetView.setContentDescription("layout res:" + resName);

        if (targetView instanceof ViewGroup) {
            ViewGroup viewGroup = (ViewGroup) targetView;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                View child = viewGroup.getChildAt(i);
                if (TextUtils.isEmpty(child.getContentDescription())) {
                    child.setContentDescription("layout res:" + resName);
                }
            }
        }

        return view;
    }
}

Then create a helper class

    public class LayoutIndicatorHelper {

    public static void init(Application application) {
        if (BuildConfig.DEBUG) {
            application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                @Override
                public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                    try {
                        Field inflaterField = ContextThemeWrapper.class.getDeclaredField("mInflater");
                        inflaterField.setAccessible(true);
                        LayoutInflater inflater = (LayoutInflater) inflaterField.get(activity);
                        LayoutInflater proxyInflater = null;
                        if (inflater != null) {
                            proxyInflater = new LayoutIndicatorInflater(inflater, activity);
                            inflaterField.set(activity, proxyInflater);
                        }

                        Class phoneWindowClass = Class.forName("com.android.internal.policy.PhoneWindow");
                        Field phoneWindowInflater = phoneWindowClass.getDeclaredField("mLayoutInflater");
                        phoneWindowInflater.setAccessible(true);
                        inflater = (LayoutInflater) phoneWindowInflater.get(activity.getWindow());
                        if (inflater != null && proxyInflater != null) {
                            phoneWindowInflater.set(activity.getWindow(), proxyInflater);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onActivityStarted(Activity activity) {

                }

                @Override
                public void onActivityResumed(Activity activity) {

                }

                @Override
                public void onActivityPaused(Activity activity) {

                }

                @Override
                public void onActivityStopped(Activity activity) {

                }

                @Override
                public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

                }

                @Override
                public void onActivityDestroyed(Activity activity) {

                }
            });
        }
    }

}

Finally call LayoutIndicatorHelper.init in your Application's onCreate

    public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        LayoutIndicatorHelper.init(this);
    }
}
shhp
  • 3,653
  • 2
  • 18
  • 23
  • Very interesting and straight-forward version of a custom LayoutInflator! And I applaud the work you've done. I will certainly take a look at this for future projects, but adding a level of abstraction isn't feasible for this project. – SMBiggs Jun 08 '18 at 03:31
2

In android 3.0 to onward, the Hierarchy Viewer tool is now deprecated. There is a standalone LayoutInspector. You can access it by clicking on tools from top bar of your Android Studio.

Nouman Ghaffar
  • 3,780
  • 1
  • 29
  • 37
1

Maybe it is a too late answer but different programmers can take advantage of my answer.

Facebook's Stetho library is wonderful tool to see inspects for running apps. Stetho library works like a Chrome extension and it will similar for many programmers.

Also to see HTTP request/responses are very easy by using Stetho. I absolutely advice every Android programmer try.

http://facebook.github.io/stetho/

Olkunmustafa
  • 3,103
  • 9
  • 42
  • 55