1a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye/*
2a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Copyright (C) 2011 The Android Open Source Project
3a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *
4a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Licensed under the Apache License, Version 2.0 (the "License");
5a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * you may not use this file except in compliance with the License.
6a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * You may obtain a copy of the License at
7a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *
8a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *      http://www.apache.org/licenses/LICENSE-2.0
9a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye *
10a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Unless required by applicable law or agreed to in writing, software
11a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * distributed under the License is distributed on an "AS IS" BASIS,
12a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * See the License for the specific language governing permissions and
14a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * limitations under the License.
15a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye */
16a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
17a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyepackage com.android.tools.lint.checks;
18a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
1912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ABSOLUTE_LAYOUT;
2012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ANDROID_URI;
2112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_BACKGROUND;
2212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_ID;
2312d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.ATTR_STYLE;
2412d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.FRAME_LAYOUT;
2512d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.GRID_LAYOUT;
2612d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.GRID_VIEW;
2712d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.HORIZONTAL_SCROLL_VIEW;
2812d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.LINEAR_LAYOUT;
2912d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.VIEW_MERGE;
3012d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.RADIO_GROUP;
3112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.RELATIVE_LAYOUT;
3212d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.SCROLL_VIEW;
3312d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.TABLE_LAYOUT;
3412d4581faa6438941e65a9dc83213be34c6ca970Tor Norbyeimport static com.android.SdkConstants.TABLE_ROW;
35229581314076be1b6f82fe1efed2bd00da340899Tor Norbye
367e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbyeimport com.android.annotations.NonNull;
37229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.detector.api.Category;
38a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Issue;
39a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.LayoutDetector;
40229581314076be1b6f82fe1efed2bd00da340899Tor Norbyeimport com.android.tools.lint.detector.api.LintUtils;
41a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Location;
42a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Scope;
43a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Severity;
44a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport com.android.tools.lint.detector.api.Speed;
453ce45b249f898697ae82e8c6dd045966227f3438Tor Norbyeimport com.android.tools.lint.detector.api.XmlContext;
46a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
47a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.w3c.dom.Element;
48a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport org.w3c.dom.Node;
49a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
50a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport java.util.ArrayList;
51a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport java.util.Collection;
52a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyeimport java.util.List;
53a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
54a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye/**
55a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye * Checks whether the current node can be removed without affecting the layout.
56a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye */
57a85107f7d763276a5a040cf68e2046ac54202015Tor Norbyepublic class UselessViewDetector extends LayoutDetector {
58a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /** Issue of including a parent that has no value on its own */
59a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    public static final Issue USELESS_PARENT = Issue.create(
60a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "UselessParent", //$NON-NLS-1$
61a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "Checks whether a parent layout can be removed.",
62a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "A layout with children that has no siblings, is not a scrollview or " +
63a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "a root layout, and does not have a background, can be removed and have " +
64a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "its children moved directly into the parent for a flatter and more " +
65a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "efficient layout hierarchy.",
66229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            Category.PERFORMANCE,
67229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            2,
68229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            Severity.WARNING,
69229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            UselessViewDetector.class,
70229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            Scope.RESOURCE_FILE_SCOPE);
71a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
72a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /** Issue of including a leaf that isn't shown */
73a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    public static final Issue USELESS_LEAF = Issue.create(
74a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "UselessLeaf", //$NON-NLS-1$
75a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "Checks whether a leaf layout can be removed.",
76a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "A layout that has no children or no background can often be removed (since it " +
77a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            "is invisible) for a flatter and more efficient layout hierarchy.",
78229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            Category.PERFORMANCE,
79229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            2,
80229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            Severity.WARNING,
81229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            UselessViewDetector.class,
82229581314076be1b6f82fe1efed2bd00da340899Tor Norbye            Scope.RESOURCE_FILE_SCOPE);
83a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
84a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    /** Constructs a new {@link UselessViewDetector} */
85a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    public UselessViewDetector() {
86a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
87a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
88a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    @Override
897e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public @NonNull Speed getSpeed() {
90a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        return Speed.FAST;
91a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
92a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
93e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye    private static final List<String> CONTAINERS = new ArrayList<String>(18);
94a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    static {
95e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        CONTAINERS.add(ABSOLUTE_LAYOUT);
96a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add(FRAME_LAYOUT);
97e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        CONTAINERS.add(GRID_LAYOUT);
98a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add(GRID_VIEW);
99a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add(HORIZONTAL_SCROLL_VIEW);
100a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add("ImageSwitcher");                      //$NON-NLS-1$
101a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add(LINEAR_LAYOUT);
102e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        CONTAINERS.add(RADIO_GROUP);
103e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        CONTAINERS.add(RELATIVE_LAYOUT);
104a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add(SCROLL_VIEW);
105a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add("SlidingDrawer");                      //$NON-NLS-1$
106a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add("StackView");                          //$NON-NLS-1$
107e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        CONTAINERS.add(TABLE_LAYOUT);
108e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        CONTAINERS.add(TABLE_ROW);
109a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add("TextSwitcher");                       //$NON-NLS-1$
110a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add("ViewAnimator");                       //$NON-NLS-1$
111a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add("ViewFlipper");                        //$NON-NLS-1$
112a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        CONTAINERS.add("ViewSwitcher");                       //$NON-NLS-1$
113a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // Available ViewGroups that are not included by this check:
114e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        //  CONTAINERS.add("android.gesture.GestureOverlayView");
115a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("AdapterViewFlipper");
116a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("DialerFilter");
117a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("ExpandableListView");
118a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("ListView");
119a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("MediaController");
120a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("merge");
121a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("SearchView");
122a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //  CONTAINERS.add("TabWidget");
123e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        //  CONTAINERS.add("TabHost");
124a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
125a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    @Override
126a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    public Collection<String> getApplicableElements() {
127a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        return CONTAINERS;
128a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
129a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
130a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    @Override
1317e4b8e9d595e45baa9d87cdb8282f02759e73abcTor Norbye    public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
132229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        int childCount = LintUtils.getChildCount(element);
133a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (childCount == 0) {
134a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            // Check to see if this is a leaf layout that can be removed
135a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            checkUselessLeaf(context, element);
136a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        } else {
137a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            // Check to see if this is a middle-man layout which can be removed
138a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            checkUselessMiddleLayout(context, element);
139a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
140a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
141a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
142a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    // This is the old UselessLayoutCheck from layoutopt
1433ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye    private void checkUselessMiddleLayout(XmlContext context, Element element) {
144a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // Conditions:
145a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node has children
146a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node does not have siblings
147a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node's parent is not a scroll view (horizontal or vertical)
148a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node does not have a background or its parent does not have a
149a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        //   background or neither the node and its parent have a background
150a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The parent is not a <merge/>
151a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
152a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        Node parentNode = element.getParentNode();
153a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (parentNode.getNodeType() != Node.ELEMENT_NODE) {
154a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            // Can't remove root
155a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            return;
156a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
157a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
158a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        Element parent = (Element) parentNode;
159a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        String parentTag = parent.getTagName();
160a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (parentTag.equals(SCROLL_VIEW) || parentTag.equals(HORIZONTAL_SCROLL_VIEW) ||
16112d4581faa6438941e65a9dc83213be34c6ca970Tor Norbye                parentTag.equals(VIEW_MERGE)) {
162a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            // Can't remove if the parent is a scroll view or a merge
163a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            return;
164a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
165a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
166a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // This method is only called when we've already ensured that it has children
167229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        assert LintUtils.getChildCount(element) > 0;
168a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
169229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        int parentChildCount = LintUtils.getChildCount(parent);
170a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (parentChildCount != 1) {
171a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            // Don't remove if the node has siblings
172a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            return;
173a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
174a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
175699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        // - A parent can be removed if it doesn't have a background
176699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        // - A parent can be removed if has a background *and* the child does not have a
177699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        //   background (in which case, just move the background over to the child, remove
178699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        //   the parent)
179699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        // - If both child and parent have a background, the parent cannot be removed (a
180699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        //   background can be translucent, have transparent padding, etc.)
181a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        boolean nodeHasBackground = element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND);
182a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        boolean parentHasBackground = parent.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND);
183699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        if (nodeHasBackground && parentHasBackground) {
184699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye            // Can't remove because both define a background, and they might both be
185699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye            // visible (e.g. through transparency or padding).
186699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye            return;
187699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        }
188699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye
189e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        // Certain parents are special - such as the TabHost and the GestureOverlayView -
190e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        // where we want to leave things alone.
191e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        if (!CONTAINERS.contains(parentTag)) {
192e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye            return;
193e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye        }
194e61a2749bf3d2509fc6b3472f07f0ecf695d2173Tor Norbye
195699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        boolean hasId = element.hasAttributeNS(ANDROID_URI, ATTR_ID);
196699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        Location location = context.getLocation(element);
197699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        String tag = element.getTagName();
198699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        String format;
199699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        if (hasId) {
200699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye            format = "This %1$s layout or its %2$s parent is possibly useless";
201699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        } else {
202699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye            format = "This %1$s layout or its %2$s parent is useless";
203699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        }
204699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        if (nodeHasBackground || parentHasBackground) {
205699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye            format += "; transfer the background attribute to the other view";
206a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
207699dcc5fd8a93fcc3842954af35be7f7be793740Tor Norbye        String message = String.format(format, tag, parentTag);
20869067f399231dc28f4ff0aa02b60153ffd2d5831Tor Norbye        context.report(USELESS_PARENT, element, location, message, null);
209a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
210a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
211a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    // This is the old UselessView check from layoutopt
2123ce45b249f898697ae82e8c6dd045966227f3438Tor Norbye    private void checkUselessLeaf(XmlContext context, Element element) {
213229581314076be1b6f82fe1efed2bd00da340899Tor Norbye        assert LintUtils.getChildCount(element) == 0;
214a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
215a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // Conditions:
216a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node is a container view (LinearLayout, etc.)
217a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node has no id
218a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node has no background
219a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        // - The node has no children
2205fc3da5bd6c546bc962cc8d9348ce631d44d6d4cTor Norbye        // - The node has no style
221be7c3d9ef5a346323faee9d6663bd2d19a6ce0a2Tor Norbye        // - The node is not a root
222a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
223a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (element.hasAttributeNS(ANDROID_URI, ATTR_ID)) {
224a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            return;
225a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
226a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
227a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        if (element.hasAttributeNS(ANDROID_URI, ATTR_BACKGROUND)) {
228a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye            return;
229a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        }
230a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye
2315fc3da5bd6c546bc962cc8d9348ce631d44d6d4cTor Norbye        if (element.hasAttribute(ATTR_STYLE)) {
2325fc3da5bd6c546bc962cc8d9348ce631d44d6d4cTor Norbye            return;
2335fc3da5bd6c546bc962cc8d9348ce631d44d6d4cTor Norbye        }
2345fc3da5bd6c546bc962cc8d9348ce631d44d6d4cTor Norbye
235be7c3d9ef5a346323faee9d6663bd2d19a6ce0a2Tor Norbye        if (element == context.document.getDocumentElement()) {
236be7c3d9ef5a346323faee9d6663bd2d19a6ce0a2Tor Norbye            return;
237be7c3d9ef5a346323faee9d6663bd2d19a6ce0a2Tor Norbye        }
238be7c3d9ef5a346323faee9d6663bd2d19a6ce0a2Tor Norbye
239a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        Location location = context.getLocation(element);
240a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        String tag = element.getTagName();
241a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye        String message = String.format(
2425fc3da5bd6c546bc962cc8d9348ce631d44d6d4cTor Norbye                "This %1$s view is useless (no children, no background, no id, no style)", tag);
24369067f399231dc28f4ff0aa02b60153ffd2d5831Tor Norbye        context.report(USELESS_LEAF, element, location, message, null);
244a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye    }
245a85107f7d763276a5a040cf68e2046ac54202015Tor Norbye}
246