├── .gitignore ├── code ├── resources │ └── icon.png ├── project.clj └── src │ └── fred │ ├── image_grabber.clj │ ├── image_saver.clj │ ├── image_resizer.clj │ └── core.clj ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | lib/ 4 | classes/ 5 | .lein-failures 6 | .lein-deps-sum 7 | -------------------------------------------------------------------------------- /code/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leocadiotine/fred/HEAD/code/resources/icon.png -------------------------------------------------------------------------------- /code/project.clj: -------------------------------------------------------------------------------- 1 | (defproject fred "1.1.0-SNAPSHOT" 2 | :description "A tool to resize images to the various screen densities of Android devices." 3 | :dependencies[ 4 | [org.clojure/clojure "1.2.1"] 5 | [fs "1.1.2"] 6 | [org.imgscalr/imgscalr-lib "4.2"] 7 | [seesaw "1.4.3"]] 8 | :main fred.core) -------------------------------------------------------------------------------- /code/src/fred/image_grabber.clj: -------------------------------------------------------------------------------- 1 | (ns fred.image-grabber 2 | (:require [fs.core :as fs] 3 | [clojure.string :as s])) 4 | 5 | (def img-exts #{".jpg" ".png" ".gif"}) 6 | 7 | (defn is-image? 8 | "Returns whether the desired file corresponds to an image." 9 | [file] 10 | (let [ext (fs/extension file)] 11 | (and ext (img-exts (s/lower-case ext))))) 12 | 13 | (defn get-images 14 | "Returns a list of image files on a certain folder." 15 | [folder] 16 | (filter is-image? (fs/list-dir folder))) 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Leocadio Tiné 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /code/src/fred/image_saver.clj: -------------------------------------------------------------------------------- 1 | (ns fred.image-saver 2 | (:require [fs.core :as fs])) 3 | 4 | (defn substitute [s substitution-map] 5 | (reduce (fn [s [match replacement]] 6 | (clojure.string/replace s match replacement)) 7 | s substitution-map)) 8 | 9 | (defn rename-droid 10 | "Returns a string that matches Android's resource naming requirements: 11 | all lowercase, replaces '-' and whitespaces with '_' and removes @2x." 12 | [old-name] 13 | (substitute (clojure.string/lower-case old-name) {" " "_" 14 | "-" "_" 15 | "@2x" ""})) 16 | 17 | (defn copy-to-dir-and-rename 18 | "Moves a list of files from the given folder to the given target directory. 19 | Also, renames all the files to match Android's resource naming requirements: 20 | all lowercase, replaces '-' and whitespaces with '_' and removes @2x." 21 | [file-names files-path target-directory-name] 22 | (doseq [filename file-names] 23 | (fs/copy+ (str files-path filename) 24 | (str target-directory-name 25 | (rename-droid filename))))) 26 | 27 | (defn clone-dir 28 | "Makes several copies of original-path. root is the root folder and all the other 29 | arguments as folders relative to it." 30 | [root-folder original-path & new-paths] 31 | (doseq [new-path new-paths] 32 | (fs/copy-dir (str root-folder original-path) (str root-folder new-path)))) 33 | -------------------------------------------------------------------------------- /code/src/fred/image_resizer.clj: -------------------------------------------------------------------------------- 1 | (ns fred.image-resizer 2 | (:import java.io.File 3 | javax.imageio.ImageIO 4 | org.imgscalr.Scalr 5 | org.imgscalr.Scalr$Method 6 | java.awt.image.BufferedImageOp)) 7 | 8 | 9 | (defn list-all-files 10 | "List all files on a given directory." 11 | [root-dir relative-dir] 12 | (.listFiles (File. (str root-dir relative-dir)))) 13 | 14 | (defn get-file-extension 15 | "Returns the file extension." 16 | [file] 17 | (let [file-name (.getName file)] 18 | (subs file-name 19 | (inc (.lastIndexOf file-name "."))))) 20 | 21 | (defn override-image 22 | "Overrides the given file with the given BufferedImage." 23 | [image-file buffered-image] 24 | (ImageIO/write buffered-image (get-file-extension image-file) image-file)) 25 | 26 | (defn resize-image 27 | "Resizes the given image applying the given scale. 28 | This method deletes 9-patches. TODO: properly resize them." 29 | [image-file scale] 30 | (if-not (.endsWith (.getName image-file) ".9.png") 31 | (let [buffered-image (ImageIO/read image-file)] 32 | (override-image image-file 33 | (Scalr/resize buffered-image 34 | Scalr$Method/AUTOMATIC 35 | (int (* (.getWidth buffered-image) scale)) 36 | (int (* (.getHeight buffered-image) scale)) 37 | (into-array BufferedImageOp [Scalr/OP_ANTIALIAS])))) 38 | ;; Delete the 9-patches 39 | (.delete image-file))) 40 | 41 | (defn resize-dir 42 | "Resizes all images on a directory based on a scale." 43 | [root-dir relative-dir scale] 44 | (doseq [file (list-all-files root-dir relative-dir)] 45 | (resize-image file scale))) 46 | -------------------------------------------------------------------------------- /code/src/fred/core.clj: -------------------------------------------------------------------------------- 1 | (ns fred.core 2 | (:require [fred.image-grabber :as image-grabber] 3 | [fred.image-saver :as image-saver] 4 | [fred.image-resizer :as image-resizer] 5 | [seesaw.dnd :as dnd]) 6 | (:use seesaw.core) 7 | (:gen-class)) 8 | 9 | (defn get-parent-path 10 | "Returns the folder immediately above of the given one." 11 | [path] 12 | (str 13 | (clojure.string/join "/" 14 | (butlast (clojure.string/split path #"/"))) 15 | "/")) 16 | 17 | (defn resize 18 | "Resizes all images on the given path." 19 | [path-images use-ldpi?] 20 | (let [path-folders (get-parent-path path-images)] 21 | (image-saver/copy-to-dir-and-rename 22 | (image-grabber/get-images path-images) path-images (str path-folders "drawable-xxhdpi/")) 23 | 24 | (image-saver/clone-dir 25 | path-folders "drawable-xxhdpi" "drawable-xhdpi" "drawable-hdpi" "drawable-mdpi") 26 | 27 | (image-resizer/resize-dir path-folders "drawable-xhdpi" 8/12) 28 | (image-resizer/resize-dir path-folders "drawable-hdpi" 6/12) 29 | (image-resizer/resize-dir path-folders "drawable-mdpi" 4/12) 30 | 31 | (when (= use-ldpi? true) 32 | (image-saver/clone-dir path-folders "drawable-xxhdpi" "drawable-ldpi") 33 | (image-resizer/resize-dir path-folders "drawable-ldpi" 3/12))) 34 | 35 | (alert "Your images were resized, buddy. Hope I've been useful.\nDo you want to grab a beer someday?")) 36 | 37 | (defn handle-file-drop 38 | "Grabs the dropped folder and start the resizing process." 39 | [data use-ldpi?] 40 | ;; data needs to be one folder. 41 | ;; TODO: support dropping multiple files. 42 | ;; TODO: handle exceptions 43 | (resize (str (.get data 0) "/") use-ldpi?)) 44 | 45 | (defn -main [] 46 | (native!) 47 | (def cb (checkbox :text "Create \"drawable-ldpi\"? Sure, I can do that!")) 48 | (invoke-later 49 | (-> (frame :title "Fred" 50 | :content (flow-panel 51 | :size [512 :by 630] 52 | :items [(label :icon (clojure.java.io/resource "icon.png") 53 | :drag-enabled? true 54 | :transfer-handler 55 | (dnd/default-transfer-handler 56 | :import [dnd/file-list-flavor (fn [{:keys [data]}] (handle-file-drop data (value cb)))])) 57 | (text :multi-line? true 58 | :editable? false 59 | :margin 20 60 | :font "BOLD-14" 61 | :text "Hely, pal, how is it going? I can resize images for you, it's no trouble\nat all! Just drag a folder and drop inside me and I'll gladly do the work.") 62 | cb]) 63 | :on-close :exit) 64 | pack! 65 | show!))) 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Fred 2 | 3 | **F**riendly **Re**sizer **D**roid 4 | 5 | ![Fred logo](http://static.leocad.io/fred.png) 6 | 7 | A tool to resize images to the various screen densities of Android devices. 8 | 9 | ##Current version 10 | 1.0 (20130502). Download the binary here: [fred-1.1.jar](ttps://github.com/leocadiotine/fred/releases/download/1.1/fred-1.1.jar). 11 | 12 | ##Purpose 13 | When developing/designing for Android, you have to deal with a bunch of different screen sizes and resolutions. If you design your layouts following the design guidelines and develop them using properly the SDK, you'll have no trouble. Android does all the hard work for you. 14 | 15 | But if you don't supply different versions of your image resources for the different screen sizes, Android will resize them at runtime. Therefore, your app will be slow. Because of that, it's always a good idea to supply images on the *drawable-xxhdpi*, *drawable-xhdpi*, *drawable-hdpi*, *drawable-mdpi* and (maybe) *drawable-ldpi* folders. 16 | 17 | Doing this on an image editor (like Photoshop) takes time, and it's extremely boring. So why lose time doing that? Fred can do this job for you! And trust me, he would be very happy to help. 18 | 19 | ##How it works 20 | While designing your layouts, use the [Nexus 5](https://www.google.com/nexus/5/) and its resolution (1920x1080) as the base. After that, slice and export all your image resources. 21 | 22 | Then, launch Fred and drag and drop the folder with the images on it. Fred will resize all and create the *drawable-xxhdpi*, *drawable-xhdpi*, *drawable-hdpi*, *drawable-mdpi* and (optional) *drawable-ldpi* folders for you. After that, just move them to your Android project. 23 | 24 | ##Usage 25 | Download [the executable jar](https://github.com/leocadiotine/fred/releases/download/1.1/fred-1.1.jar) and double-click it. Fred's window will appear. Drag a folder from your file explorer and drop on it. 26 | 27 | You need to have [Java](https://www.java.com/en/download/index.jsp) installed to run Fred. 28 | 29 | ##Build instructions 30 | For build instructions, refer to the [wiki](https://github.com/leocadiotine/fred/wiki/Build-instructions) page. 31 | 32 | ##License 33 | Fred's source code is released under BSD 2-clause license. Check LICENSE file for more information. 34 | 35 | If you use this code, I'd appreciate you refer my name (Leocadio Tiné) and the link to this project's page in your project's website or credits screen. Though you don't have any legal/contractual obligation to do so, just good karma. 36 | 37 | ##Suggestions? Comments? 38 | Pull requests are always welcome. So are donations :) 39 | 40 | To find me, buzz at `me[at]leocad.io` or [follow me on Twitter](http://www.twitter.com/leocadiotine). To read interesting stuff, go to [my blog](http://blog.leocad.io). 41 | 42 | Special thanks to [swannodette](https://github.com/swannodette), [zachallaun](https://github.com/zachallaun) and [guilhermearaujo](https://github.com/guilhermearaujo) for the pull requests! 43 | 44 | ~~~~ 45 | ::::::::::::: 46 | :: :: 47 | :: Made at :: 48 | :: :: 49 | ::::::::::::: 50 | :: 51 | Hacker School 52 | ::::::::::::: 53 | ~~~~ 54 | 55 | [![Donate button](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=PPHGB75L9LUC4&lc=US&item_name=Leocadio%20Tin%c3%a9¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted) 56 | --------------------------------------------------------------------------------