| /* |
| * Copyright 2012-present Facebook, Inc. |
| * |
| * 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.facebook.buck.android; |
| |
| import com.facebook.buck.step.ExecutionContext; |
| import com.facebook.buck.step.Step; |
| import com.facebook.buck.util.DirectoryTraversal; |
| import com.facebook.buck.util.FilteredDirectoryCopier; |
| import com.facebook.buck.util.Filters; |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Predicate; |
| import com.google.common.collect.ImmutableBiMap; |
| import com.google.common.collect.ImmutableSet; |
| |
| import java.io.File; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| /** |
| * This {@link com.facebook.buck.step.Step} copies {@code res} directories to a different location, while filtering out |
| * certain resources. |
| */ |
| public class FilterResourcesStep implements Step { |
| |
| private static final Pattern DRAWABLE_PATH_PATTERN = Pattern.compile( |
| ".*drawable.*/.*(png|jpg|jpeg|gif)", Pattern.CASE_INSENSITIVE); |
| |
| private final File baseDestination; |
| private final String resourceFilter; |
| private final FilteredDirectoryCopier filteredDirectoryCopier; |
| private final DrawableFinder drawableFinder; |
| private final ImmutableBiMap<String, String> originalToFiltered; |
| |
| /** |
| * Creates a command that filters a specified set of directories. |
| * @param resDirectories set of {@code res} directories to filter |
| * @param baseDestination destination directory, where copies of these directories will be placed |
| * (will be created if necessary) |
| * @param resourceFilter filtering to perform (currently based on density; allowed values are |
| * {@code "mdpi"}, {@code "hdpi"} and {@code "xhdpi"}) |
| */ |
| public FilterResourcesStep( |
| Set<String> resDirectories, |
| File baseDestination, |
| String resourceFilter, |
| FilteredDirectoryCopier filteredDirectoryCopier, |
| DrawableFinder drawableFinder) { |
| this.baseDestination = Preconditions.checkNotNull(baseDestination); |
| this.resourceFilter = Preconditions.checkNotNull(resourceFilter); |
| this.filteredDirectoryCopier = Preconditions.checkNotNull(filteredDirectoryCopier); |
| this.drawableFinder = Preconditions.checkNotNull(drawableFinder); |
| this.originalToFiltered = assignDestinations( |
| Preconditions.checkNotNull(resDirectories), |
| Preconditions.checkNotNull(baseDestination)); |
| } |
| |
| private static ImmutableBiMap<String, String> assignDestinations(Set<String> sources, File base) { |
| ImmutableBiMap.Builder<String, String> builder = ImmutableBiMap.builder(); |
| int count = 0; |
| for (String source : sources) { |
| builder.put(source, new File(base, String.valueOf(count++)).getAbsolutePath()); |
| } |
| return builder.build(); |
| } |
| |
| /** |
| * Returns drop-in set of resource directories for the rest of the build (e.g. {@code aapt}). |
| * @return set of directories which, after running {@link #execute(com.facebook.buck.step.ExecutionContext)}, |
| * will contain filtered copies of the resource directories passed on construction. |
| */ |
| public ImmutableSet<String> getFilteredResourceDirectories() { |
| // getFilteredResourceDirectories() yields matching destination directories in the same order |
| // as the sources, which is relevant to aapt. Adding |
| // return new TreeSet<String>(destinations); |
| // here would yield a broken app |
| return originalToFiltered.values(); |
| } |
| |
| @Override |
| public int execute(ExecutionContext context) { |
| // Get list of candidate drawables. |
| Set<String> drawables = drawableFinder.findDrawables(originalToFiltered.keySet()); |
| // Create a filter that removes drawables not of desired density. |
| Predicate<File> densityFilter = Filters.createImageDensityFilter(drawables, resourceFilter); |
| // Create filtered copies of all resource directories. These will be passed to aapt instead. |
| filteredDirectoryCopier.copyDirs(originalToFiltered, |
| densityFilter, |
| context.getProcessExecutor()); |
| return 0; |
| } |
| |
| @Override |
| public String getShortName(ExecutionContext context) { |
| return "resource filtering"; |
| } |
| |
| @Override |
| public String getDescription(ExecutionContext context) { |
| return String.format( |
| "Filtering %d resource directories into: %s", |
| originalToFiltered.size(), |
| baseDestination); |
| } |
| |
| public interface DrawableFinder { |
| public Set<String> findDrawables(Iterable<String> dirs); |
| } |
| |
| public static class DefaultDrawableFinder implements DrawableFinder { |
| |
| private static final DefaultDrawableFinder instance = new DefaultDrawableFinder(); |
| |
| public static DefaultDrawableFinder getInstance() { |
| return instance; |
| } |
| |
| @Override |
| public Set<String> findDrawables(Iterable<String> dirs) { |
| final ImmutableSet.Builder<String> drawableBuilder = ImmutableSet.builder(); |
| for (String dir : dirs) { |
| new DirectoryTraversal(new File(dir)) { |
| @Override |
| public void visit(File file, String relativePath) { |
| if (DRAWABLE_PATH_PATTERN.matcher(relativePath).matches()) { |
| drawableBuilder.add(file.getPath()); |
| } |
| } |
| }.traverse(); |
| } |
| return drawableBuilder.build(); |
| } |
| } |
| } |