├── AndroidManifest.xml ├── README ├── TODO ├── clojure-inits.txt ├── pom.xml ├── proguard.cfg ├── res ├── drawable-hdpi │ └── icon.png ├── drawable-ldpi │ └── icon.png ├── drawable-mdpi │ └── icon.png ├── layout │ └── main.xml └── values │ └── strings.xml └── src └── com └── hsaliak └── HelloFlashlight.clj /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | Flashlight Example for Clojure based on the example found at https://github.com/jayway/maven-android-plugin-samples 3 | 4 | Goals 5 | ==== 6 | 1)Android development in Clojure 7 | 2)No java bridge. 8 | 3)Ability to leverage Android tools and workflow. Ie: using generated resources. 9 | 4)A REPL with Android libraries available for rapid prototyping 10 | 5)Able to use DDMS on the app. With stack traces hitting clojure line numbers. 11 | 6)Escape getting mugged out of all your RAM by gangster IDEs 12 | 7)Use ProGuard to shrink the apk size. - Done , a size reduction of around 300k can be achieved by pruning unused parts of clojure core. apk size is around 900k. 13 | 8) TODO: Optimize with proguard 14 | 15 | The purpose of this codebase is to serve as an illustrative example of how to achieve the above goals. The idea is that the code will allow you to replicate this set-up for your android projects, giving you a productive environment quickly. 16 | 17 | Prerequisites: 18 | ==== 19 | Android SDK: 20 | - http://developer.android.com/sdk/index.html 21 | It is assumed that you have the SDK and a (Froyo) AVD ready. 22 | Maven: 23 | The whole build is managed by maven. 24 | - http://maven.apache.org 25 | You need to have a fair understanding of maven to not be put off by the xml work that needs to be done. 26 | 27 | Maven Android Plugin: 28 | The example implemented here is the flashlight example that demonstrates the android-maven plugin. The code for the Java version of this can be found at: 29 | - https://github.com/jayway/maven-android-plugin-samples 30 | To understand the maven-android example, this may be a good place to start. 31 | There is a detailed discussion of this plugin as well as all of maven here: 32 | - http://www.sonatype.com/books/mvnref-book/reference/public-book.html 33 | - http://www.sonatype.com/books/mvnref-book/reference/android-dev.html 34 | 35 | Clojure maven Plugin: 36 | The clojure-maven plugin is also required, and it can be found here: 37 | - https://github.com/talios/clojure-maven-plugin 38 | 39 | The project was first created by using the 'android' command found in the android SDK. 40 | #android create project -t android-8 -p helloflashlight -k com.yourname -a HelloFlashlight 41 | Refer to the android documentation for further details. 42 | 43 | Add maven support to this project, this is done by creating a pom.xml with the appropriate details. 44 | See http://www.sonatype.com/books/mvnref-book/reference/android-dev-sect-using.html 45 | 46 | Add clojure support to the project. For this, you will need to add the official clojure maven repository (for clojure-contrib) and the clojars repository for swank. (Goal 4) 47 | 48 | Add proguard support to the project. See http://proguard.sourceforge.net/ The tool is quite complex, so a lot of researching on the internet may be required. 49 | Clojure is also fairly complex and needs special treatment when used with proguard. The current approach and its limitations are explained in proguard.cfg 50 | 51 | Reading the material linked above, and this project's pom.xml should make the workflow clear. 52 | ==== 53 | 54 | Reccomended reading order: 55 | 1)pom.xml. 56 | 2) com/hsaliak/HelloFlashlight.clj ( I know I am missing 1 package level) 57 | 3) res/layout/main.xml 58 | 4) res/values/strings.xml 59 | 5) proguard.cfg 60 | 6) You may want to peek into clojure-inits.txt at the top level as a reference. 61 | 62 | 63 | Debugging: 64 | You will be able to run DDMS from your android SDK. This can be used to debug the app once its deployed in the emulator. 65 | 66 | REPL: 67 | pom.xml contains the swank plugin. 68 | Running "mvn clojure:swank" will start a swank server. Emacs can be used to slime-connect it. 69 | In the REPL, Android libs will be available to test. 70 | Eg: 71 | user> (import [android.graphics Color]) 72 | android.graphics.Color 73 | user> Color/WHITE 74 | -1 75 | user> Color/RED 76 | -65536 77 | 78 | 79 | Compilation: 80 | #mvn install android:deploy 81 | This step requires the avd to be started using the 'android' tool. 82 | pom.xml can be set up to start the emulator with the android:start-emulator target 83 | but has not yet been set up to do so. 84 | 85 | Useful targets: 86 | android:generate-sources - THis will create R.java, useful to know how to look at when coding the clojure bits 87 | android:undeploy : Make sure you enable undeploy before deploy so that you will always be running the latest app on your emulator 88 | android:deploy 89 | 90 | 91 | 92 | Startup time 93 | ========== 94 | The app took 9 seconds to start up on a froyo emulator. [ Measured via ddms] 95 | 02-03 14:34:11.560: INFO/ActivityManager(59): Displayed activity com.hsaliak/.HelloFlashlight: 8767 ms (total 8767 ms) 96 | 97 | The app took 2 seconds to start up on a Samsung Galaxy S I9000 [Measured via ddms ] 98 | 02-03 14:03:08.969: INFO/ActivityManager(2494): Displayed activity com.hsaliak/.HelloFlashlight: 2163 ms (total 2163 ms) 99 | 100 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Investigate the use of Proguard to minimize binary size [Done, but needs continued investigation] 2 | - Investigate including of Clojure-contrib and optimizing it. 3 | - Android-test support 4 | - Clojure unit test support 5 | -------------------------------------------------------------------------------- /clojure-inits.txt: -------------------------------------------------------------------------------- 1 | -keep public class clojure.template__init {*;} 2 | -keep public class clojure.core_deftype__init {*;} 3 | -keep public class clojure.zip__init {*;} 4 | -keep public class clojure.set__init {*;} 5 | -keep public class clojure.inspector__init {*;} 6 | -keep public class clojure.core__init {*;} 7 | -keep public class clojure.test.junit__init {*;} 8 | -keep public class clojure.core_print__init {*;} 9 | -keep public class clojure.repl__init {*;} 10 | -keep public class clojure.java.browse_ui__init {*;} 11 | -keep public class clojure.main__init {*;} 12 | -keep public class clojure.pprint.column_writer__init {*;} 13 | -keep public class clojure.java.browse__init {*;} 14 | -keep public class clojure.core_proxy__init {*;} 15 | -keep public class clojure.pprint.cl_format__init {*;} 16 | -keep public class clojure.java.shell__init {*;} 17 | -keep public class clojure.stacktrace__init {*;} 18 | -keep public class clojure.pprint.utilities__init {*;} 19 | -keep public class clojure.walk__init {*;} 20 | -keep public class clojure.pprint.dispatch__init {*;} 21 | -keep public class clojure.pprint.pprint_base__init {*;} 22 | -keep public class clojure.core.protocols__init {*;} 23 | -keep public class clojure.java.javadoc__init {*;} 24 | -keep public class clojure.test__init {*;} 25 | -keep public class clojure.java.io__init {*;} 26 | -keep public class clojure.genclass__init {*;} 27 | -keep public class clojure.test.tap__init {*;} 28 | -keep public class clojure.xml__init {*;} 29 | -keep public class clojure.pprint__init {*;} 30 | -keep public class clojure.string__init {*;} 31 | -keep public class clojure.gvec__init {*;} 32 | -keep public class clojure.pprint.pretty_writer__init {*;} 33 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 4.0.0 8 | com.hsaliak 9 | helloflashlight 10 | 0.1-SNAPSHOT 11 | apk 12 | HelloFlashlight 13 | 14 | 15 | 16 | 17 | com.google.android 18 | android 19 | 2.2.1 20 | provided 21 | 22 | 23 | 24 | org.clojure 25 | clojure 26 | 1.2.0 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | swank-clojure 36 | swank-clojure 37 | 1.3.0-SNAPSHOT 38 | 39 | 40 | 41 | 42 | 43 | src 44 | 45 | 47 | 48 | com.theoryinpractise 49 | clojure-maven-plugin 50 | 1.3.2 51 | 52 | 53 | src 54 | 55 | 56 | 57 | 58 | compile-clojure 59 | compile 60 | 61 | compile 62 | 63 | 64 | 65 | 66 | 67 | com.pyx4me 68 | proguard-maven-plugin 69 | 2.0.4 70 | 71 | 72 | net.sf.proguard 73 | proguard 74 | 4.4 75 | runtime 76 | 77 | 78 | 79 | 80 | 81 | process-classes 82 | proguard 83 | 84 | 85 | 86 | 87 | 88 | 89 | android-classes 90 | android-classes 91 | 4.4 92 | 93 | 94 | ${java.home}/lib/rt.jar 95 | 96 | 97 | 98 | proguard.cfg 99 | 100 | 101 | 102 | 103 | 104 | com.jayway.maven.plugins.android.generation2 105 | 106 | maven-android-plugin 107 | 2.8.4 108 | 109 | 110 | froyo 111 | 112 | 113 | 8 114 | 115 | 116 | true 117 | 118 | true 119 | 120 | true 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | clojure 136 | http://build.clojure.org/releases 137 | 138 | 139 | clojars 140 | http://clojars.org/repo 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /proguard.cfg: -------------------------------------------------------------------------------- 1 | -dontobfuscate 2 | -dontoptimize 3 | #-dontshrink #When developing your app, I recommend that this option is enabled by default.Proguard is best used at the end, 4 | #when your app is complete and tested. 5 | #When -dontshrink -dontoptimize and -ontobfuscate are all enabled, proguard can exist in your maven configuration/workflow without having any effect on the code. 6 | -verbose 7 | -dontpreverify 8 | -printseeds seed.txt 9 | 10 | -keep public class * extends android.app.Activity 11 | -keep public class * extends android.app.Application 12 | -keep public class * extends android.app.Service 13 | -keep public class * extends android.content.BroadcastReceiver 14 | -keep public class * extends android.content.ContentProvider 15 | -keep public class com.android.vending.licensing.ILicensingService 16 | 17 | #Note, the approach taken below is not ideal. 18 | #The best practicec seems to be to derive a config file 19 | #which is able to look at the class tree and determine what is needed. 20 | #In any case, here goes: 21 | #From gen-class's doc: "Each file, fn and gen-class will produce a .class file 22 | #Each file generates a loader class of the same name with "__init" appended." 23 | #Start with the configuration below, this causes all files in the clojure codebase to be kept. 24 | #-keep class clojure.**init { *;} 25 | 26 | #1) Once you have a successful proguard run which yields a working app, take a look at proguard_seeds.txt in target/ 27 | #2) Grok the file along the lines of the sed command shown below to pick up the relevant namespaces. 28 | #cat proguard_seeds.txt | sed -e s/^clojure.//g | sed -e s/\:.*$//g | uniq > ../clojure-inits.txt 29 | #3) Trim your classes from the bottom if necessary. See generated clojure-inits.txt for an example of what we want to extract. 30 | #4) With these init files, we can begin pruning the code base to yield the smallest runtime that works for us. Ideally, the proguard configuration will determine what is needed, but I have not found a way to make this work with clojure, given its dynamic nature and my limited understanding of proguard. The pruned configuration below was determined through looking at clojure.org and systematic trial and error (some of the namespaces are used internally.) 31 | #5) This approach outlined should actually work for any clojure app. The general idea is to remove unused namespaces. Using contrib libraries will have recursive dependancies. For clojure-contrib, a similar clojure.contrib.**init can be done, all unused contribs can be disabled. TODO: test this. 32 | #6) The pruning reduced the binary size from 1.2mb to 900k. Since most of the fat comes from the runtime, your app should grow much more gradually from that point. 33 | 34 | #===============Clojure related keep options=============== 35 | #The following cannot be disabled--------------- 36 | #Core classes 37 | -keep public class clojure.core__init {*;} 38 | -keep public class clojure.core_deftype__init {*;} 39 | -keep public class clojure.core_proxy__init {*;} 40 | -keep public class clojure.core.protocols__init {*;} 41 | -keep public class clojure.core_print__init {*;} 42 | -keep public class clojure.main__init {*;} 43 | 44 | #These are used internally.Disabling them causes the app to not load. 45 | #clojure.org has details on what they are. 46 | -keep public class clojure.zip__init {*;} 47 | -keep public class clojure.set__init {*;} 48 | -keep public class clojure.string__init {*;} 49 | -keep public class clojure.gvec__init {*;} 50 | -keep public class clojure.genclass__init {*;} 51 | -keep public class clojure.xml__init {*;} 52 | 53 | #Java interop related, all except IO should not be needed in android. 54 | #IO functions - This is needed for clojure 55 | -keep public class clojure.java.io__init {*;} 56 | 57 | 58 | #The following can be enabled if needed.--------------- 59 | 60 | # Tree walker, you will probably use them for complex apps 61 | #-keep public class clojure.walk__init {*;} 62 | #Clojure oriented stack traces. 63 | #-keep public class clojure.stacktrace__init {*;} 64 | #For use in macros 65 | #-keep public class clojure.template__init {*;} 66 | #Pretty printer not usually needed. 67 | # -keep public class clojure.pprint.column_writer__init {*;} 68 | # This may be needed if you are using cl-format. 69 | # -keep public class clojure.pprint.cl_format__init {*;} 70 | # -keep public class clojure.pprint.utilities__init {*;} 71 | # -keep public class clojure.pprint.dispatch__init {*;} 72 | # -keep public class clojure.pprint.pprint_base__init {*;} 73 | # -keep ptublic class clojure.pprint__init {*;} 74 | # -keep public class clojure.pprint.pretty_writer__init {*;} 75 | 76 | #It should be ok to keep the following disabled--------------- 77 | #Test libs. I feel that they do not make sense in an android app as the tests are run on the jvm prior to proguard and dexing. 78 | #-keep public class clojure.test.junit__init {*;} 79 | #-keep public class clojure.test__init {*;} 80 | #-keep public class clojure.test.tap__init {*;} 81 | 82 | #Used for inspecting clojure data structures see clojure.org 83 | #-keep public class clojure.inspector__init {*;} 84 | #Repl is not needed in android env, can be disabled. 85 | #-keep public class clojure.repl__init {*;} 86 | 87 | #Start a web browser from clojure, not needed. 88 | #-keep public class clojure.java.browse__init {*;} 89 | #Launch a subprocess 90 | #-keep public class clojure.java.shell__init {*;} 91 | #Open javadoc in repl 92 | #-keep public class clojure.java.javadoc__init {*;} 93 | #-keep public class clojure.java.browse_ui__init {*;} 94 | 95 | #End of clojure related keep options=============== 96 | 97 | # For now it is recommended that you keep your classes as-is. 98 | -keep class com.hsaliak.** {*;} 99 | # Required when exposes-methods is used. Without this, exposing super class methods with 100 | #alternate method names eg onCreate -> superOnCreate, does not seem to work 101 | -keep class com.hsaliak.HelloFlashlight extends * {*;} 102 | # Tip: when in doubt, browse to targets/classes and use javap to disassemble the class. 103 | 104 | 105 | 106 | 107 | 108 | -keepclasseswithmembernames class * { 109 | native ; 110 | } 111 | 112 | -keepclasseswithmembernames class * { 113 | public (android.content.Context, android.util.AttributeSet); 114 | } 115 | 116 | -keepclasseswithmembernames class * { 117 | public (android.content.Context, android.util.AttributeSet, int); 118 | } 119 | 120 | -keepclassmembers enum * { 121 | public static **[] values(); 122 | public static ** valueOf(java.lang.String); 123 | } 124 | 125 | -keep class * implements android.os.Parcelable { 126 | public static final android.os.Parcelable$Creator *; 127 | } 128 | 129 | -keep public class * extends android.view.View { 130 | public (android.content.Context); 131 | public (android.content.Context, android.util.AttributeSet); 132 | public (android.content.Context, android.util.AttributeSet, int); 133 | public void set*(...); 134 | } 135 | -keepclassmembers class * { 136 | public void *(android.view.View); 137 | } 138 | 139 | -keepclassmembers class **.R$* { 140 | public static ; 141 | } 142 | -------------------------------------------------------------------------------- /res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsaliak/android-clojure-flashlight-example/2da9f2fabd3daf94d6a701b428b3ec30f949ef3f/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsaliak/android-clojure-flashlight-example/2da9f2fabd3daf94d6a701b428b3ec30f949ef3f/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hsaliak/android-clojure-flashlight-example/2da9f2fabd3daf94d6a701b428b3ec30f949ef3f/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 9 |