| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.tools.lint.checks |
| |
| import com.android.SdkConstants.ANDROID_URI |
| import com.android.SdkConstants.ATTR_CONTENT_DESCRIPTION |
| import com.android.SdkConstants.ATTR_HINT |
| import com.android.SdkConstants.ATTR_LABEL |
| import com.android.SdkConstants.ATTR_PROMPT |
| import com.android.SdkConstants.ATTR_TEXT |
| import com.android.SdkConstants.ATTR_TITLE |
| import com.android.resources.ResourceFolderType |
| import com.android.tools.lint.checks.RestrictionsDetector.Companion.ATTR_DESCRIPTION |
| import com.android.tools.lint.checks.RestrictionsDetector.Companion.TAG_RESTRICTIONS |
| import com.android.tools.lint.detector.api.Category |
| import com.android.tools.lint.detector.api.Implementation |
| import com.android.tools.lint.detector.api.Issue |
| import com.android.tools.lint.detector.api.LayoutDetector |
| import com.android.tools.lint.detector.api.Scope |
| import com.android.tools.lint.detector.api.Severity |
| import com.android.tools.lint.detector.api.XmlContext |
| import org.w3c.dom.Attr |
| import java.util.Arrays |
| |
| /** |
| * Check which looks at the children of ScrollViews and ensures that they fill/match the parent |
| * width instead of setting wrap_content. |
| * |
| * |
| * TODO: Consider looking at the localization="suggested" attribute in the platform attrs.xml to |
| * catch future recommended attributes. |
| */ |
| class HardcodedValuesDetector : LayoutDetector() { |
| |
| override fun getApplicableAttributes(): Collection<String>? { |
| return Arrays.asList( |
| // Layouts |
| ATTR_TEXT, |
| ATTR_CONTENT_DESCRIPTION, |
| ATTR_HINT, |
| ATTR_LABEL, |
| ATTR_PROMPT, |
| "textOn", |
| "textOff", |
| |
| // Menus |
| ATTR_TITLE, |
| |
| // App restrictions |
| ATTR_DESCRIPTION |
| ) |
| } |
| |
| override fun appliesTo(folderType: ResourceFolderType): Boolean { |
| return (folderType == ResourceFolderType.LAYOUT || |
| folderType == ResourceFolderType.MENU || |
| folderType == ResourceFolderType.XML) |
| } |
| |
| override fun visitAttribute(context: XmlContext, attribute: Attr) { |
| val value = attribute.value |
| if (!value.isEmpty() && value[0] != '@' && value[0] != '?') { |
| // Make sure this is really one of the android: attributes |
| if (ANDROID_URI != attribute.namespaceURI) { |
| return |
| } |
| |
| // Filter out a few special cases: |
| |
| if (value == "Hello World!") { |
| // This is the default text in new templates. Users are unlikely to |
| // leave this in, so let's not add warnings in the editor as their |
| // welcome to Android development greeting. |
| return |
| } |
| if (value == "Large Text" || |
| value == "Medium Text" || |
| value == "Small Text" || |
| value.startsWith("New ") && (value == "New Text" || value == "New " + attribute.ownerElement.tagName) |
| ) { |
| // The layout editor initially places the label "New Button", "New TextView", |
| // etc on widgets dropped on the layout editor. Again, users are unlikely |
| // to leave it that way, so let's not flag it until they change it. |
| return |
| } |
| |
| // In XML folders, currently only checking application restriction files |
| // (since in general the res/xml folder can contain arbitrary XML content |
| // interpreted by the app) |
| if (context.resourceFolderType == ResourceFolderType.XML) { |
| val tagName = attribute.ownerDocument.documentElement.tagName |
| if (tagName != TAG_RESTRICTIONS) { |
| return |
| } |
| } |
| |
| context.report( |
| ISSUE, |
| attribute, |
| context.getLocation(attribute), |
| String.format( |
| "Hardcoded string \"%1\$s\", should use `@string` resource", value |
| ) |
| ) |
| } |
| } |
| |
| companion object { |
| // TODO: Add additional issues here, such as hardcoded colors, hardcoded sizes, etc |
| |
| /** The main issue discovered by this detector */ |
| @JvmField |
| val ISSUE = Issue.create( |
| id = "HardcodedText", |
| briefDescription = "Hardcoded text", |
| explanation = """ |
| Hardcoding text attributes directly in layout files is bad for several reasons: |
| |
| * When creating configuration variations (for example for landscape or \ |
| portrait) you have to repeat the actual text (and keep it up to date when \ |
| making changes) |
| |
| * The application cannot be translated to other languages by just adding new \ |
| translations for existing string resources. |
| |
| There are quickfixes to automatically extract this hardcoded string into a \ |
| resource lookup. |
| """, |
| category = Category.I18N, |
| priority = 5, |
| severity = Severity.WARNING, |
| implementation = Implementation( |
| HardcodedValuesDetector::class.java, |
| Scope.RESOURCE_FILE_SCOPE |
| ) |
| ) |
| } |
| } |