Exemple #1
0
  @Override
  public boolean isIgnored(
      @NonNull Context context,
      @NonNull Issue issue,
      @Nullable Location location,
      @NonNull String message,
      @Nullable Object data) {
    ensureInitialized();

    String id = issue.getId();
    List<String> paths = mSuppressed.get(id);
    if (paths == null) {
      paths = mSuppressed.get(VALUE_ALL);
    }
    if (paths != null && location != null) {
      File file = location.getFile();
      String relativePath = context.getProject().getRelativePath(file);
      for (String suppressedPath : paths) {
        if (suppressedPath.equals(relativePath)) {
          return true;
        }
        // Also allow a prefix
        if (relativePath.startsWith(suppressedPath)) {
          return true;
        }
      }
    }

    if (mRegexps != null) {
      List<Pattern> regexps = mRegexps.get(id);
      if (regexps == null) {
        regexps = mRegexps.get(VALUE_ALL);
      }
      if (regexps != null && location != null) {
        File file = location.getFile();
        String relativePath = context.getProject().getRelativePath(file);
        boolean checkUnixPath = false;
        for (Pattern regexp : regexps) {
          Matcher matcher = regexp.matcher(relativePath);
          if (matcher.find()) {
            return true;
          } else if (regexp.pattern().indexOf('/') != -1) {
            checkUnixPath = true;
          }
        }

        if (checkUnixPath && CURRENT_PLATFORM == PLATFORM_WINDOWS) {
          relativePath = relativePath.replace('\\', '/');
          for (Pattern regexp : regexps) {
            Matcher matcher = regexp.matcher(relativePath);
            if (matcher.find()) {
              return true;
            }
          }
        }
      }
    }

    return mParent != null && mParent.isIgnored(context, issue, location, message, data);
  }
  @Override
  public void afterCheckProject(Context context) {
    // Make sure no
    File res = new File(context.getProject().getDir(), RES_FOLDER);
    if (res.isDirectory()) {
      File[] folders = res.listFiles();
      if (folders != null) {
        boolean checkFolders =
            context.isEnabled(ICON_DENSITIES)
                || context.isEnabled(ICON_MISSING_FOLDER)
                || context.isEnabled(ICON_NODPI);
        boolean checkDipSizes = context.isEnabled(ICON_DIP_SIZE);
        boolean checkDuplicates =
            context.isEnabled(DUPLICATES_NAMES) || context.isEnabled(DUPLICATES_CONFIGURATIONS);

        Map<File, Dimension> pixelSizes = null;
        Map<File, Long> fileSizes = null;
        if (checkDipSizes || checkDuplicates) {
          pixelSizes = new HashMap<File, Dimension>();
          fileSizes = new HashMap<File, Long>();
        }
        Map<File, Set<String>> folderToNames = new HashMap<File, Set<String>>();
        for (File folder : folders) {
          String folderName = folder.getName();
          if (folderName.startsWith(DRAWABLE_FOLDER)) {
            File[] files = folder.listFiles();
            if (files != null) {
              checkDrawableDir(context, folder, files, pixelSizes, fileSizes);

              if (checkFolders && DENSITY_PATTERN.matcher(folderName).matches()) {
                Set<String> names = new HashSet<String>(files.length);
                for (File f : files) {
                  String name = f.getName();
                  if (hasBitmapExtension(name)) {
                    names.add(f.getName());
                  }
                }
                folderToNames.put(folder, names);
              }
            }
          }
        }

        if (checkDipSizes) {
          checkDipSizes(context, pixelSizes);
        }

        if (checkDuplicates) {
          checkDuplicates(context, pixelSizes, fileSizes);
        }

        if (checkFolders && folderToNames.size() > 0) {
          checkDensities(context, res, folderToNames);
        }
      }
    }
  }
  /**
   * Is this drawable folder for an Android 2.3 drawable? This will be the case if it specifies -v9
   * or -v10, or if the minimum SDK version declared in the manifest is 9 or 10 (and it does not
   * specify some higher version like -v11
   */
  private boolean isAndroid23(Context context, int folderVersion) {
    if (isAndroid30(context, folderVersion)) {
      return false;
    }

    if (folderVersion == 9 || folderVersion == 10) {
      return true;
    }

    int minSdk = context.getProject().getMinSdk();

    return minSdk == 9 || minSdk == 10;
  }
  private void checkDensities(Context context, File res, Map<File, Set<String>> folderToNames) {
    // TODO: Is there a way to look at the manifest and figure out whether
    // all densities are expected to be needed?
    // Note: ldpi is probably not needed; it has very little usage
    // (about 2%; http://developer.android.com/resources/dashboard/screens.html)
    // TODO: Use the matrix to check out if we can eliminate densities based
    // on the target screens?

    Set<String> definedDensities = new HashSet<String>();
    for (File f : folderToNames.keySet()) {
      definedDensities.add(f.getName());
    }

    // Look for missing folders -- if you define say drawable-mdpi then you
    // should also define -hdpi and -xhdpi.
    if (context.isEnabled(ICON_MISSING_FOLDER)) {
      List<String> missing = new ArrayList<String>();
      for (String density : REQUIRED_DENSITIES) {
        if (!definedDensities.contains(density)) {
          missing.add(density);
        }
      }
      if (missing.size() > 0) {
        context.report(
            ICON_MISSING_FOLDER,
            null /* location */,
            String.format(
                "Missing density variation folders in %1$s: %2$s",
                context.getProject().getDisplayPath(res),
                LintUtils.formatList(missing, missing.size())),
            null);
      }
    }

    if (context.isEnabled(ICON_NODPI)) {
      Set<String> noDpiNames = new HashSet<String>();
      for (Map.Entry<File, Set<String>> entry : folderToNames.entrySet()) {
        if (isNoDpiFolder(entry.getKey())) {
          noDpiNames.addAll(entry.getValue());
        }
      }
      if (noDpiNames.size() > 0) {
        // Make sure that none of the nodpi names appear in a non-nodpi folder
        Set<String> inBoth = new HashSet<String>();
        List<File> files = new ArrayList<File>();
        for (Map.Entry<File, Set<String>> entry : folderToNames.entrySet()) {
          File folder = entry.getKey();
          String folderName = folder.getName();
          if (!isNoDpiFolder(folder)) {
            assert DENSITY_PATTERN.matcher(folderName).matches();
            Set<String> overlap = intersection(noDpiNames, entry.getValue());
            inBoth.addAll(overlap);
            for (String name : overlap) {
              files.add(new File(folder, name));
            }
          }
        }

        if (inBoth.size() > 0) {
          List<String> list = new ArrayList<String>(inBoth);
          Collections.sort(list);

          // Chain locations together
          Collections.sort(files);
          Location location = null;
          for (File file : files) {
            Location linkedLocation = location;
            location = Location.create(file);
            location.setSecondary(linkedLocation);
          }

          context.report(
              ICON_DENSITIES,
              location,
              String.format(
                  "The following images appear in both -nodpi and in a density folder: %1$s",
                  LintUtils.formatList(list, 10)),
              null);
        }
      }
    }

    if (context.isEnabled(ICON_DENSITIES)) {
      // Look for folders missing some of the specific assets
      Set<String> allNames = new HashSet<String>();
      for (Entry<File, Set<String>> entry : folderToNames.entrySet()) {
        if (!isNoDpiFolder(entry.getKey())) {
          Set<String> names = entry.getValue();
          allNames.addAll(names);
        }
      }

      for (Map.Entry<File, Set<String>> entry : folderToNames.entrySet()) {
        File file = entry.getKey();
        if (isNoDpiFolder(file)) {
          continue;
        }
        Set<String> names = entry.getValue();
        if (names.size() != allNames.size()) {
          List<String> delta = new ArrayList<String>(difference(allNames, names));
          Collections.sort(delta);
          String foundIn = "";
          if (delta.size() == 1) {
            // Produce list of where the icon is actually defined
            List<String> defined = new ArrayList<String>();
            String name = delta.get(0);
            for (Map.Entry<File, Set<String>> e : folderToNames.entrySet()) {
              if (e.getValue().contains(name)) {
                defined.add(e.getKey().getName());
              }
            }
            if (defined.size() > 0) {
              foundIn = String.format(" (found in %1$s)", LintUtils.formatList(defined, 5));
            }
          }

          context.report(
              ICON_DENSITIES,
              Location.create(file),
              String.format(
                  "Missing the following drawables in %1$s: %2$s%3$s",
                  file.getName(), LintUtils.formatList(delta, 5), foundIn),
              null);
        }
      }
    }
  }
 /**
  * Is this drawable folder for an Android 3.0 drawable? This will be the case if it specifies
  * -v11+, or if the minimum SDK version declared in the manifest is at least 11.
  */
 private boolean isAndroid30(Context context, int folderVersion) {
   return folderVersion >= 11 || context.getProject().getMinSdk() >= 11;
 }