Max Nagl

Sort Drawables Build Script

posted in Utils on March 01, 2016

In many projects I got drawable files in the format ic_sun@1x.png, ic_sun@2x.png and ic_sun@3x.png. I created a script that automatically sorts the files into the drawables folders.

Usage

Copy the script below into your buildSrc/java folder and add the following line to your app/build.gradle file:

apply plugin: SortDrawablesPlugin

Then put the files you got from you designer in the main res folder and run the script:

gradlew.bat app:sortDrawables

Files that have the string @1x or _1x in their name will be stripped of this string and placed in th drawable-mdpi folder. Files with @2x will be placed in xhdpi, @3x goes to xxhdpi and so on.

The script will not override already existing files. To replace exiting files call gradlew.bat app:sortDrawablesOverride

Example

Files before running the script:

project\app\src\main\res\drawable-mdpi\ic_moon.png
project\app\src\main\res\drawable-xhdpi\ic_moon.png
project\app\src\main\res\drawable-xxhdpi\ic_moon.png
project\app\src\main\res\ic_sun@1.png
project\app\src\main\res\ic_sun@2.png
project\app\src\main\res\ic_sun@3.png

The script runs:

> gradle app:sortDrawables
:app:sortDrawables
Looking for images in project\app\src\main\res
Sort ic_sun@1x.png to: project\app\src\main\res\drawable-mdpi\ic_sun.png
Sort ic_sun@2x.png to: project\app\src\main\res\drawable-xhdpi\ic_sun.png
Sort ic_sun@3x.png to: project\app\src\main\res\drawable-xxhdpi\ic_sun.png

Files after running the script:

project\app\src\main\res\drawable-mdpi\ic_moon.png
project\app\src\main\res\drawable-mdpi\ic_sun.png
project\app\src\main\res\drawable-xhdpi\ic_moon.png
project\app\src\main\res\drawable-xhdpi\ic_sun.png
project\app\src\main\res\drawable-xxhdpi\ic_moon.png
project\app\src\main\res\drawable-xxhdpi\ic_sun.png

The Script

/*
 * Copyright 2016 Max Nagl
 *
 * 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.
 */

import org.gradle.api.Plugin
import org.gradle.api.Project

class SortDrawablesPlugin implements Plugin<Project> {
    def resolutions = ['0.75': 'mdpi', '1': 'mdpi', '1.5': 'hdpi', '2': 'xhdpi', '3': 'xxhdpi', '4': 'xxxhdpi'];
    def tags = ['@#x', '_#x'];

    void apply(Project project) {
        project.task('sortDrawables') << { sortDrawables(project, false); }
        project.task('sortDrawablesOverride') << { sortDrawables(project, true); }
    }

    public void sortDrawables(Project project, boolean override) {
        File resDir = new File(project.projectDir, "src/main/res");

        def tagsToFolders = [:];
        resolutions.each { resoltion, folder ->
            tags.each { tag ->
                tagsToFolders.put(tag.replace('#', resoltion), new File(resDir, 'drawable-' + folder));
            };
        };

        println "Looking for images in " + resDir.absolutePath;
        for (File file : resDir.listFiles()) {
            if (file.isDirectory()) continue;
            String name = file.getName().toLowerCase();
            if (file.getName().endsWith(".png") || file.getName().endsWith(".jpg") || file.getName().endsWith(".gif")) {
                File sortTo = null;
                tagsToFolders.each { tag, folder ->
                    if (name.contains((String) tag)) {
                        sortTo = new File((File) folder, name.replace((String) tag, ''));
                        return false;
                    }
                };
                if (!sortTo.parentFile.exists()) {
                    println "Create folder " + sortTo.parentFile;
                    sortTo.parentFile.mkdirs();
                }
                if (sortTo != null) {
                    println "Sort " + file.name + " to: " + sortTo;
                    if (sortTo.exists()) {
                        if (override) {
                            println "Overriding file: " + sortTo;
                            sortTo.delete();
                            file.renameTo(sortTo);
                        } else {
                            println "File already exists. Skipping: " + sortTo;
                        }
                    } else {
                        file.renameTo(sortTo);
                    }
                }
            }
        }
    }
}

Configuration

There are 6 predefined resolutions. Feel free to edit the following line to add more resolutions:

def resolutions = [..,'1': 'mdpi',..];

Many graphic editors output files in the format name@1x or name_1x. If your designer has another file naming convention you can add it in this line:

def tags = ['@#x', '_#x'];

The # character will be replaced by the resolution factor.