All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.tools.lint.checks.VectorDrawableCompatDetector Maven / Gradle / Ivy

There is a newer version: 25.3.0
Show newest version
/*
 * Copyright (C) 2016 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 static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_SRC;
import static com.android.SdkConstants.ATTR_SRC_COMPAT;
import static com.android.SdkConstants.AUTO_URI;
import static com.android.SdkConstants.TAG_VECTOR;

import com.android.SdkConstants;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.builder.model.AndroidProject;
import com.android.builder.model.Variant;
import com.android.ide.common.repository.GradleVersion;
import com.android.ide.common.res2.AbstractResourceRepository;
import com.android.ide.common.res2.ResourceFile;
import com.android.ide.common.res2.ResourceItem;
import com.android.ide.common.resources.ResourceUrl;
import com.android.resources.ResourceFolderType;
import com.android.resources.ResourceType;
import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Category;
import com.android.tools.lint.detector.api.Context;
import com.android.tools.lint.detector.api.Implementation;
import com.android.tools.lint.detector.api.Issue;
import com.android.tools.lint.detector.api.LintUtils;
import com.android.tools.lint.detector.api.Location;
import com.android.tools.lint.detector.api.ResourceXmlDetector;
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 com.android.utils.XmlUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;

/**
 * Finds all the vector drawables and checks references to them in layouts.
 *
 * 

This detector looks for common mistakes related to AppCompat support for vector drawables, * that is: *

    *
  • Using app:srcCompat without useSupportLibrary in build.gradle *
  • Using android:src with useSupportLibrary in build.gradle *
*/ public class VectorDrawableCompatDetector extends ResourceXmlDetector { /** The main issue discovered by this detector */ public static final Issue ISSUE = Issue.create( "VectorDrawableCompat", //$NON-NLS-1$ "Using VectorDrawableCompat", "To use VectorDrawableCompat, you need to make two modifications to your project. " + "First, set `android.defaultConfig.vectorDrawables.useSupportLibrary = true` " + "in your `build.gradle` file, " + "and second, use `app:srcCompat` instead of `android:src` to refer to vector " + "drawables.", Category.CORRECTNESS, 5, Severity.ERROR, new Implementation( VectorDrawableCompatDetector.class, Scope.ALL_RESOURCES_SCOPE, Scope.RESOURCE_FILE_SCOPE)) .addMoreInfo("http://chris.banes.me/2016/02/25/appcompat-vector/#enabling-the-flag"); /** Whether to skip the checks altogether. */ private boolean mSkipChecks; /** All vector drawables in the project. */ private Set mVectors = Sets.newHashSet(); /** Whether the project uses AppCompat for vectors. */ private boolean mUseSupportLibrary; @Override public void beforeCheckProject(@NonNull Context context) { AndroidProject model = context.getProject().getGradleProjectModel(); if (model == null) { mSkipChecks = true; return; } if (context.getProject().getMinSdk() >= 21) { mSkipChecks = true; return; } GradleVersion version = context.getProject().getGradleModelVersion(); if (version == null || version.getMajor() < 2) { mSkipChecks = true; return; } Variant currentVariant = context.getProject().getCurrentVariant(); if (currentVariant == null) { mSkipChecks = true; return; } if (Boolean.TRUE.equals( currentVariant.getMergedFlavor().getVectorDrawables().getUseSupportLibrary())) { mUseSupportLibrary = true; } } @Override public boolean appliesTo(@NonNull ResourceFolderType folderType) { //noinspection SimplifiableIfStatement if (mSkipChecks) { return false; } return folderType == ResourceFolderType.DRAWABLE || folderType == ResourceFolderType.LAYOUT; } /** * Saves names of all vector resources encountered. Because "drawable" is before "layout" in * alphabetical order, Lint will first call this on every vector, before calling * {@link #visitAttribute(XmlContext, Attr)} on every attribute. */ @Override public void visitElement(@NonNull XmlContext context, @NonNull Element element) { if (mSkipChecks) { return; } String resourceName = LintUtils.getBaseName(context.file.getName()); mVectors.add(resourceName); } @Nullable @Override public Collection getApplicableElements() { return mSkipChecks ? null : Collections.singletonList(TAG_VECTOR); } @Nullable @Override public Collection getApplicableAttributes() { return mSkipChecks ? null : ImmutableList.of(ATTR_SRC, ATTR_SRC_COMPAT); } @Override public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { if (mSkipChecks) { return; } boolean incrementalMode = !context.getDriver().getScope().contains(Scope.ALL_RESOURCE_FILES); if (!incrementalMode && mVectors.isEmpty()) { return; } Predicate isVector; if (!incrementalMode) { // TODO: Always use resources, once command-line client supports it. isVector = mVectors::contains; } else { LintClient client = context.getClient(); AbstractResourceRepository resources = client.getResourceRepository( context.getMainProject(), true, false); if (resources == null) { // We only run on a single layout file, but have no access to the resources // database, there's no way we can perform the check. return; } isVector = name -> checkResourceRepository(resources, name); } String name = attribute.getLocalName(); String namespace = attribute.getNamespaceURI(); if ((ATTR_SRC.equals(name) && !ANDROID_URI.equals(namespace) || (ATTR_SRC_COMPAT.equals(name) && !AUTO_URI.equals(namespace)))) { // Not the attribute we are looking for. return; } ResourceUrl resourceUrl = ResourceUrl.parse(attribute.getValue()); if (resourceUrl == null) { return; } if (mUseSupportLibrary && ATTR_SRC.equals(name) && isVector.test(resourceUrl.name)) { Location location = context.getNameLocation(attribute); String message = "When using VectorDrawableCompat, you need to use `app:srcCompat`."; context.report(ISSUE, attribute, location, message); } if (!mUseSupportLibrary && ATTR_SRC_COMPAT.equals(name) && isVector.test(resourceUrl.name)) { Location location = context.getNameLocation(attribute); String message = "To use VectorDrawableCompat, you need to set " + "`android.defaultConfig.vectorDrawables.useSupportLibrary = true`."; context.report(ISSUE, attribute, location, message); } } private static boolean checkResourceRepository( @NonNull AbstractResourceRepository resources, @NonNull String name) { List items = resources.getResourceItem(ResourceType.DRAWABLE, name); if (items == null) { return false; } // Check if at least one drawable with this name is a vector. for (ResourceItem item : items) { ResourceFile source = item.getSource(); if (source == null) { return false; } File file = source.getFile(); if (!file.getPath().endsWith(SdkConstants.DOT_XML)) { continue; } return SdkConstants.TAG_VECTOR.equals(XmlUtils.getRootTagName(file)); } return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy