Четвер, 12 липня 2012 р.

On UI stuff, asynchronicity and the power of reader

Time is due for my usual report about the state of Clojure Android combo. During this time lein-droid was touched only lightly as I rewrote the version resolving part of the plugin (one that chose the latest version of the library on classpath if more than one were provided) to use aether library.

But most changes were done to neko of course. A brief rundown on new things follows.

New features in defui

After I introduced eval inside defui macro I missed one not-so-subtle bug. The macro worked as following: if the UI element is a vector - then it is considered an UI tree form and processed respectively, otherwise it is considered a language element to evaluate and pass through defui one more time. So if you add a form that doesn't eventually return a vector, the compilation will end up in the infinite loop.

Now you can add arbitrary objects created in runtime by quoting their symbol/form. For example, this code will create a LinearLayout and add a button to it:

(let [but (Button. context)]
  (defui [:linear-layout {:orientation :vertical}
           'but]))

One more useful feature is a new :def attribute for elements. Previously I used :id attribute, which registered an element in the global map and allowed further fetching by executing something like (by-id ::ok-button). Now you can directly provide a symbol to :def, which the element would automatically be bound to. See an example (dots mean there could be other code there):

(defui ... [:text-edit {:def price-edit ...}])
...
(.getText price-edit)

This approach is more convenient (don't have to call anything to get the object) and faster (no time is wasted to fetch the object from the map).

First primitives for ListView

Two-argument version of get-checked
ListView is a very big topic that has to be dealt with eventually so I started with some utility functions for multi-check ListViews first.

By default calling .getCheckedItemPositions on a ListView object returns a SparseBooleanArray instance that is not exceptionally delightful to work with. That is why I wrote get-checked which when given a ListView returns a list of integers that represent the IDs of elements being checked. If provided a second argument (a list of all values in the ListView) returns a vector of only those elements that are checked. Screenshot on the right shows this in action.

set-checked! obviously checks elements whose numbers are respective to the integers in the given list.

Adapters are currently constructed via interop but they need some kind of wrapper too, especially for defining custom views for elements (it would be nice if defui could be harnessed for that as well).

Intents and Bundles as maps

This one was fun to implement. Since Bundles (things that can store activity state or send some data to another activity) are basically maps but don't directly subclass ones I wrote a wrapper called like-map that allows to treat them as Clojure maps.

Here is how it looks:

(defactivity ...
  :on-create
  (fn [this bundle]
        (let [{:keys [sharks-num with-lasers?]} (like-map bundle)]
          ...

The whole power of destructuring is available to you instead of boring calls to .getInteger, .getBoolean and the like!

SharedPreferences as universal data storage

If Bundles are used to store some short-lived data while the application is running, SharedPreferences are for storing data between application restarts. like-map supports SharedPreferences as well but there is one more thing to it interesting on its own.

Initially SharedPreferences class supports saving a very limited number of primitive types - integers, booleans, floats and strings. Even for a small array of primitive type you need to use some other local storage (SQLite for instance). But having Clojure reader facilities at hand its a little hard to bear with...

So I didn't: assoc-arbitrary! when called on SharedPreferences.Editor object saves an arbitrary Clojure data structure into SP by serializing it into a string. Then you can read this data by calling get-arbitrary on the like-mapped SP object.

(-> (.edit shared-prefs)
    (assoc! :cpu-level 3)
    (assoc-arbitrary! :game-info {:sharks-num 3, 
                      :shark-health [100 30 75]})
    .commit)

You can still use primitive values with assoc! to avoid serializing primitives.

Keep in mind that Clojure reader is a relatively slow serialization mechanism. If you have a large amount of data to save, then DB or file should be preferred.

Doing things asynchronously

It's 25% and rising.

Mobile application should be responsive at all costs. That means staying responsive no matter what complex computations or large amount of IO you are performing at any given moment. Since Android runs on Java-like Virtual Machine it is essential to use multithreading to separate the UI code execution from some time-consuming activities.

Android SDK has this thing called AsyncTask to deal with (wait for it) asynchronous tasks. It is an abstract class that you should extend in your code and provide the following methods: onPreExecute, onPostExecute, onProgressUpdate and doInBackground. First three methods are executed on the UI thread, the last one - on the individual thread. This allows a task to be run asynchronously but at the same time communicate with UI about its progress.

For you to have a general impression how it is done in Java here's the code:

final ProgressDialog pb = new ProgressDialog(this);
pb.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pb.setText("Java is not a verbose language");

new AsyncTask<Void, Integer, String>() {

    @Override
    protected void onPreExecute() {
        pb.show();
    }

    @Override
    protected String doInBackground(Void... params) {
        for (int i = 0; i < 100; i++) {
            Thread.sleep(100);
            publishProgress(i);
        }
        return "sample string";
    };

    @Override
    protected void onProgressUpdate(Integer... values) {
        pb.setProgress(values[0]);
    }

    @Override
    protected void onPostExecute(String msg) {
        pb.hide();
        some_callback(msg);
    }
}.execute();

So in order to deal with this pile of terrifically incidental complexity I wrote... nothing! Clojure's native capabilities and on-ui macro I wrote so far was enough to implement asynchronous tasks simply and idiomatically. The same code in Clojure:

(let [pb (defui [:progress-dialog 
                 {:progress-style :horizontal
                  :text "Clojure conquering the world..."}])]
  (.show pb)
  (future
    (dotimes [i 100]
      (Thread/sleep 100)
      (on-ui (.setProgress pb i)))
    (on-ui (.hide pb)
           (some-callback "sample string"))))


Besides being three times shorter the implementation in Clojure has one more important advantage - the whole task is in the same lexical scope, so you don't need tricky callbacks to pass the data around it - everything is available everywhere.

Dealing with resources

The initial neko by Daniel Solano Gómez already had a convenient way to resolve resources, so instead of writing your.package.name.R$id/foo you could do (get-id :foo). The problem with this approach lies in its poor performance - the time was spent to resolve a proper internal class and get a field from it using reflection.

And that got me thinking - why do it in runtime if..? You know the rest of the story. Resource-resolving utilities are now macros that simply return the field reference. It looks the same but has no performance overhead at all! Did I tell you why I love Lisp?

Documentation

I wrote quite a description of nealy every neko namespace. It's not finished yet, but I'll append information on the rest namespaces soon. The docs are available here.

Summary

These two weeks involved deep thinking and some difficult decisions so the list of changes might not be that impressive. There is still a lot of stuff to do like ListViews and properly implement listeners for UI elements in defui. I hope to catch up with this in the following week. For now, thank you for your attention.

Вівторок, 26 червня 2012 р.

On macros, toasts, private detectives and experimental tools

My GSoC project goes on and it's two weeks now since I've reported in the last time. Good thing is I have some news to share.

Compilation modes

Runtime arity exceptions are no
longer scary if you're safe-for-ui!
One interesting feature I've implemented in neko and lein-droid simultaneously is the support for different code ending up in different compilation modes (say, in debug and release ones). It is achieved by defining a value in the compile time  (boolean one in this case) that represents the AOT-compilation mode currently happening. While compiling lein-droid puts a special property on the JVM property map (via System/setProperty), and then neko.init/is-debug var is set to provide the mode info to other parts of the application.

What is the use of this? For example, this allows to cleanly solve the "exceptions on the UI thread" problem. Brief description: all UI-specific code in Android (including UI handlers) runs on the so-called "UI thread". The issue with it is that it is uncontrollable from the REPL, so if an exceptions is uncaught on it - your program gets forcefully closed.

It's perfectly fine if you are in production (actually, it isn't, but at least you're fully responsible for not handling that exception), but REPL-driven development system has to be more forgiving. After all, you don't want lose your runtime image for accidentally dividing by zero while developing. The solution comes with the feature described above. neko.debug/safe-for-ui macro wraps any block of code in a try/catch. If an exception occurs it's message is displayed on the device screen, and the exception itself is available by using neko.debug/ui-e function. But if compiled in the release mode all your code is on its own - with no extra security and no redundant code.

Notifications

Sadly though it fails to do what
it claims to but I'm working on it.
This is rather a minor change but I've added a couple of wrappers to use Android notification subsystem (specifically Toasts and Notifications). The wrapper for the former is thin, only removing the necessity of providing a Context to create a toast. Application context is used in this case, it is captured with the call to defapplication and stored as a singleton (gosh, I hate this word).

The functions for Notifications are a bit more useful. The original Notification API seems inconsistent, with separate constructor and setter to specify different (but related) parameters. Anyway, that's how making a notification looks in neko. Notice how I provide a keyword ID instead of integer one. That's much more convenient to reference it if you need to remove the notification later. There's a stubbish declaration of the intent which I would certainly replace with something more clever. Also, icon could be specified once using neko.notify/set-default-notification-icon! so the respective field in notification function could be ignored.

(fire :mynotification
      (notification :icon R$drawable/ic_launcher
                    :ticker-text "Reporting in from ClojureHQ"
                    :content-title "Android Conquer Squad"
                    :content-text "Initiate Clojure World Domination."
                    :action [:activity Intent/ACTION_WEB_SEARCH]))

Project dependencies

Conjing some vals is never
bad if your name is Clojure
No matter how fun it is to develop apps in Clojure, you'll have to be able to use the existing Java libraries. One problem with them is that they usually are not distributed in JARs, but rather as a separate projects. This crutch comes from the limitation of JAR format - it is tedious to extract the resources from the jar to compile it into your own project.

Anyway Eclipse solves this by providing a feature called "library project". This means that you can set one project as a dependency for another so the latter can use both code and resources from the former. Now lein-droid has this ability too. You can use lein init command in the existing repository to create project.clj file in it. lein-droid would automatically conclude if the project is a library or not (based on project.properties file). Then this project can be added under the :project-dependencies in the :android map (sample/project.clj contains an example).

Using this feature I was able to build a Clojure HelloWorld application with ActionBarSherlock. On the screenshot you can see the basic SherlockActivity with some tabs.

Optimizing build time

Bad news first - I've wasted two days bashing my head against the incremental DEX-file generation. Unfortunately without any positive effect. I guess the feature is yet very experimental (that's why there is no documentation on it at all). The idea is to save some time by reusing existing DEX-files to append new classes to them. This may be very valuable since DEX generation is the most time-consuming process throughout the toolchain. But it doesn't work. Most of the time it fails while merging existing file with the new ones, sometimes it succeeds to create a DEX, but application doesn't work at the end. I've tried to debug dx tool but there's lots of byte twisting I need to have deeper knowledge in to understand what's wrong.

However, I discovered the --no-optimize option which cut the DEXing time of my project from 110 seconds down to 80. That's 28% for free which I'm very happy with.

Wrapping up

lein-droid is getting better and closer to the point to be considered a serious build tool for Android applications. neko (my fork specifically) is still very young and rough around the edges (especially the UI part), so it will get most of my attention in the upcoming days.

On a side note, lein-droid 0.0.5 is available on Clojars, as well as neko 1.1.3.

This concludes the news for today. Stay tuned, mail me for any reason you might think of and have fun!

Неділя, 10 червня 2012 р.

The goods and bads of my UI struggle

Since it's time for my [to-be] weekly report I wrote down a few words about my progress.

First comes the boring stuff (not boring in a sense that it is boring for me to do, but may be rather boring for the reader). From now on lein-droid correctly handles multiple Android devices connected to the machine. If there is only one of them it directs all device-specific commands (like install or run) to it, otherwise it prompts user to choose the correct device. The user can also specify the device from the command line since all device-specific commands now accept additional arguments same as adb binary (like -d for the only connected device, -e for emulator, or -s SERIAL_NUMBER for the very particular thing).

I added a code-gen task that generates R.java files from the available resources (like images, strings and XML layouts). After the file is compiled with the compile task and goes all the way to the final application.

A minor change in sign task - now it automatically creates a debug keystore if it can't find one. The default path for the keystore is /home/username/.android/debug.keystore (the one Eclipse uses) but if you can change it by providing a value to the :profiles/:dev/:android/:keystore-path in project.clj.

So with all these changes lein-droid bumped to version 0.0.2 and is available on Clojars.

Now to the fun part. I extended and sophisticated my UI toolkit by providing a convenient way of defining "keyword to real thing" relations. Let me substantiate this. Since my initial intent was (and is) to generate in compile time as much as possible, the toolkit needs to know in compile time what the user has passed to it. Class names are not entirely great for this since they are just symbols (so we have to believe user that this symbol will be resolved into the class name when compiled). But if the class names are bound to the respective keywords beforehand then it is safer and easier to use these keywords. Furthermore we need some kind of ad-hoc hierarchy between elements (I partly mentioned it in my previous post). Some attributes (like :layout-height and :layout-width) require special treatment when generating code, and we would like to reuse these specific handlers across multiple elements.

One thing I'm not quite comfortable with is that I had to add eval into the UI-generating facility. I added it to let user manipulate his UI tree any way he wants before feeding it to the defui macro. Without eval user must provide the whole tree as-is to the macro call. In order to better explain what I mean a little demonstration follows.
(def button-attributes
  {:layout-width 0
   :layout-weight 1
   :layout-height :fill})

(defn number-button [i]
  [:button 
   (assoc button-attributes
          :text (str i)
          :onClickListener `(on-click
                             (add-symbol ~i)))])

(defui
  (stuff
   [:linear-layout {:orientation   :vertical
                    :layout-width  :fill
                    :layout-height :fill}
    [:edit {:id           ::edit
            :layout-width :fill}]]
   (map (fn [i]
          (stuff
           [:linear-layout {:orientation   :horizontal
                            :layout-width  :fill
                            :layout-height 0
                            :layout-weight 1}] 
           (map (fn [j]
                  (let [n (+ (* i 3) j)]
                    (number-button n)))
                (range 1 4))))
        (range 3))
   [[:linear-layout {:orientation   :horizontal
                     :layout-width  :fill
                     :layout-height 0
                     :layout-weight 1}
     [:button (assoc button-attributes
                :text            (str \u2190)
                :onClickListener '(on-click (delete-symbol)))]
     (number-button 0)
     [:button (assoc button-attributes
                :text            "Call"
                :onClickListener '(on-click (call-number)))]]]))
This code generates the UI you see on the screenshot. It's a bit messy and isn't probably what I would write in the real application. Right, it's rather a proof of concept. Let me describe it step by step.

First I define a simple map of attributes that all my buttons will share. It means that all buttons would fill the entire vertical space they are granted and adjust themselves space-equally in the horizontal dimension. An interesting point arises here. We just defined an ad-hoc style for a button to reuse in any other button (or any other View). A feature that required certain implementation in XML we received for free, just for the fact that we're using the same language for both data (UI in this case) and logic.

Then I write a function called number-button that given a number returns an element of our DSL for describing a button. As you could already see I don't write all number buttons manually, I just call this function with different arguments to generate templates for different buttons. I say "templates" because they will be turned into real buttons later by defui macro.

The final block is how the complete interface is defined. Don't mind the stuff function - it equals (comp vec concat) because I have to insert the generated buttons to the LinearLayouts and every element in the tree looks like [type attributes & inner-elements], so I need to actually append every button to the :linear-layout vector (I might change this in future, perhaps). There are some undefined functions which are not necessary for the demonstration. The whole application code is available here.

There are still a couple of sad things I discovered while working on this. The stack overflows when compiling some elaborate macro. It even overflows on the simplest usages of for macro. Fortunately the separation helps, so if an overflow happens I promote some parts to separate functions and it helps. Though it may seriously hamper productivity when devising really complex layouts.

Anyways that's it for now. Thank you for reading and I'll be happy to answer any questions that may arise.

Субота, 2 червня 2012 р.

Android UI with Clojure

Hey again.

It's been a while (about two weeks to be more specific) since I released the initial lein-droid plugin. Somebody even managed to run it which I'm both very astounded and glad for.

Oh, and by the way, no more Clojurish titles. They were not helping.

Meanwhile I started working on the toolkit part of the project. Well, "working" is a word too good for drafting the design and crumpling it every now and then. I took remvee's clj-android as an inspiration and shamelessly stole some of the functions and concepts from there. I still don't know what to do with the license - it is MIT one and I don't know whether it is compatible with neko's EPL. Right now I just added notices to the borrowed methods but in future I would either add a MIT license file into the project or rewrite the methods and claim them to be entirely mine (which is morally incorrect, but if I have no choice...)

clj-android is a nice little library by the way. The last commit dates back to 2010 when Daniel Solano Gómez was yet to release his patched Android version. This means that author saw Clojure just as the statically-compiled language replacement for Java. Maybe that's why he hasn't given much effort into it. Still there are macros for defining activities and user interfaces. Although I copied the defactivity macro entirely (for now), UI facilities definitely require more thought.

First, I have a question to answer - why at all bother with replacing XML UI design that is convenient, tested and at least already exists? Well, there are a few points of importance here:
  1. XML UI is static. XML layout files are embedded into application resources at compilation time and cannot be changed from there. This means that you have to recompile your application every time you change every minor thing in the UI. This directly interferes with our goal to achieve as much dynamic development environment as possible.
  2. XML is verbose. This may be a matter of taste but most tastes agree on this one.
  3. XML is different from Clojure. Obvious, huh? Though this point is very important. You cannot use Clojure from XML. You cannot use XML from Clojure except for a few provided points of intersection (ID's for example). It is not only that you have to think in both languages at once, it is about the ability to navigate seamlessly between the logic code and the UI code. Consider this example - you need to create 10 buttons with the names from 1 to 10. With XML you can't do this except manually define every button. No slightest kind of programming is available to you there. With a Clojure DSL for UI you have the entire Clojure at your command while defining the UI.
  4. XML is [potentially] slower. I added this "potentially" because currently Clojure/Android combo isn't on par with Java in the terms of performance and startup time. But perhaps in long perspective it could make a difference. Remember that XML layout files are parsed in runtime and in runtime views are inflated. On the other hand a DSL generates Java-interop code consisting of constructors and setters in  compile time.
So the idea is simple: get rid of XML, write Clojure code, achieve Java performance [for UI]. At the same time it is preferable to preserve the same attributes/values names as in XML (so the developer doesn't have to study them once again - he probably already knows all XML attributes by heart). For any other non-Lisp you are pretty much screwed to do this but with Clojure you are golden - just write a bunch of macros that [in compile time] transform the Clojure data structures into Java-interop code of setters. For example the button attribute :text becomes (.setText aButton "some text"), and LinearLayout attribute :orientation becomes (.setOrientation aLayout LinearLayout/VERTICAL). Looks easy, right? Most of the attributes have the respective setters like .setCapitalizedAttribute.

Most, but not all of them. For instance, two attributes :layout-width and :layout-height should be processed together by creating a LayoutParams instance from them and applying it via .setLayoutParams. Another point to be addressed is the user-created UI elements. We have to provide a facility that allows user to define a) his own attributes/attribute-processing-functions; b) inherit existing attributes for new elements.

To cut the long story short I created a draft of how it could possibly look like. For now I'm using multimethods that dispatch on the keyword representing the element. I also created a primitive tools for defining element's hierarchy to reuse the common attribute handlers, and for binding class names to keywords.

Another one of special attribute handlers is the handler of :id attribute. When this attribute is found, the library binds the provided ID to this element, so you can access the element by calling (by-id ID) (just like R.id.blabla for XML).

And finally some code and images. Here's how I define the UI you see on the first screenshot:

(defui [:linear-layout {:id ::main-layout,  
                        :orientation :vertical,  
                        :layout-width :fill
                        :layout-height :wrap}  
         [:button {:id ::android-button
                   :text "Android"  
                   :layout-width :fill
                   :layout-height :wrap}]  
         [:button {:text "Clojure"  
                   :layout-width :fill
                   :layout-height :wrap}]])  

A few changes, execute this piece of code again (no
recompilation/redeployment, do you remember? REPL for the win) and
here's what we have:

(defui [:linear-layout {:id ::main-layout   
                        :orientation :horizontal  
                        :layout-width :fill
                        :layout-height :wrap}  
         [:button {:id ::android-button  
                   :text "Thin and tall"  
                   :layout-width :wrap
                   :layout-height :fill}] 
         [:button {:text "Fat and short"   
                   :layout-width :fill
                   :layout-height :wrap}]])

P.S.: UI and defactivity stuff is available in neko 1.1.0-SNAPSHOT. For the time being I commit new neko code into my fork. Also, the example of the UI usage can be found in the sample/ folder in the lein-droid repository.

Середа, 23 травня 2012 р.

[lein-droid "0.0.1"]

Best friends forever.
So, the very first release is done.

Actually it wasn't much work to do since I posted here the last time. I added some fancy functions to take apart the AndroidManifest.xml file - it was necessary since starting the REPL requires the Internet permission, and I wanted it to be added automatically (if it wasn't not already there). Also I get the package name and the main activity name from the manifest in order to start the application from Leiningen. Not so necessary, but convenient.

I managed to make all the project dependencies to get totally AOT-compiled for the development build. Now all of them are available in the REPL (as it turned out, they were not before).

Added a "new" task to create Android projects using leiningen.new.templates library.

Designed a fancy macro for running subprocesses (which form the 90% of my plugin). Now the subprocess runs silently by default, but if it's return code is not zero, then it prints out the errors. With the DEBUG flag enabled it prints the messages even if the process finishes successfully.
Android wields the parentheses
filled with the Power of Lisp

Learned how to read passwords from the console in Java. The official javadocs suggest to fill the array of password characters with zeros as soon as the password is no longer needed. I wonder how many potentially-unsafe data Clojure leaves in memory when I convert the array to string and pass it around for a bit? And how long will it live before the GC cleans it out?
Hey there!

Put together a little tutorial to explain how to start using lein-droid.

And by the way - GSoC has officially begun! So things would only get more interesting. So stay tuned and enjoy the end of the spring ;).

Понеділок, 7 травня 2012 р.

(conj project libraries)

Well, adding dependencies to the project went smoother than I thought.

Adding the necessary libraries to the :dependencies vector in project.clj was nearly all I had to do. Leiningen automatically finds all Clojure namespaces on the classpath and compiles them. Though there were a few things I had to resolve:
  • DEX and APK packaging utilities really don't like being fed with the same jars. Same jars are the same regardless of their version - so packaging both Clojure 1.2.1 and Clojure 1.4.0 is impossible. Since each Clojure dependency library has its own dependencies so the Leiningen's dependency list fills up with different versions of the same jars. Currently I filter them out with a function that classifies these jars by their location in the ~/.m2/ folder (it looks like ~/.m2/repository/groupId/library-name/version/jarname.jar). And I agree that it is a bad BAD BAD solution. A much better option would be to process the POM-file that is stored in the same folder with this jar but that's not the safest you can get. The best thing to do I think is to extract the POM from the jar itself and then process it.
  • APK requires clojure.jar being packed as sources as well as binaries. I don't yet fully understand why it does this but after the discovery I decided that I should pack all dependencies as sources (unique ones, of course). There was the fun part: APK took project.clj-s from different libraries and said: "Nah, duplicate files, won't do". I was almost ready (psychologically) to reinvent another wheel when it turned out that source reference for libraries is not necessary. Phew.
So I may now say that I made it possible to run nREPL on the device by means of Leiningen. But that was the easy part.

The harder part is to provide all the tasks to be used conveniently. Right now my complete build line looks like this:
 lein droid cpl, droid dex, droid crunch, droid package, droid apk, droid sign, droid zip, droid install, droid run  
Can't say I've got huge problems executing it (shell history works, after all) but it is still a bit ugly. It gets even worse if you want to release your project - I thought I'd rely on Lein 2 profiles which are merged using `with-profile` task. Which affects only the command before comma. In other words, to release the thing I would have to type something like:
 lein with-profile release droid cpl, droid with-profile release droid dex, ...
Which is, of course, pure horror.

It can be easily solved by adding something like `doall` subtask (which I've already done). But it doesn't quite feel "the leiningen way" where all tasks do more or less only one thing. And I surely don't want to reimplement Leiningen's subtasks model and handle all comma-separated commands myself.

This difficult dilemma concludes my post for today. If anyone have an idea of how to do this better, please feel free to leave a note in the comments. Thank you.

Вівторок, 1 травня 2012 р.

(take 1 (iterate work nil))

Well, I started to work on my proposal.

Can't say that I'm productive and can hardly call it "work" at all. Seems more like tripping over every stone Leiningen and Android could ever prepare for me. But I'm happy about these stones because when resolved they actually lead to pretty valuable information and some possibilities unknown before. So bring it on, I say.

Some things I discovered:
  • Leiningen is very cool. Even features that not are implemented are easily hackable with hooks and middleware.
  • Marginalia is super cool. I've never done literate programming before but now I'm positively sure I should try it. For now I enjoy writing comments and seeing how they look in my browser. Hell, I always hated writing comments but now I write them for this single purpose.
Note to self:
  • Google deprecated some of their command-line stuff so at some point the revelant parts of the plugin should be rewritten to use their sdklib.jar. The way to do it in Leiningen is to use pomegranate.
What is done:
  • A couple of functions that are "just enough" to compile the project, create DEX, package into APK etc. right to the point of installation. The installed application even seems to work. Though this is an application without any dependencies (except for clojure) so there is still much to do.
That is all for now. If someone is interested here's the repository for the plugin: https://github.com/alexander-yakushev/lein-droid. And I guess I don't have to say this but just in case: IT IS IN EARLY DEVELOPMENT STAGE AND MOST PROBABLY NOT WORKING. Yeah, just like that. Come back in a month:).