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