├── .gitignore
├── .travis.yml
├── COPYING
├── README.org
├── project.clj
├── res
└── templates
│ ├── AndroidManifest.library.xml
│ ├── AndroidManifest.template.xml
│ ├── BuildConfig.java
│ ├── LICENSE
│ ├── README.library.md
│ ├── README.md
│ ├── SplashActivity.java
│ ├── Util.java
│ ├── core.clj
│ ├── gitignore
│ ├── ic_launcher_hdpi.png
│ ├── ic_launcher_mdpi.png
│ ├── library.project.clj
│ ├── main.clj
│ ├── proguard_minify.cfg
│ ├── proguard_multi_dex.cfg
│ ├── project.clj
│ ├── splash_background.xml
│ ├── splash_circle.png
│ ├── splash_droid.png
│ ├── splash_hands.png
│ ├── splash_rotation.xml
│ ├── splashscreen.xml
│ ├── strings.library.xml
│ └── strings.xml
├── sample
├── .gitignore
├── AndroidManifest.template.xml
├── README.md
├── build
│ ├── proguard-minify.cfg
│ └── proguard-multi-dex.cfg
├── project.clj
├── res
│ ├── anim
│ │ └── splash_rotation.xml
│ ├── drawable-hdpi
│ │ ├── ic_launcher.png
│ │ ├── splash_circle.png
│ │ ├── splash_droid.png
│ │ └── splash_hands.png
│ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ ├── drawable
│ │ └── splash_background.xml
│ ├── layout
│ │ └── splashscreen.xml
│ └── values
│ │ └── strings.xml
└── src
│ ├── clojure
│ └── test
│ │ └── leindroid
│ │ └── sample
│ │ └── main.clj
│ └── java
│ └── test
│ └── leindroid
│ └── sample
│ └── SplashActivity.java
├── src
├── lein_droid
│ └── plugin.clj
└── leiningen
│ ├── droid.clj
│ └── droid
│ ├── aar.clj
│ ├── build.clj
│ ├── classpath.clj
│ ├── code_gen.clj
│ ├── compile.clj
│ ├── deploy.clj
│ ├── manifest.clj
│ ├── new.clj
│ ├── sdk.clj
│ ├── sideload.clj
│ ├── test.clj
│ └── utils.clj
└── travis
├── deploy.sh
└── profiles.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /lib
3 | /classes
4 | /checkouts
5 | /docs
6 | pom.xml
7 | *.jar
8 | *.class
9 | .lein-deps-sum
10 | .lein-failures
11 | .lein-plugins
12 | *~
13 | .nrepl-port
14 | .lein-repl-history
15 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | sudo: false
3 |
4 | cache:
5 | directories:
6 | - $HOME/.m2
7 |
8 | before_install:
9 | - mkdir ~/bin
10 | - wget https://raw.github.com/technomancy/leiningen/stable/bin/lein -P ~/bin/
11 | - chmod a+x ~/bin/lein
12 | - mkdir ~/.lein
13 | - cp travis/profiles.clj ~/.lein/
14 |
15 | android:
16 | components:
17 | - build-tools-21.1.1
18 | - android-18
19 | - extra-android-m2repository
20 |
21 | lein: ~/bin/lein
22 |
23 | env:
24 | - PROJECT="sample" PREP="cd sample" LEIN_DROID_PROJECT="" DEPLOY="bash travis/deploy.sh"
25 | - PROJECT="newtest" PREP="lein droid new newtest org.clojure_android.newtest" LEIN_DROID_PROJECT="newtest/project.clj" DEPLOY=""
26 |
27 | script:
28 | - $PREP
29 | - DEBUG=1 LEIN_DROID_PROFILES=travis,dev lein with-profile travis,dev do droid build, droid apk
30 | - DEBUG=1 LEIN_DROID_PROFILES=travis,release lein with-profile travis,release do droid build, droid apk
31 | - DEBUG=1 LEIN_DROID_PROFILES=travis,lean lein with-profile travis,lean do clean, droid build, droid apk
32 |
33 | after_success:
34 | - cd ..
35 | - $DEPLOY
36 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | Distributed under the Eclipse Public License - v 1.0
2 |
3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF
5 | THE PROGRAM CONSTITUTES RECIPIENT’S ACCEPTANCE OF THIS AGREEMENT.
6 |
7 | 1. DEFINITIONS
8 |
9 | "Contribution" means:
10 |
11 | a) in the case of the initial Contributor, the initial code and
12 | documentation distributed under this Agreement, and
13 | b) in the case of each subsequent Contributor:
14 |
15 | i)changes to the Program, and
16 |
17 | ii)additions to the Program;
18 |
19 | where such changes and/or additions to the Program originate from and
20 | are distributed by that particular Contributor. A Contribution
21 | 'originates' from a Contributor if it was added to the Program by such
22 | Contributor itself or anyone acting on such Contributor’s behalf.
23 | Contributions do not include additions to the Program which: (i) are
24 | separate modules of software distributed in conjunction with the
25 | Program under their own license agreement, and (ii) are not derivative
26 | works of the Program.
27 |
28 | "Contributor" means any person or entity that distributes the Program.
29 |
30 | "Licensed Patents " mean patent claims licensable by a Contributor
31 | which are necessarily infringed by the use or sale of its Contribution
32 | alone or when combined with the Program.
33 |
34 | "Program" means the Contributions distributed in accordance with this
35 | Agreement.
36 |
37 | "Recipient" means anyone who receives the Program under this
38 | Agreement, including all Contributors.
39 |
40 | 2. GRANT OF RIGHTS
41 |
42 | a) Subject to the terms of this Agreement, each Contributor hereby
43 | grants Recipient a non-exclusive, worldwide, royalty-free copyright
44 | license to reproduce, prepare derivative works of, publicly display,
45 | publicly perform, distribute and sublicense the Contribution of such
46 | Contributor, if any, and such derivative works, in source code and
47 | object code form.
48 |
49 | b) Subject to the terms of this Agreement, each Contributor hereby
50 | grants Recipient a non-exclusive, worldwide, royalty-free patent
51 | license under Licensed Patents to make, use, sell, offer to sell,
52 | import and otherwise transfer the Contribution of such Contributor, if
53 | any, in source code and object code form. This patent license shall
54 | apply to the combination of the Contribution and the Program if, at
55 | the time the Contribution is added by the Contributor, such addition
56 | of the Contribution causes such combination to be covered by the
57 | Licensed Patents. The patent license shall not apply to any other
58 | combinations which include the Contribution. No hardware per se is
59 | licensed hereunder.
60 |
61 | c) Recipient understands that although each Contributor grants the
62 | licenses to its Contributions set forth herein, no assurances are
63 | provided by any Contributor that the Program does not infringe the
64 | patent or other intellectual property rights of any other entity. Each
65 | Contributor disclaims any liability to Recipient for claims brought by
66 | any other entity based on infringement of intellectual property rights
67 | or otherwise. As a condition to exercising the rights and licenses
68 | granted hereunder, each Recipient hereby assumes sole responsibility
69 | to secure any other intellectual property rights needed, if any. For
70 | example, if a third party patent license is required to allow
71 | Recipient to distribute the Program, it is Recipient’s responsibility
72 | to acquire that license before distributing the Program.
73 |
74 | d) Each Contributor represents that to its knowledge it has sufficient
75 | copyright rights in its Contribution, if any, to grant the copyright
76 | license set forth in this Agreement.
77 |
78 | 3. REQUIREMENTS
79 |
80 | A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
81 |
82 | a) it complies with the terms and conditions of this Agreement; and
83 |
84 | b) its license agreement:
85 |
86 | i) effectively disclaims on behalf of all Contributors all
87 | warranties and conditions, express and implied, including
88 | warranties or conditions of title and non-infringement, and
89 | implied warranties or conditions of merchantability and fitness
90 | for a particular purpose;
91 |
92 | ii) effectively excludes on behalf of all Contributors all
93 | liability for damages, including direct, indirect, special,
94 | incidental and consequential damages, such as lost profits;
95 |
96 | iii) states that any provisions which differ from this Agreement
97 | are offered by that Contributor alone and not by any other party;
98 | and
99 |
100 | iv) states that source code for the Program is available from such
101 | Contributor, and informs licensees how to obtain it in a
102 | reasonable manner on or through a medium customarily used for
103 | software exchange.
104 |
105 | When the Program is made available in source code form:
106 |
107 | a) it must be made available under this Agreement; and
108 |
109 | b) a copy of this Agreement must be included with each copy of the
110 | Program.
111 |
112 | Contributors may not remove or alter any copyright notices contained
113 | within the Program.
114 |
115 | Each Contributor must identify itself as the originator of its
116 | Contribution, if any, in a manner that reasonably allows subsequent
117 | Recipients to identify the originator of the Contribution.
118 |
119 | 4. COMMERCIAL DISTRIBUTION
120 |
121 | Commercial distributors of software may accept certain
122 | responsibilities with respect to end users, business partners and the
123 | like. While this license is intended to facilitate the commercial use
124 | of the Program, the Contributor who includes the Program in a
125 | commercial product offering should do so in a manner which does not
126 | create potential liability for other Contributors. Therefore, if a
127 | Contributor includes the Program in a commercial product offering,
128 | such Contributor ("Commercial Contributor") hereby agrees to defend
129 | and indemnify every other Contributor ("Indemnified Contributor")
130 | against any losses, damages and costs (collectively "Losses") arising
131 | from claims, lawsuits and other legal actions brought by a third party
132 | against the Indemnified Contributor to the extent caused by the acts
133 | or omissions of such Commercial Contributor in connection with its
134 | distribution of the Program in a commercial product offering. The
135 | obligations in this section do not apply to any claims or Losses
136 | relating to any actual or alleged intellectual property infringement.
137 | In order to qualify, an Indemnified Contributor must: a) promptly
138 | notify the Commercial Contributor in writing of such claim, and b)
139 | allow the Commercial Contributor to control, and cooperate with the
140 | Commercial Contributor in, the defense and any related settlement
141 | negotiations. The Indemnified Contributor may participate in any such
142 | claim at its own expense.
143 |
144 | For example, a Contributor might include the Program in a commercial
145 | product offering, Product X. That Contributor is then a Commercial
146 | Contributor. If that Commercial Contributor then makes performance
147 | claims, or offers warranties related to Product X, those performance
148 | claims and warranties are such Commercial Contributor’s responsibility
149 | alone. Under this section, the Commercial Contributor would have to
150 | defend claims against the other Contributors related to those
151 | performance claims and warranties, and if a court requires any other
152 | Contributor to pay any damages as a result, the Commercial Contributor
153 | must pay those damages.
154 |
155 | 5. NO WARRANTY
156 |
157 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
158 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
159 | KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
160 | WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
161 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
162 | responsible for determining the appropriateness of using and
163 | distributing the Program and assumes all risks associated with its
164 | exercise of rights under this Agreement , including but not limited to
165 | the risks and costs of program errors, compliance with applicable
166 | laws, damage to or loss of data, programs or equipment, and
167 | unavailability or interruption of operations.
168 |
169 | 6. DISCLAIMER OF LIABILITY
170 |
171 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR
172 | ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
173 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
174 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
175 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
176 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
177 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
178 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
179 |
180 | 7. GENERAL
181 |
182 | If any provision of this Agreement is invalid or unenforceable under
183 | applicable law, it shall not affect the validity or enforceability of
184 | the remainder of the terms of this Agreement, and without further
185 | action by the parties hereto, such provision shall be reformed to the
186 | minimum extent necessary to make such provision valid and enforceable.
187 |
188 | If Recipient institutes patent litigation against any entity
189 | (including a cross-claim or counterclaim in a lawsuit) alleging that
190 | the Program itself (excluding combinations of the Program with other
191 | software or hardware) infringes such Recipient’s patent(s), then such
192 | Recipient’s rights granted under Section 2(b) shall terminate as of
193 | the date such litigation is filed.
194 |
195 | All Recipient’s rights under this Agreement shall terminate if it
196 | fails to comply with any of the material terms or conditions of this
197 | Agreement and does not cure such failure in a reasonable period of
198 | time after becoming aware of such noncompliance. If all Recipient’s
199 | rights under this Agreement terminate, Recipient agrees to cease use
200 | and distribution of the Program as soon as reasonably practicable.
201 | However, Recipient’s obligations under this Agreement and any licenses
202 | granted by Recipient relating to the Program shall continue and
203 | survive.
204 |
205 | Everyone is permitted to copy and distribute copies of this Agreement,
206 | but in order to avoid inconsistency the Agreement is copyrighted and
207 | may only be modified in the following manner. The Agreement Steward
208 | reserves the right to publish new versions (including revisions) of
209 | this Agreement from time to time. No one other than the Agreement
210 | Steward has the right to modify this Agreement. The Eclipse Foundation
211 | is the initial Agreement Steward. The Eclipse Foundation may assign
212 | the responsibility to serve as the Agreement Steward to a suitable
213 | separate entity. Each new version of the Agreement will be given a
214 | distinguishing version number. The Program (including Contributions)
215 | may always be distributed subject to the version of the Agreement
216 | under which it was received. In addition, after a new version of the
217 | Agreement is published, Contributor may elect to distribute the
218 | Program (including its Contributions) under the new version. Except as
219 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives
220 | no rights or licenses to the intellectual property of any Contributor
221 | under this Agreement, whether expressly, by implication, estoppel or
222 | otherwise. All rights in the Program not expressly granted under this
223 | Agreement are reserved.
224 |
225 | This Agreement is governed by the laws of the State of New York and
226 | the intellectual property laws of the United States of America. No
227 | party to this Agreement will bring a legal action under this Agreement
228 | more than one year after the cause of action arose. Each party waives
229 | its rights to a jury trial in any resulting litigation.
230 |
--------------------------------------------------------------------------------
/README.org:
--------------------------------------------------------------------------------
1 | * lein-droid
2 |
3 | [[https://travis-ci.org/clojure-android/lein-droid/][https://travis-ci.org/clojure-android/lein-droid.svg?branch=master]]
4 |
5 | A Leiningen plugin to simplify Clojure development for Android
6 | platform. It acts as a build-tool for Clojure/Android projects.
7 |
8 | ** Usage
9 |
10 | First make sure you have [[http://developer.android.com/sdk/index.html][Android SDK]] installed, with the latest
11 | Android SDK Build-tools and Android Support Repository.
12 |
13 | Follow the [[https://github.com/clojure-android/lein-droid/wiki/Tutorial][Tutorial]] to start using lein-droid.
14 |
15 | Latest release:
16 |
17 | [[https://clojars.org/lein-droid][https://clojars.org/lein-droid/latest-version.svg]]
18 |
19 | ** Documentation
20 |
21 | The [[https://github.com/clojure-android/lein-droid/wiki][wiki]] is a primary source of lein-droid documentation. To know more about
22 | =project.clj= options lein-droid supports, consult [[https://github.com/clojure-android/lein-droid/wiki/project.clj-options][this page]].
23 |
24 | If you'd like to read the code, Marginalia docs are [[http://clojure-android.github.io/lein-droid/][here]].
25 |
26 | [[https://github.com/krisc][Kris Calabio]] wrote a [[https://github.com/alexander-yakushev/events/blob/master/tutorial.md][tutorial]] on how to write your first real Clojure/Android
27 | application.
28 |
29 | ** How to modify lein-droid
30 |
31 | Being a plugin for Leiningen, the workflow for modifying, debugging and
32 | testing it is slightly more complicated than with a regular Clojure project.
33 | See [[https://github.com/clojure-android/lein-droid/wiki/Hacking-the-plugin][Hacking the plugin]] page for details.
34 |
35 | ** Contributors
36 |
37 | I thank the following people for their help in extending and
38 | improving lein-droid:
39 |
40 | - [[https://github.com/AdamClements][Adam Clements]]
41 | - [[https://github.com/oakes][Zach Oakes]]
42 | - [[https://github.com/ayamada][Atsuo Yamada]]
43 | - [[https://github.com/sergv][Sergey Vinokurov]]
44 | - [[https://github.com/clojure-android/lein-droid/graphs/contributors][and others]]
45 |
46 | ** License
47 |
48 | Copyright © 2012-2015 Alexander Yakushev. Distributed under the Eclipse
49 | Public License, the same as Clojure. See the file [[https://github.com/clojure-android/lein-droid/blob/master/COPYING][COPYING]].
50 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject lein-droid/lein-droid "0.4.6"
2 | :description "Plugin for easy Clojure/Android development and deployment"
3 | :url "https://github.com/clojure-android/lein-droid"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 | :dependencies [[robert/hooke "1.3.0"]
7 | [org.clojure/data.zip "0.1.1"]
8 | [net.lingala.zip4j/zip4j "1.3.2"]
9 | [com.android.tools.build/manifest-merger "24.2.3"]
10 | [de.ubercode.clostache/clostache "1.4.0"]]
11 | :resource-paths ["res"]
12 | :eval-in-leiningen true)
13 |
--------------------------------------------------------------------------------
/res/templates/AndroidManifest.library.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/templates/AndroidManifest.template.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 | {[={{ }}=]}
8 |
9 | {{={[ ]}=}}
10 |
12 | {[={{ }}=]}
13 |
14 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{={[ ]}=}}
34 | {{#debug-build}}
35 |
36 |
37 |
38 |
39 | {{/debug-build}}
40 | {[={{ }}=]}
41 |
42 |
--------------------------------------------------------------------------------
/res/templates/BuildConfig.java:
--------------------------------------------------------------------------------
1 | package {{{package-name}}};
2 |
3 | /* This file is autogenerated from the values in :android
4 | * {:build-config {"NAME" value}}*/
5 |
6 | public class BuildConfig {
7 | public static final boolean DEBUG = {{{debug}}};
8 | {{#constants}}
9 | public static final {{{type}}} {{{key}}} = {{{value}}};
10 | {{/constants}}
11 | }
12 |
--------------------------------------------------------------------------------
/res/templates/LICENSE:
--------------------------------------------------------------------------------
1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
4 |
5 | 1. DEFINITIONS
6 |
7 | "Contribution" means:
8 |
9 | a) in the case of the initial Contributor, the initial code and
10 | documentation distributed under this Agreement, and
11 |
12 | b) in the case of each subsequent Contributor:
13 |
14 | i) changes to the Program, and
15 |
16 | ii) additions to the Program;
17 |
18 | where such changes and/or additions to the Program originate from and are
19 | distributed by that particular Contributor. A Contribution 'originates' from
20 | a Contributor if it was added to the Program by such Contributor itself or
21 | anyone acting on such Contributor's behalf. Contributions do not include
22 | additions to the Program which: (i) are separate modules of software
23 | distributed in conjunction with the Program under their own license
24 | agreement, and (ii) are not derivative works of the Program.
25 |
26 | "Contributor" means any person or entity that distributes the Program.
27 |
28 | "Licensed Patents" mean patent claims licensable by a Contributor which are
29 | necessarily infringed by the use or sale of its Contribution alone or when
30 | combined with the Program.
31 |
32 | "Program" means the Contributions distributed in accordance with this
33 | Agreement.
34 |
35 | "Recipient" means anyone who receives the Program under this Agreement,
36 | including all Contributors.
37 |
38 | 2. GRANT OF RIGHTS
39 |
40 | a) Subject to the terms of this Agreement, each Contributor hereby grants
41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to
42 | reproduce, prepare derivative works of, publicly display, publicly perform,
43 | distribute and sublicense the Contribution of such Contributor, if any, and
44 | such derivative works, in source code and object code form.
45 |
46 | b) Subject to the terms of this Agreement, each Contributor hereby grants
47 | Recipient a non-exclusive, worldwide, royalty-free patent license under
48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise
49 | transfer the Contribution of such Contributor, if any, in source code and
50 | object code form. This patent license shall apply to the combination of the
51 | Contribution and the Program if, at the time the Contribution is added by the
52 | Contributor, such addition of the Contribution causes such combination to be
53 | covered by the Licensed Patents. The patent license shall not apply to any
54 | other combinations which include the Contribution. No hardware per se is
55 | licensed hereunder.
56 |
57 | c) Recipient understands that although each Contributor grants the licenses
58 | to its Contributions set forth herein, no assurances are provided by any
59 | Contributor that the Program does not infringe the patent or other
60 | intellectual property rights of any other entity. Each Contributor disclaims
61 | any liability to Recipient for claims brought by any other entity based on
62 | infringement of intellectual property rights or otherwise. As a condition to
63 | exercising the rights and licenses granted hereunder, each Recipient hereby
64 | assumes sole responsibility to secure any other intellectual property rights
65 | needed, if any. For example, if a third party patent license is required to
66 | allow Recipient to distribute the Program, it is Recipient's responsibility
67 | to acquire that license before distributing the Program.
68 |
69 | d) Each Contributor represents that to its knowledge it has sufficient
70 | copyright rights in its Contribution, if any, to grant the copyright license
71 | set forth in this Agreement.
72 |
73 | 3. REQUIREMENTS
74 |
75 | A Contributor may choose to distribute the Program in object code form under
76 | its own license agreement, provided that:
77 |
78 | a) it complies with the terms and conditions of this Agreement; and
79 |
80 | b) its license agreement:
81 |
82 | i) effectively disclaims on behalf of all Contributors all warranties and
83 | conditions, express and implied, including warranties or conditions of title
84 | and non-infringement, and implied warranties or conditions of merchantability
85 | and fitness for a particular purpose;
86 |
87 | ii) effectively excludes on behalf of all Contributors all liability for
88 | damages, including direct, indirect, special, incidental and consequential
89 | damages, such as lost profits;
90 |
91 | iii) states that any provisions which differ from this Agreement are offered
92 | by that Contributor alone and not by any other party; and
93 |
94 | iv) states that source code for the Program is available from such
95 | Contributor, and informs licensees how to obtain it in a reasonable manner on
96 | or through a medium customarily used for software exchange.
97 |
98 | When the Program is made available in source code form:
99 |
100 | a) it must be made available under this Agreement; and
101 |
102 | b) a copy of this Agreement must be included with each copy of the Program.
103 |
104 | Contributors may not remove or alter any copyright notices contained within
105 | the Program.
106 |
107 | Each Contributor must identify itself as the originator of its Contribution,
108 | if any, in a manner that reasonably allows subsequent Recipients to identify
109 | the originator of the Contribution.
110 |
111 | 4. COMMERCIAL DISTRIBUTION
112 |
113 | Commercial distributors of software may accept certain responsibilities with
114 | respect to end users, business partners and the like. While this license is
115 | intended to facilitate the commercial use of the Program, the Contributor who
116 | includes the Program in a commercial product offering should do so in a
117 | manner which does not create potential liability for other Contributors.
118 | Therefore, if a Contributor includes the Program in a commercial product
119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend
120 | and indemnify every other Contributor ("Indemnified Contributor") against any
121 | losses, damages and costs (collectively "Losses") arising from claims,
122 | lawsuits and other legal actions brought by a third party against the
123 | Indemnified Contributor to the extent caused by the acts or omissions of such
124 | Commercial Contributor in connection with its distribution of the Program in
125 | a commercial product offering. The obligations in this section do not apply
126 | to any claims or Losses relating to any actual or alleged intellectual
127 | property infringement. In order to qualify, an Indemnified Contributor must:
128 | a) promptly notify the Commercial Contributor in writing of such claim, and
129 | b) allow the Commercial Contributor tocontrol, and cooperate with the
130 | Commercial Contributor in, the defense and any related settlement
131 | negotiations. The Indemnified Contributor may participate in any such claim
132 | at its own expense.
133 |
134 | For example, a Contributor might include the Program in a commercial product
135 | offering, Product X. That Contributor is then a Commercial Contributor. If
136 | that Commercial Contributor then makes performance claims, or offers
137 | warranties related to Product X, those performance claims and warranties are
138 | such Commercial Contributor's responsibility alone. Under this section, the
139 | Commercial Contributor would have to defend claims against the other
140 | Contributors related to those performance claims and warranties, and if a
141 | court requires any other Contributor to pay any damages as a result, the
142 | Commercial Contributor must pay those damages.
143 |
144 | 5. NO WARRANTY
145 |
146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON
147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR
149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A
150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the
151 | appropriateness of using and distributing the Program and assumes all risks
152 | associated with its exercise of rights under this Agreement , including but
153 | not limited to the risks and costs of program errors, compliance with
154 | applicable laws, damage to or loss of data, programs or equipment, and
155 | unavailability or interruption of operations.
156 |
157 | 6. DISCLAIMER OF LIABILITY
158 |
159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
166 | OF SUCH DAMAGES.
167 |
168 | 7. GENERAL
169 |
170 | If any provision of this Agreement is invalid or unenforceable under
171 | applicable law, it shall not affect the validity or enforceability of the
172 | remainder of the terms of this Agreement, and without further action by the
173 | parties hereto, such provision shall be reformed to the minimum extent
174 | necessary to make such provision valid and enforceable.
175 |
176 | If Recipient institutes patent litigation against any entity (including a
177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself
178 | (excluding combinations of the Program with other software or hardware)
179 | infringes such Recipient's patent(s), then such Recipient's rights granted
180 | under Section 2(b) shall terminate as of the date such litigation is filed.
181 |
182 | All Recipient's rights under this Agreement shall terminate if it fails to
183 | comply with any of the material terms or conditions of this Agreement and
184 | does not cure such failure in a reasonable period of time after becoming
185 | aware of such noncompliance. If all Recipient's rights under this Agreement
186 | terminate, Recipient agrees to cease use and distribution of the Program as
187 | soon as reasonably practicable. However, Recipient's obligations under this
188 | Agreement and any licenses granted by Recipient relating to the Program shall
189 | continue and survive.
190 |
191 | Everyone is permitted to copy and distribute copies of this Agreement, but in
192 | order to avoid inconsistency the Agreement is copyrighted and may only be
193 | modified in the following manner. The Agreement Steward reserves the right to
194 | publish new versions (including revisions) of this Agreement from time to
195 | time. No one other than the Agreement Steward has the right to modify this
196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The
197 | Eclipse Foundation may assign the responsibility to serve as the Agreement
198 | Steward to a suitable separate entity. Each new version of the Agreement will
199 | be given a distinguishing version number. The Program (including
200 | Contributions) may always be distributed subject to the version of the
201 | Agreement under which it was received. In addition, after a new version of
202 | the Agreement is published, Contributor may elect to distribute the Program
203 | (including its Contributions) under the new version. Except as expressly
204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
205 | licenses to the intellectual property of any Contributor under this
206 | Agreement, whether expressly, by implication, estoppel or otherwise. All
207 | rights in the Program not expressly granted under this Agreement are
208 | reserved.
209 |
210 | This Agreement is governed by the laws of the State of Washington and the
211 | intellectual property laws of the United States of America. No party to this
212 | Agreement will bring a legal action under this Agreement more than one year
213 | after the cause of action arose. Each party waives its rights to a jury trial
214 | in any resulting litigation.
215 |
--------------------------------------------------------------------------------
/res/templates/README.library.md:
--------------------------------------------------------------------------------
1 | # {{app-name}}
2 |
3 | This is a Clojure/Android library.
4 |
5 | ## Usage
6 |
7 | FIXME
8 |
9 | ## License
10 |
11 | Copyright © 2015 FIXME
12 |
13 | Distributed under the Eclipse Public License, the same as Clojure.
14 |
--------------------------------------------------------------------------------
/res/templates/README.md:
--------------------------------------------------------------------------------
1 | # {{app-name}}
2 |
3 | This is a Clojure/Android application.
4 |
5 | ## Usage
6 |
7 | FIXME
8 |
9 | ## License
10 |
11 | Copyright © 2015 FIXME
12 |
13 | Distributed under the Eclipse Public License, the same as Clojure.
14 |
--------------------------------------------------------------------------------
/res/templates/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package {{package}};
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.animation.Animation;
7 | import android.view.animation.AnimationUtils;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 | import neko.App;
11 |
12 | import {{package}}.R;
13 |
14 | public class SplashActivity extends Activity {
15 |
16 | private static boolean firstLaunch = true;
17 |
18 | @Override
19 | public void onCreate(Bundle bundle) {
20 | super.onCreate(bundle);
21 |
22 | if (firstLaunch) {
23 | firstLaunch = false;
24 | setupSplash();
25 | App.loadAsynchronously("{{package}}.{{activity}}",
26 | new Runnable() {
27 | @Override
28 | public void run() {
29 | proceed();
30 | }});
31 | } else {
32 | proceed();
33 | }
34 | }
35 |
36 | public void setupSplash() {
37 | setContentView(R.layout.splashscreen);
38 |
39 | TextView appNameView = (TextView)findViewById(R.id.splash_app_name);
40 | appNameView.setText(R.string.app_name);
41 |
42 | Animation rotation = AnimationUtils.loadAnimation(this, R.anim.splash_rotation);
43 | ImageView circleView = (ImageView)findViewById(R.id.splash_circles);
44 | circleView.startAnimation(rotation);
45 | }
46 |
47 | public void proceed() {
48 | startActivity(new Intent("{{package-sanitized}}.MAIN"));
49 | finish();
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/res/templates/Util.java:
--------------------------------------------------------------------------------
1 | package {{package}};
2 |
3 | import android.content.Context;
4 |
5 | import {{package}}.R;
6 |
7 | public class Util {
8 |
9 | public static String getName(Context c) {
10 | return c.getString(R.string.{{name}}_library_name);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/res/templates/core.clj:
--------------------------------------------------------------------------------
1 | (ns {{package}}.core)
2 |
3 | (defn sum [a b]
4 | (+ a b))
5 |
--------------------------------------------------------------------------------
/res/templates/gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /classes
3 | /gen
4 | /checkouts
5 | pom.xml
6 | pom.xml.asc
7 | *.jar
8 | *.class
9 | /.lein-*
10 | /.nrepl-port
11 |
--------------------------------------------------------------------------------
/res/templates/ic_launcher_hdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/res/templates/ic_launcher_hdpi.png
--------------------------------------------------------------------------------
/res/templates/ic_launcher_mdpi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/res/templates/ic_launcher_mdpi.png
--------------------------------------------------------------------------------
/res/templates/library.project.clj:
--------------------------------------------------------------------------------
1 | (defproject {{name}}/{{name}} "0.1.0-SNAPSHOT"
2 | :description "FIXME: Android library description"
3 | :packaging "aar"
4 |
5 | :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
6 | {{#new-project}}
7 | :source-paths ["src/clojure"]
8 | :java-source-paths ["src/java"]
9 | {{/new-project}}
10 | {{^new-project}}
11 | :java-source-paths ["src"]
12 |
13 | ;; Uncomment this line if your project doesn't use Clojure.
14 | ;; :java-only true
15 | {{/new-project}}
16 |
17 | :plugins [[lein-droid "{{lein-droid-version}}"]]
18 | :profiles {:default [:android-common]}
19 |
20 | :android {:target-version "{{target-sdk}}"
21 | :library true
22 | {{^new-project}}
23 | :manifest-template-path "AndroidManifest.xml"
24 | {{/new-project}}
25 | })
26 |
--------------------------------------------------------------------------------
/res/templates/main.clj:
--------------------------------------------------------------------------------
1 | (ns {{package}}.main
2 | (:require [neko.activity :refer [defactivity set-content-view!]]
3 | [neko.debug :refer [*a]]
4 | [neko.notify :refer [toast]]
5 | [neko.resource :as res]
6 | [neko.find-view :refer [find-view]]
7 | [neko.threading :refer [on-ui]])
8 | (:import android.widget.EditText))
9 |
10 | ;; We execute this function to import all subclasses of R class. This gives us
11 | ;; access to all application resources.
12 | (res/import-all)
13 |
14 | (defn notify-from-edit
15 | "Finds an EditText element with ID ::user-input in the given activity. Gets
16 | its contents and displays them in a toast if they aren't empty. We use
17 | resources declared in res/values/strings.xml."
18 | [activity]
19 | (let [^EditText input (.getText (find-view activity ::user-input))]
20 | (toast (if (empty? input)
21 | (res/get-string R$string/input_is_empty)
22 | (res/get-string R$string/your_input_fmt input))
23 | :long)))
24 |
25 | ;; This is how an Activity is defined. We create one and specify its onCreate
26 | ;; method. Inside we create a user interface that consists of an edit and a
27 | ;; button. We also give set callback to the button.
28 | (defactivity {{package}}.{{activity}}
29 | :key :main
30 |
31 | (onCreate [this bundle]
32 | (.superOnCreate this bundle)
33 | (neko.debug/keep-screen-on this)
34 | (on-ui
35 | (set-content-view! (*a)
36 | [:linear-layout {:orientation :vertical
37 | :layout-width :fill
38 | :layout-height :wrap}
39 | [:edit-text {:id ::user-input
40 | :hint "Type text here"
41 | :layout-width :fill}]
42 | [:button {:text R$string/touch_me ;; We use resource here, but could
43 | ;; have used a plain string too.
44 | :on-click (fn [_] (notify-from-edit (*a)))}]]))))
45 |
--------------------------------------------------------------------------------
/res/templates/proguard_minify.cfg:
--------------------------------------------------------------------------------
1 | # This is a configuration file for ProGuard.
2 | # http://proguard.sourceforge.net/index.html#manual/usage.html
3 |
4 | -libraryjars /lib/rt.jar
5 |
6 | -dontusemixedcaseclassnames
7 | -dontskipnonpubliclibraryclasses
8 | -verbose
9 |
10 | # Optimization is turned off by default. Dex does not like code run
11 | # through the ProGuard optimize and preverify steps (and performs some
12 | # of these optimizations on its own).
13 | # -optimizations code/removal/advanced
14 | # !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
15 | # -optimizationpasses 2
16 | -dontoptimize
17 | -dontpreverify
18 | -dontobfuscate
19 | -dontnote
20 |
21 | # Note that if you want to enable optimization, you cannot just include
22 | # optimization flags in your own project configuration file; instead use
23 | # "proguard-android-optimize.txt" file instead from your SDK folder.
24 |
25 | -keepattributes *Annotation*
26 | -keep public class com.google.vending.licensing.ILicensingService
27 | -keep public class com.android.vending.licensing.ILicensingService
28 |
29 | # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
30 | -keepclasseswithmembernames class * {
31 | native ;
32 | }
33 |
34 | # keep setters in Views so that animations can still work.
35 | # see http://proguard.sourceforge.net/manual/examples.html#beans
36 | -keepclassmembers public class * extends android.view.View {
37 | void set*(***);
38 | *** get*();
39 | }
40 |
41 | # We want to keep methods in Activity that could be used in the XML attribute onClick
42 | -keepclassmembers class * extends android.app.Activity {
43 | public void *(android.view.View);
44 | public void super*(...);
45 | }
46 |
47 | # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
48 | -keepclassmembers enum * {
49 | public static **[] values();
50 | public static ** valueOf(java.lang.String);
51 | }
52 |
53 | -keepclassmembers class **.R$* {
54 | public static ;
55 | }
56 |
57 | -keep public class clojure.lang.Fn
58 | -keep public class neko.App
59 | -keep public class **__init
60 |
61 | -keep public class {{package-sanitized}}.*
62 |
63 | # The support library contains references to newer platform versions.
64 | # Don't warn about those in case this app is linking against an older
65 | # platform version. We know about them, and they are safe.
66 | # -dontwarn android.support.**
67 |
--------------------------------------------------------------------------------
/res/templates/proguard_multi_dex.cfg:
--------------------------------------------------------------------------------
1 | -dontoptimize
2 | -dontpreverify
3 | -dontobfuscate
4 | -dontwarn
5 | -dontnote
6 | -forceprocessing
7 |
8 | -keep class clojure.**
9 |
10 | -keep public class * extends android.app.Instrumentation {
11 | ();
12 | }
13 | -keep public class * extends android.app.Application {
14 | ();
15 | void attachBaseContext(android.content.Context);
16 | }
17 | -keep public class * extends android.app.Activity {
18 | ();
19 | }
20 | -keep public class * extends android.app.Service {
21 | ();
22 | }
23 | -keep public class * extends android.content.ContentProvider {
24 | ();
25 | }
26 | -keep public class * extends android.content.BroadcastReceiver {
27 | ();
28 | }
29 | -keep public class * extends android.app.backup.BackupAgent {
30 | ();
31 | }
32 | # We need to keep all annotation classes because proguard does not trace annotation attribute
33 | # it just filter the annotation attributes according to annotation classes it already kept.
34 | -keep public class * extends java.lang.annotation.Annotation {
35 | *;
36 | }
37 |
--------------------------------------------------------------------------------
/res/templates/project.clj:
--------------------------------------------------------------------------------
1 | (defproject {{name}}/{{name}} "0.1.0-SNAPSHOT"
2 | :description "FIXME: Android project description"
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :global-vars {clojure.core/*warn-on-reflection* true}
8 |
9 | :source-paths ["src/clojure" "src"]
10 | :java-source-paths ["src/java"]
11 | :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
12 | :plugins [[lein-droid "{{lein-droid-version}}"]]
13 |
14 | :dependencies [[org.clojure-android/clojure "{{clojure-version}}"]
15 | [neko/neko "{{neko-version}}"]]
16 | :profiles {:default [:dev]
17 |
18 | :dev
19 | [:android-common :android-user
20 | {:dependencies [[org.clojure/tools.nrepl "0.2.10"]]
21 | :target-path "target/debug"
22 | :android {:aot :all-with-unused
23 | :manifest-options {:app-name "{{app-name}} (debug)"}
24 | ;; Uncomment to be able install debug and release side-by-side.
25 | ;; :rename-manifest-package "{{package-sanitized}}.debug"
26 | }}]
27 | :release
28 | [:android-common
29 | {:target-path "target/release"
30 | :android
31 | {;; :keystore-path "/home/user/.android/private.keystore"
32 | ;; :key-alias "mykeyalias"
33 | ;; :sigalg "MD5withRSA"
34 |
35 | :use-debug-keystore true
36 | :ignore-log-priority [:debug :verbose]
37 | :aot :all
38 | :build-type :release}}]
39 |
40 | :lean
41 | [:release
42 | {:dependencies ^:replace [[org.skummet/clojure "{{skummet-version}}"]
43 | [neko/neko "{{neko-version}}"]]
44 | :exclusions [[org.clojure/clojure]
45 | [org.clojure-android/clojure]]
46 | :jvm-opts ["-Dclojure.compile.ignore-lean-classes=true"]
47 | :android {:lean-compile true
48 | :proguard-execute true
49 | :proguard-conf-path "build/proguard-minify.cfg"}}]}
50 |
51 | :android {;; Specify the path to the Android SDK directory.
52 | ;; :sdk-path "/home/user/path/to/android-sdk/"
53 |
54 | ;; Increase this value if dexer fails with OutOfMemoryException.
55 | :dex-opts ["-JXmx4096M" "--incremental"]
56 |
57 | :target-version "{{target-sdk}}"
58 | :aot-exclude-ns ["clojure.parallel" "clojure.core.reducers"
59 | "cider.nrepl" "cider-nrepl.plugin"
60 | "cider.nrepl.middleware.util.java.parser"
61 | #"cljs-tooling\..+"]})
62 |
--------------------------------------------------------------------------------
/res/templates/splash_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/res/templates/splash_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/res/templates/splash_circle.png
--------------------------------------------------------------------------------
/res/templates/splash_droid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/res/templates/splash_droid.png
--------------------------------------------------------------------------------
/res/templates/splash_hands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/res/templates/splash_hands.png
--------------------------------------------------------------------------------
/res/templates/splash_rotation.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/res/templates/splashscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
19 |
20 |
25 |
26 |
27 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
50 |
51 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/res/templates/strings.library.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{app-name}}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/res/templates/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{app-name}}
5 | Your input is empty
6 | Your input: %1$s
7 | Touch me
8 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | /gen
3 | /lib
4 | /classes
5 | /checkouts
6 | pom.xml
7 | *.jar
8 | *.class
9 | .lein-deps-sum
10 | .lein-failures
11 | .lein-plugins
12 | .nrepl-port
13 |
--------------------------------------------------------------------------------
/sample/AndroidManifest.template.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
13 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {{#debug-build}}
29 |
30 |
31 |
32 |
33 | {{/debug-build}}
34 |
35 |
--------------------------------------------------------------------------------
/sample/README.md:
--------------------------------------------------------------------------------
1 | # sample
2 |
3 | This is an application for testing the lein-droid plugin for Leiningen
4 |
5 | ## Usage
6 |
7 | FIXME
8 |
9 | ## License
10 |
11 | Copyright © 2012 FIXME
12 |
13 | Distributed under the Eclipse Public License, the same as Clojure.
14 |
--------------------------------------------------------------------------------
/sample/build/proguard-minify.cfg:
--------------------------------------------------------------------------------
1 | # This is a configuration file for ProGuard.
2 | # http://proguard.sourceforge.net/index.html#manual/usage.html
3 |
4 | -libraryjars /lib/rt.jar
5 |
6 | -dontusemixedcaseclassnames
7 | -dontskipnonpubliclibraryclasses
8 | -verbose
9 |
10 | # Optimization is turned off by default. Dex does not like code run
11 | # through the ProGuard optimize and preverify steps (and performs some
12 | # of these optimizations on its own).
13 | # -optimizations code/removal/advanced
14 | # !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
15 | # -optimizationpasses 2
16 | -dontoptimize
17 | -dontpreverify
18 | -dontobfuscate
19 | -dontnote
20 |
21 | # Note that if you want to enable optimization, you cannot just include
22 | # optimization flags in your own project configuration file; instead use
23 | # "proguard-android-optimize.txt" file instead from your SDK folder.
24 |
25 | -keepattributes *Annotation*
26 | -keep public class com.google.vending.licensing.ILicensingService
27 | -keep public class com.android.vending.licensing.ILicensingService
28 |
29 | # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
30 | -keepclasseswithmembernames class * {
31 | native ;
32 | }
33 |
34 | # keep setters in Views so that animations can still work.
35 | # see http://proguard.sourceforge.net/manual/examples.html#beans
36 | -keepclassmembers public class * extends android.view.View {
37 | void set*(***);
38 | *** get*();
39 | }
40 |
41 | # We want to keep methods in Activity that could be used in the XML attribute onClick
42 | -keepclassmembers class * extends android.app.Activity {
43 | public void *(android.view.View);
44 | public void super*(...);
45 | }
46 |
47 | # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
48 | -keepclassmembers enum * {
49 | public static **[] values();
50 | public static ** valueOf(java.lang.String);
51 | }
52 |
53 | -keepclassmembers class **.R$* {
54 | public static ;
55 | }
56 |
57 | -keep public class clojure.lang.Fn
58 | -keep public class neko.App
59 | -keep public class **__init
60 |
61 | -keep public class test.leindroid.sample.*
62 |
63 | # The support library contains references to newer platform versions.
64 | # Don't warn about those in case this app is linking against an older
65 | # platform version. We know about them, and they are safe.
66 | # -dontwarn android.support.**
67 |
68 | # Uncomment if you want to know why specific class is kept
69 | -whyareyoukeeping class clojure.core$subvec# neko.tools.repl$patch_unsupported_dependencies
70 |
71 |
--------------------------------------------------------------------------------
/sample/build/proguard-multi-dex.cfg:
--------------------------------------------------------------------------------
1 | -dontoptimize
2 | -dontpreverify
3 | -dontobfuscate
4 | -dontwarn
5 | -dontnote
6 | -forceprocessing
7 |
8 | -keep class clojure.**
9 |
10 | -keep public class * extends android.app.Instrumentation {
11 | ();
12 | }
13 | -keep public class * extends android.app.Application {
14 | ();
15 | void attachBaseContext(android.content.Context);
16 | }
17 | -keep public class * extends android.app.Activity {
18 | ();
19 | }
20 | -keep public class * extends android.app.Service {
21 | ();
22 | }
23 | -keep public class * extends android.content.ContentProvider {
24 | ();
25 | }
26 | -keep public class * extends android.content.BroadcastReceiver {
27 | ();
28 | }
29 | -keep public class * extends android.app.backup.BackupAgent {
30 | ();
31 | }
32 | # We need to keep all annotation classes because proguard does not trace annotation attribute
33 | # it just filter the annotation attributes according to annotation classes it already kept.
34 | -keep public class * extends java.lang.annotation.Annotation {
35 | *;
36 | }
37 |
--------------------------------------------------------------------------------
/sample/project.clj:
--------------------------------------------------------------------------------
1 | (defproject sample/sample "0.0.1-SNAPSHOT"
2 | :description "Sample Android project to test lein-droid plugin."
3 | :url "http://example.com/FIXME"
4 | :license {:name "Eclipse Public License"
5 | :url "http://www.eclipse.org/legal/epl-v10.html"}
6 |
7 | :global-vars {*warn-on-reflection* true}
8 |
9 | :source-paths ["src/clojure" "src"]
10 | :java-source-paths ["src/java"]
11 | :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"]
12 |
13 | :plugins [[lein-droid "0.4.6"]]
14 |
15 | :dependencies [[org.clojure-android/clojure "1.7.0-RC1" :use-resources true]
16 | ;; [com.google.android.gms/play-services "7.0.0" :extension "aar"]
17 | ;; [com.android.support/appcompat-v7 "18.0.0" :extension "aar"]
18 | [neko/neko "4.0.0-alpha5"]]
19 |
20 | :profiles {:default [:dev]
21 |
22 | :dev
23 | [:android-common :android-user
24 | ;; The above profiles can be specified in your profiles.clj and
25 | ;; contain machine-specific options such as {:android {:sdk-path
26 | ;; "/path/to/sdk"}}. :android-user profile is for global
27 | ;; dev-related options like CIDER configuration.
28 |
29 | {:dependencies [[org.clojure/tools.nrepl "0.2.10"]]
30 | :target-path "target/debug"
31 | :android {:aot :all-with-unused
32 | ;; The namespace of the app package - having a
33 | ;; different one for dev and release allows you to
34 | ;; install both at the same time.
35 | :rename-manifest-package "test.leindroid.sample.debug"
36 | :manifest-options {:app-name "Android-Clojure (debug)"}
37 | }}]
38 |
39 | :release
40 | [:android-common
41 | {:target-path "target/release"
42 | :android { ;; Specify the path to your private keystore and the
43 | ;; the alias of the key you want to sign APKs with.
44 | ;; :keystore-path "/home/user/.android/private.keystore"
45 | ;; :key-alias "mykeyalias"
46 | ;; :sigalg "MD5withRSA"
47 |
48 | ;; You can specify these to avoid entering them for
49 | ;; each rebuild, but generally it's a bad idea.
50 | ;; :keypass "android"
51 | ;; :storepass "android"
52 |
53 | :ignore-log-priority [:debug :verbose]
54 | :aot :all
55 |
56 | ;; This tells lein-droid to build in release mode,
57 | ;; disabling debugging and signing the resulting
58 | ;; package.
59 | :build-type :release}}]
60 |
61 | :lean
62 | [:release
63 | {:dependencies ^:replace [[org.skummet/clojure "1.7.0-r1"]
64 | [neko/neko "4.0.0-alpha5"]]
65 | :exclusions [[org.clojure/clojure]
66 | [org.clojure-android/clojure]]
67 | :jvm-opts ["-Dclojure.compile.ignore-lean-classes=true"]
68 | :global-vars ^:replace {clojure.core/*warn-on-reflection* true}
69 | :android {:use-debug-keystore true
70 | :proguard-execute true
71 | :proguard-conf-path "build/proguard-minify.cfg"
72 | :lean-compile true
73 | :skummet-skip-vars [;; You can list here var names that
74 | ;; you want to keep non-lean. E.g.:
75 | ;; "#'foo.bar/my-function"
76 | ]}}]
77 |
78 | ;; Here's an example of using different profiles
79 | :trial-version-dev
80 | [:dev ; Inherits from :dev profile
81 | {:android {:rename-manifest-package "my.sample.app.dev.trial"
82 | ;; And then some options which might be
83 | ;; additional/different source-paths to pull in different
84 | ;; code, or a manifest option which configures some aspect
85 | ;; of your application.
86 | }}]}
87 |
88 | :android {;; Specify the path to the Android SDK directory either here or in
89 | ;; :android-common profile in your ~/.lein/profiles.clj file.
90 | ;; :sdk-path "/home/user/path/to/android-sdk/"
91 |
92 | ;; Use this if you don't want to use the latest version of
93 | ;; Android Build Tools.
94 | ;; :build-tools-version "19.0.3"
95 |
96 | ;; Specify this if your project is a library.
97 | ;; :library true
98 |
99 | :dex-opts ["-JXmx4096M" "--incremental"]
100 |
101 | ;; If you hit the 65k method limit while dexing, uncomment the
102 | ;; following lines. Also remove the --incremental option above.
103 | ;; :multi-dex true
104 | ;; :multi-dex-proguard-conf-path "build/proguard-multi-dex.cfg"
105 |
106 | ;; Sequence of external jars or class folders to include
107 | ;; into project.
108 | ;; :external-classes-paths ["path/to/external/jar/file"
109 | ;; "path/to/classfiles/"]
110 |
111 | ;; Sequence of jars, resources from which will be added to
112 | ;; application package.
113 | ;; :resource-jars-paths ["path/to/resource/jar"]
114 |
115 | ;; Sequence of native libraries files that will be added
116 | ;; to application package.
117 | ;; :native-libraries-paths ["path/to/native/library"]
118 |
119 | ;; Target version affects api used for compilation.
120 | :target-version 18
121 |
122 | ;; Sequence of namespaces that should not be compiled.
123 | :aot-exclude-ns ["clojure.parallel" "clojure.core.reducers"]
124 |
125 | ;; This specifies replacements which are inserted into
126 | ;; AndroidManifest-template.xml at build time. See Clostache for
127 | ;; more advanced substitution syntax. Version name and code are
128 | ;; automatically inserted
129 | :manifest-options {:app-name "@string/app_name"}
130 | })
131 |
--------------------------------------------------------------------------------
/sample/res/anim/splash_rotation.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/sample/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/res/drawable-hdpi/splash_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/sample/res/drawable-hdpi/splash_circle.png
--------------------------------------------------------------------------------
/sample/res/drawable-hdpi/splash_droid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/sample/res/drawable-hdpi/splash_droid.png
--------------------------------------------------------------------------------
/sample/res/drawable-hdpi/splash_hands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/sample/res/drawable-hdpi/splash_hands.png
--------------------------------------------------------------------------------
/sample/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/clojure-android/lein-droid/1e0f8d5a5dd3c53232e21a1874867c212edb9848/sample/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/res/drawable/splash_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/res/layout/splashscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
19 |
20 |
25 |
26 |
27 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
50 |
51 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/sample/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Android-Clojure
5 | Your input is empty
6 | Your input: %1$s
7 | Touch me
8 |
9 |
10 |
--------------------------------------------------------------------------------
/sample/src/clojure/test/leindroid/sample/main.clj:
--------------------------------------------------------------------------------
1 | (ns test.leindroid.sample.main
2 | (:require [neko.activity :refer [defactivity set-content-view!]]
3 | [neko.debug :refer [*a]]
4 | [neko.notify :refer [toast]]
5 | [neko.resource :as res]
6 | [neko.find-view :refer [find-view]]
7 | [neko.threading :refer [on-ui]])
8 | (:import android.widget.EditText))
9 |
10 | ;; We execute this function to import all subclasses of R class. This gives us
11 | ;; access to all application resources.
12 | (res/import-all)
13 |
14 | (defn notify-from-edit
15 | "Finds an EditText element with ID ::user-input in the given activity. Gets
16 | its contents and displays them in a toast if they aren't empty. We use
17 | resources declared in res/values/strings.xml."
18 | [activity]
19 | (let [^EditText input (.getText (find-view activity ::user-input))]
20 | (toast (if (empty? input)
21 | (res/get-string R$string/input_is_empty)
22 | (res/get-string R$string/your_input_fmt input))
23 | :long)))
24 |
25 | ;; This is how an Activity is defined. We create one and specify its onCreate
26 | ;; method. Inside we create a user interface that consists of an edit and a
27 | ;; button. We also give set callback to the button.
28 | (defactivity test.leindroid.sample.MainActivity
29 | :key :main
30 |
31 | (onCreate [this bundle]
32 | (.superOnCreate this bundle)
33 | (neko.debug/keep-screen-on this)
34 | (on-ui
35 | (set-content-view! (*a)
36 | [:linear-layout {:orientation :vertical
37 | :layout-width :fill
38 | :layout-height :wrap}
39 | [:edit-text {:id ::user-input
40 | :hint "Type text here"
41 | :layout-width :fill}]
42 | [:button {:text R$string/touch_me ;; We use resource here, but could
43 | ;; have used a plain string too.
44 | :on-click (fn [_] (notify-from-edit (*a)))}]]))))
45 |
--------------------------------------------------------------------------------
/sample/src/java/test/leindroid/sample/SplashActivity.java:
--------------------------------------------------------------------------------
1 | package test.leindroid.sample;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.animation.Animation;
7 | import android.view.animation.AnimationUtils;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 | import neko.App;
11 |
12 | import test.leindroid.sample.R;
13 |
14 | public class SplashActivity extends Activity {
15 |
16 | private static boolean firstLaunch = true;
17 |
18 | @Override
19 | public void onCreate(Bundle bundle) {
20 | super.onCreate(bundle);
21 |
22 | if (firstLaunch) {
23 | firstLaunch = false;
24 | setupSplash();
25 | App.loadAsynchronously("test.leindroid.sample.MainActivity",
26 | new Runnable() {
27 | @Override
28 | public void run() {
29 | proceed();
30 | }});
31 | } else {
32 | proceed();
33 | }
34 | }
35 |
36 | public void setupSplash() {
37 | setContentView(R.layout.splashscreen);
38 |
39 | TextView appNameView = (TextView)findViewById(R.id.splash_app_name);
40 | appNameView.setText(R.string.app_name);
41 |
42 | Animation rotation = AnimationUtils.loadAnimation(this, R.anim.splash_rotation);
43 | ImageView circleView = (ImageView)findViewById(R.id.splash_circles);
44 | circleView.startAnimation(rotation);
45 | }
46 |
47 | public void proceed() {
48 | startActivity(new Intent("test.leindroid.sample.MAIN"));
49 | finish();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/lein_droid/plugin.clj:
--------------------------------------------------------------------------------
1 | (ns lein-droid.plugin
2 | "Hosts middleware function to be applied early to the project map."
3 | (:require [leiningen.droid.classpath :refer [init-hooks]]
4 | [leiningen.droid.utils :refer [android-parameters]]))
5 |
6 | (defn middleware
7 | "Lein-droid's middleware adds default Android parameters to `:android` map,
8 | and also adds local Maven repositories from Android SDK."
9 | [project]
10 | (init-hooks)
11 | (android-parameters project))
12 |
--------------------------------------------------------------------------------
/src/leiningen/droid.clj:
--------------------------------------------------------------------------------
1 | ;; ## Clojure is simple. Android should also be.
2 | ;; This plugin is intended to make your Clojure/Android development as
3 | ;; seamless and efficient as when developing ordinar Clojure JVM programs.
4 | ;;
5 | (ns leiningen.droid
6 | (:refer-clojure :exclude [compile doall repl])
7 | (:require clojure.pprint
8 | [leiningen.droid.aar :refer [extract-aar-dependencies]]
9 | [leiningen.droid.code-gen :refer [code-gen]])
10 | (:use [leiningen.core.project :only [set-profiles]]
11 | [leiningen.core.main :only [abort]]
12 | [leiningen.help :only (subtask-help-for)]
13 | [leiningen.clean :only [clean]]
14 | [leiningen.droid.compile :only [compile]]
15 | [leiningen.droid
16 | [classpath :only [init-hooks]]
17 | [build :only [create-dex
18 | crunch-resources package-resources create-apk
19 | sign-apk zipalign-apk apk build jar aar]]
20 | [deploy :only [install run forward-port repl deploy local-repo]]
21 | [new :only [new init]]
22 | [test :only [local-test]]
23 | [utils :only [proj wrong-usage android-parameters sdk-sanity-check
24 | dev-build?]]]))
25 |
26 | (defn help
27 | "Shows the list of possible `lein droid` subtasks."
28 | ([]) ([droid-var]
29 | (println "lein-droid is a plugin for Clojure/Android development."
30 | (subtask-help-for nil droid-var))))
31 |
32 | (defn pprint
33 | "Pretty-prints a representation of the project map."
34 | [project & keys]
35 | (if (seq keys)
36 | (clojure.pprint/pprint (select-keys project (map read-string keys)))
37 | (clojure.pprint/pprint project))
38 | (flush))
39 |
40 | (declare execute-subtask)
41 |
42 | (defn doall
43 | "Metatask. Performs all Android tasks from compilation to deployment."
44 | [{{:keys [library]} :android :as project} & device-args]
45 | (let [build-steps (if library ["build"] ["build" "apk" "deploy"])]
46 | (doseq [task build-steps]
47 | (execute-subtask project task device-args))))
48 |
49 | (defn ^{:no-project-needed true
50 | :subtasks [#'new #'init #'code-gen #'compile #'create-dex
51 | #'crunch-resources #'package-resources
52 | #'create-apk #'sign-apk #'zipalign-apk
53 | #'install #'run #'forward-port #'repl
54 | #'build #'apk #'deploy #'doall #'help #'local-test
55 | #'jar #'pprint]}
56 | droid
57 | "Supertask for Android-related tasks (see `lein droid` for list)."
58 | ([project]
59 | (help #'droid))
60 | ([project & [cmd & args]]
61 | (init-hooks)
62 | (if (#{"new" "help" "init"} cmd)
63 | (execute-subtask nil cmd args)
64 | (let [env-project (System/getenv "LEIN_DROID_PROJECT")
65 | project (if-not (empty? env-project) (proj env-project) project)]
66 | (cond (= cmd "pprint") (execute-subtask project cmd args)
67 | project (doto project
68 | sdk-sanity-check
69 | extract-aar-dependencies
70 | (execute-subtask cmd args))
71 | :else (abort "Subtask" cmd "should be run from the project folder."))))))
72 |
73 | (defn execute-subtask
74 | "Executes a subtask defined by `name` on the given project."
75 | [project name args]
76 | (case name
77 | ;; Standalone tasks
78 | "new" (if (< (count args) 2)
79 | (abort (wrong-usage "lein droid new" #'new))
80 | (apply new args))
81 | "init" (init (.getAbsolutePath (clojure.java.io/file ".")))
82 | "code-gen" (code-gen project)
83 | "compile" (compile project)
84 | "create-dex" (create-dex project)
85 | "crunch-resources" (crunch-resources project)
86 | "package-resources" (package-resources project)
87 | "create-apk" (create-apk project)
88 | "sign-apk" (sign-apk project)
89 | "zipalign-apk" (zipalign-apk project)
90 | "install" (apply install project args)
91 | "run" (apply run project args)
92 | "forward-port" (apply forward-port project args)
93 | "repl" (repl project)
94 | "clean" (clean project)
95 | "local-repo" (local-repo project)
96 |
97 | ;; Test tasks
98 | "local-test" (apply local-test project args)
99 |
100 | ;; Meta tasks
101 | "build" (build project)
102 | "apk" (apk project)
103 | "deploy" (apply deploy project args)
104 | "doall" (apply doall project args)
105 | "jar" (jar project)
106 | "aar" (aar project)
107 |
108 | ;; Help tasks
109 | "pprint" (apply pprint project args)
110 | "help" (help #'droid)
111 |
112 | (println "Subtask is not recognized:" name
113 | (subtask-help-for nil #'droid))))
114 |
--------------------------------------------------------------------------------
/src/leiningen/droid/aar.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.aar
2 | "Utilities for manipulating Android package format (AAR)."
3 | (:require [clojure.java.io :as io]
4 | [clojure.edn :as edn]
5 | [leiningen.droid.utils :refer [get-dependencies]]
6 | [leiningen.core.main :refer [debug]])
7 | (:import java.io.File
8 | net.lingala.zip4j.core.ZipFile))
9 |
10 | (defn- get-aar-dependencies
11 | "Returns a list of artifact dependencies that have `aar` extension."
12 | [project]
13 | (let [deps (get-dependencies project)]
14 | (for [[[art-id ver & opts :as dep]] deps
15 | :let [opts (apply hash-map opts)]
16 | :when (= (:extension opts) "aar")]
17 | dep)))
18 |
19 | (defn- str-dependency
20 | "Takes a dependency vector and returns its stringified version to be used in a
21 | file system."
22 | [dep]
23 | (-> (meta dep)
24 | :dependency
25 | .getArtifact
26 | str
27 | (.replace ":" "_")))
28 |
29 | (defn extract-aar-dependencies
30 | "Unpacks all AAR dependencies of the project into the target directory."
31 | [{:keys [target-path] :as project}]
32 | (let [deps (set (get-aar-dependencies project))
33 | aar-extracted-dir (io/file target-path "aar-extracted")
34 | ;; Read which AARs we already extracted to avoid doing it again.
35 | extracted-file (io/file aar-extracted-dir "extracted.edn")
36 | already-extracted (when (.exists ^File extracted-file)
37 | (edn/read-string (slurp extracted-file)))]
38 | (when-not (or (empty? deps) (= deps already-extracted))
39 | (debug "Extracting AAR dependencies: " deps)
40 | (doseq [dep deps]
41 | (.extractAll (ZipFile. (:file (meta dep)))
42 | (str (io/file aar-extracted-dir (str-dependency dep)))))
43 | (spit extracted-file deps))))
44 |
45 | (defn get-aar-files
46 | "Returns the list of files or directories specified by `subpath` extracted
47 | from each AAR dependency."
48 | [{:keys [target-path] :as project} & subpath]
49 | (let [aar-extracted-dir (io/file target-path "aar-extracted")]
50 | (for [dep (get-aar-dependencies project)]
51 | (apply io/file aar-extracted-dir (str-dependency dep) subpath))))
52 |
53 | (defn get-aar-classes
54 | "Returns the list of all jars extracted from all AAR dependencies."
55 | [project]
56 | (let [classes-jars (get-aar-files project "classes.jar")
57 | libs-dirs (get-aar-files project "libs")]
58 | (concat (filter #(.exists ^File %) classes-jars)
59 | (mapcat #(.listFiles ^File %) libs-dirs))))
60 |
61 | (defn get-aar-native-paths
62 | "Returns the list of existing paths to native libraries extracted from AAR
63 | dependencies."
64 | [project]
65 | (filter #(.exists ^File %) (get-aar-files project "jni")))
66 |
--------------------------------------------------------------------------------
/src/leiningen/droid/build.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.build
2 | "A set of functions and subtasks responsible for building the
3 | Android project."
4 | (:refer-clojure :exclude [compile])
5 | (:use [leiningen.core
6 | [main :only [debug info abort *debug*]]]
7 | [leiningen.droid
8 | [compile :only [compile]]
9 | [utils :only [get-sdk-android-jar sh dev-build?
10 | ensure-paths with-process read-password append-suffix
11 | create-debug-keystore read-project resolve-dependencies
12 | sdk-binary relativize-path get-sdk-annotations-jar
13 | get-resource-jars get-sdk-build-tools-path]]])
14 | (:require [clojure.string :as str]
15 | [clojure.set :as set]
16 | [clojure.java.io :as io]
17 | leiningen.core.project
18 | [leiningen.droid
19 | [code-gen :refer [code-gen]]
20 | [aar :refer [get-aar-files]]
21 | [manifest :refer [get-package-name]]
22 | [sdk :as sdk]]
23 | [leiningen.jar :as jar]
24 | leiningen.javac leiningen.pom)
25 | (:import java.io.File
26 | net.lingala.zip4j.core.ZipFile
27 | net.lingala.zip4j.model.ZipParameters
28 | net.lingala.zip4j.util.Zip4jConstants))
29 |
30 | ;; ### Build-related subtasks
31 |
32 | (defn- run-proguard-minifying
33 | "Run proguard on the compiled classes and dependencies, create an JAR with
34 | minimized and shaken classes."
35 | [{{:keys [external-classes-paths
36 | proguard-conf-path proguard-opts proguard-output-jar-path]} :android
37 | compile-path :compile-path :as project}]
38 | (info "Running Proguard...")
39 | (ensure-paths compile-path proguard-conf-path)
40 | (let [proguard-bin (sdk-binary project :proguard)
41 | android-jar (get-sdk-android-jar project)
42 | annotations (get-sdk-annotations-jar project)
43 | deps (resolve-dependencies project)
44 | external-paths (or external-classes-paths [])
45 | proguard-opts (or proguard-opts [])]
46 | (sh proguard-bin (str "@" proguard-conf-path)
47 | "-injars" (->> (concat [compile-path] deps external-paths)
48 | (map str)
49 | (str/join ":"))
50 | "-libraryjars" (->> [annotations android-jar]
51 | (map str)
52 | (str/join ":"))
53 | "-outjars" proguard-output-jar-path
54 | proguard-opts)))
55 |
56 | (defn- run-proguard-multidexing
57 | "Run proguard on the compiled classes and dependencies to determine which
58 | classes have to be kept in primary dex."
59 | [{{:keys [multi-dex-proguard-conf-path multi-dex-root-classes-path]} :android
60 | :as project} target-paths]
61 | (ensure-paths multi-dex-proguard-conf-path)
62 | (let [proguard-bin (sdk-binary project :proguard)
63 | android-jar (io/file (get-sdk-build-tools-path project)
64 | "lib" "shrinkedAndroid.jar")]
65 | (sh proguard-bin (str "@" multi-dex-proguard-conf-path)
66 | "-injars" (str/join ":" target-paths)
67 | "-libraryjars" (str android-jar)
68 | "-outjars" multi-dex-root-classes-path)))
69 |
70 | (defn- generate-main-dex-list
71 | "Creates a text file with the list of classes that should be included into
72 | primary dex."
73 | [{{:keys [multi-dex-root-classes-path multi-dex-main-dex-list-path]} :android
74 | :as project}
75 | target-paths]
76 | (run-proguard-multidexing project target-paths)
77 | (let [dx-jar (io/file (get-sdk-build-tools-path project) "lib" "dx.jar")
78 | builder (ProcessBuilder.
79 | ["java" "-cp" (str dx-jar) "com.android.multidex.MainDexListBuilder"
80 | multi-dex-root-classes-path (str/join ":" target-paths)])
81 | process-name (.start builder)
82 | output (line-seq (io/reader (.getInputStream process-name)))
83 | writer (io/writer (io/file multi-dex-main-dex-list-path))]
84 | (binding [*out* writer]
85 | (doseq [line output]
86 | (println line)))
87 | (.waitFor process-name)))
88 |
89 | ;; Since the execution of `dx` takes a pretty lot of time we need to
90 | ;; ensure that its subprocess will be killed if user cancels the build
91 | ;; (sends SIGINT to leiningen). That is why we add a hook to the
92 | ;; runtime that will be triggered when Leiningen is closed.
93 | ;;
94 | (defn- run-dx
95 | "Run dex on the given target paths, each should be either a directory with
96 | .class files or a jar file."
97 | [{{:keys [out-dex-path force-dex-optimize dex-opts multi-dex
98 | multi-dex-root-classes-path multi-dex-main-dex-list-path]} :android :as project}
99 | target-paths]
100 | (if multi-dex
101 | (do (info "Creating multi DEX....")
102 | (generate-main-dex-list project target-paths))
103 | (info "Creating DEX...."))
104 | (let [dx-bin (sdk-binary project :dx)
105 | options (or dex-opts [])
106 | no-optimize (if (and (not force-dex-optimize) (dev-build? project))
107 | "--no-optimize" [])
108 | annotations (get-sdk-annotations-jar project)
109 | multi-dex (if multi-dex
110 | ["--multi-dex" "--main-dex-list" multi-dex-main-dex-list-path]
111 | [])]
112 | (with-process [proc (->> [dx-bin options "--dex" no-optimize multi-dex
113 | "--output" out-dex-path
114 | target-paths annotations]
115 | flatten
116 | (map str))]
117 | (.addShutdownHook (Runtime/getRuntime) (Thread. #(.destroy proc))))))
118 |
119 | (defn create-dex
120 | "Creates a DEX file from the compiled .class files."
121 | [{{:keys [sdk-path external-classes-paths
122 | proguard-execute proguard-output-jar-path]} :android,
123 | compile-path :compile-path :as project}]
124 | (if proguard-execute
125 | (do
126 | (run-proguard-minifying project)
127 | (run-dx project [proguard-output-jar-path]))
128 | (let [deps (resolve-dependencies project)
129 | external-classes-paths (or external-classes-paths [])]
130 | (run-dx project (concat [compile-path] deps external-classes-paths)))))
131 |
132 | (defn build
133 | "Metatask. Compiles the project and creates DEX."
134 | [{{:keys [library]} :android :as project}]
135 | (doto project
136 | code-gen compile create-dex))
137 |
138 | (defn jar
139 | "Metatask. Packages compiled Java files and Clojure sources into JAR.
140 |
141 | Same as `lein jar` but appends Android libraries to the classpath
142 | while compiling Java files."
143 | [project]
144 | (leiningen.javac/javac project)
145 | (jar/write-jar project (jar/get-jar-filename project)
146 | (#'jar/filespecs (dissoc project :java-source-paths)))
147 | (leiningen.pom/pom project))
148 |
149 | (defn aar
150 | "Metatask. Packages library into AAR archive."
151 | [{{:keys [manifest-path res-path gen-path assets-paths]} :android
152 | :keys [name version target-path compile-path root] :as project}]
153 | (code-gen project)
154 | (.renameTo (io/file gen-path "R.txt") (io/file target-path "R.txt"))
155 | (leiningen.javac/javac project)
156 | ;; Remove unnecessary files
157 | (.delete ^File (io/file gen-path "R.java.d"))
158 | (let [package-name (get-package-name manifest-path)
159 | classes-path (apply io/file compile-path (str/split package-name #"\."))]
160 | (doseq [^File file (.listFiles ^File classes-path)
161 | :let [filename (.getName file)]
162 | :when (or (= filename "R.class")
163 | (re-matches #"R\$\w+\.class" filename))]
164 | (.delete file)))
165 | ;; Make a JAR
166 | (jar/write-jar project (io/file target-path "classes.jar")
167 | (#'jar/filespecs (dissoc project :java-source-paths)))
168 | ;; Finally create AAR file
169 | (let [zip (ZipFile. (io/file target-path (format "%s-%s.aar" name version)))
170 | params (doto (ZipParameters.)
171 | (.setCompressionMethod Zip4jConstants/COMP_STORE)
172 | ;; (.setDefaultFolderPath "target")
173 | (.setEncryptFiles false))]
174 | (.addFile zip (io/file manifest-path) params)
175 | (.addFile zip (io/file target-path "classes.jar") params)
176 | (.addFile zip (io/file target-path "R.txt") params)
177 | (.addFolder zip (io/file res-path) params)
178 | (when (.exists (io/file root "libs"))
179 | (.addFolder zip (io/file root "libs") params))
180 | (doseq [path assets-paths
181 | :when (.exists (io/file path))]
182 | (.addFolder zip (io/file path) params)))
183 | (leiningen.pom/pom (assoc project :packaging "aar")))
184 |
185 | ;; ### APK-related subtasks
186 |
187 | ;; Because of the AAPT bug we must turn paths to files here so that proper
188 | ;; canonical names are calculated.
189 | ;;
190 | (defn crunch-resources
191 | "Updates the pre-processed PNG cache.
192 |
193 | Calls `aapt` binary with the _crunch_ task."
194 | [{{:keys [res-path out-res-path]} :android :as project}]
195 | (info "Crunching resources...")
196 | (ensure-paths res-path)
197 | (let [aapt-bin (sdk-binary project :aapt)
198 | crunch (fn [src-dir target-dir]
199 | (sh aapt-bin "crunch -v" "-S" src-dir "-C" target-dir))]
200 | (doseq [aar (get-aar-files project)
201 | :when (.exists ^File (io/file aar "R.txt"))
202 | :let [out (io/file aar "out-res")]]
203 | (.mkdirs ^File out)
204 | (crunch (io/file aar "res") out))
205 | (crunch (io/file res-path) (io/file out-res-path))))
206 |
207 | (defn package-resources
208 | "Packages application resources."
209 | [{{:keys [sdk-path target-version manifest-path assets-paths res-path
210 | out-res-path external-res-paths out-res-pkg-path
211 | rename-manifest-package assets-gen-path]} :android :as project}]
212 | (info "Packaging resources...")
213 | (ensure-paths sdk-path manifest-path res-path)
214 | (let [aapt-bin (sdk-binary project :aapt)
215 | android-jar (get-sdk-android-jar sdk-path target-version)
216 | debug-mode (if (dev-build? project) ["--debug-mode"] [])
217 | ;; Only add `assets` directories when they are present.
218 | assets (mapcat #(when (.exists (io/file %)) ["-A" (str %)])
219 | (concat assets-paths [assets-gen-path]
220 | (get-aar-files project "assets")))
221 | aar-resources (for [res (get-aar-files project "res")] ["-S" res])
222 | aar-crunched-resources (for [res (get-aar-files project "out-res")
223 | :when (.exists ^File res)]
224 | ["-S" res])
225 | external-resources (for [res external-res-paths] ["-S" res])]
226 | (sh aapt-bin "package" "--no-crunch" "-f" debug-mode "--auto-add-overlay"
227 | "-M" manifest-path
228 | "-S" out-res-path
229 | "-S" res-path
230 | aar-crunched-resources
231 | aar-resources
232 | external-resources
233 | assets
234 | "-I" android-jar
235 | "-F" out-res-pkg-path
236 | "--generate-dependencies"
237 | (if rename-manifest-package
238 | ["--rename-manifest-package" rename-manifest-package] []))))
239 |
240 | (defn create-apk
241 | "Creates a deployment-ready APK file.
242 |
243 | It is done by executing methods from ApkBuilder SDK class on the
244 | generated DEX-file and the resource package."
245 | [{{:keys [out-apk-path out-res-pkg-path resource-jars-paths]} :android
246 | :as project}]
247 | (info "Creating APK...")
248 | (ensure-paths out-res-pkg-path)
249 | (let [unaligned-path (append-suffix out-apk-path "unaligned")
250 | resource-jars (concat (get-resource-jars project)
251 | (map io/file resource-jars-paths))]
252 | (sdk/create-apk project
253 | :apk-name unaligned-path :resource-jars resource-jars)))
254 |
255 | (defn sign-apk
256 | "Signs APK file with the key taken from the keystore.
257 |
258 | Either a debug keystore key or a release key is used based on
259 | whether the build type is the debug one. Creates a debug keystore if
260 | it is missing."
261 | [{{:keys [out-apk-path sigalg use-debug-keystore
262 | keystore-path key-alias keypass storepass]} :android :as project}]
263 | (info "Signing APK with" keystore-path "...")
264 | (let [debug (or (dev-build? project) use-debug-keystore
265 | (.endsWith keystore-path "debug.keystore"))
266 | unaligned-path (append-suffix out-apk-path "unaligned")
267 | sigalg (or sigalg "SHA1withRSA")]
268 | (when (and debug (not (.exists (io/file keystore-path))))
269 | ;; Create a debug keystore if there isn't one
270 | (create-debug-keystore keystore-path))
271 | (ensure-paths unaligned-path keystore-path)
272 | (let [storepass (or (when debug "android")
273 | storepass
274 | (System/getenv "STOREPASS")
275 | (read-password "Enter storepass: "))
276 | keypass (or (when debug "android")
277 | keypass
278 | (System/getenv "KEYPASS")
279 | (read-password "Enter keypass: "))]
280 | (sh "jarsigner"
281 | "-sigalg" sigalg
282 | "-digestalg" "SHA1"
283 | "-keystore" keystore-path
284 | "-storepass" storepass
285 | "-keypass" keypass
286 | unaligned-path key-alias))))
287 |
288 | (defn zipalign-apk
289 | "Aligns resources locations on 4-byte boundaries in the APK file.
290 |
291 | Done by calling `zipalign` binary on APK file."
292 | [{{:keys [sdk-path out-apk-path]} :android :as project}]
293 | (info "Aligning APK...")
294 | (let [zipalign-bin (sdk-binary project :zipalign)
295 | unaligned-path (append-suffix out-apk-path "unaligned")]
296 | (ensure-paths unaligned-path)
297 | (.delete (io/file out-apk-path))
298 | (sh zipalign-bin "4" unaligned-path out-apk-path)))
299 |
300 | (defn apk
301 | "Metatask. Crunches and packages resources, creates, signs and aligns an APK."
302 | [project]
303 | (doto project
304 | crunch-resources package-resources
305 | create-apk sign-apk zipalign-apk))
306 |
--------------------------------------------------------------------------------
/src/leiningen/droid/classpath.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.classpath
2 | "Contains functions and hooks for Android-specific classpath
3 | manipulation."
4 | (:require [leiningen.droid.aar :refer [get-aar-classes]]
5 | [leiningen.droid.utils :refer [get-sdk-android-jar
6 | get-sdk-annotations-jar
7 | leiningen-2-p-7-or-later?]]
8 | [robert.hooke :refer [add-hook]])
9 | (:import org.sonatype.aether.util.version.GenericVersionScheme))
10 |
11 | ;; Since `dx` and `ApkBuilder` utilities fail when they are feeded
12 | ;; repeated jar-files, we need to make sure that JAR dependencies list
13 | ;; contains only unique jars.
14 |
15 | (defn remove-duplicate-dependencies
16 | "Filters project's dependency list for unique jars regardless of
17 | version or groupId. Android-patched version of Clojure is prefered
18 | over the other ones. For the rest the latest version is preferred."
19 | [dependencies]
20 | (let [tagged (for [[artifact version :as dep] dependencies]
21 | (let [[_ group name] (re-matches #"(.+/)?(.+)" (str artifact))]
22 | {:name name, :group group, :ver version, :original dep}))
23 | grouped (group-by :name tagged)
24 | scheme (GenericVersionScheme.)]
25 | (for [[name same-jars] grouped]
26 | ;; For Clojure jar choose only from Android-specific versions
27 | ;; (if there is at least one).
28 | (let [same-jars (if (= name "clojure")
29 | (let [droid-clojures (filter #(= (:group %)
30 | "org.clojure-android/")
31 | same-jars)]
32 | (if-not (empty? droid-clojures)
33 | droid-clojures
34 | same-jars))
35 | same-jars)]
36 | (:original
37 | (reduce #(if (pos? (compare (.parseVersion scheme (or (:version %2)
38 | "0"))
39 | (.parseVersion scheme (or (:version %1)
40 | "0"))))
41 | %2 %1)
42 | same-jars))))))
43 |
44 | (defn- dependencies-hook
45 | "Takes the original `get-dependencies` function and arguments to it.
46 | Removes duplicate entries from the result when resolving project
47 | dependencies."
48 | [f dependency-key & rest]
49 | (let [[managed-deps project & rest] (if (leiningen-2-p-7-or-later?)
50 | rest (cons nil rest))
51 | all-deps (if (leiningen-2-p-7-or-later?)
52 | (apply f dependency-key managed-deps project rest)
53 | (apply f dependency-key project rest))]
54 | (if (= dependency-key :dependencies)
55 | ;; aether/dependency-files expects a map but uses keys only,
56 | ;; so we transform a list into a map with nil values.
57 | (zipmap (remove-duplicate-dependencies (keys all-deps))
58 | (repeat nil))
59 | all-deps)))
60 |
61 | (defn- resolve-dependencies-hook
62 | "Takes the original `resolve-dependencies` function and arguments to it.
63 | Appends jar files extracted from AAR dependencies."
64 | [f dependency-key project & rest]
65 | (let [deps (apply f dependency-key project rest)]
66 | (if (= dependency-key :dependencies)
67 | (concat deps (get-aar-classes project))
68 | deps)))
69 |
70 | (defn- resolve-managed-dependencies-hook
71 | "Takes the original `resolve-managed-dependencies` function and arguments to
72 | it. Appends jar files extracted from AAR dependencies."
73 | [f dependency-key managed-dependency-key project & rest]
74 | (let [deps (apply f dependency-key managed-dependency-key project rest)]
75 | (if (= dependency-key :dependencies)
76 | (->> (concat deps (get-aar-classes project))
77 | ;; resolve-managed-dependencies is called multiple times. We must
78 | ;; dedupe already added dependencies.
79 | (group-by str) vals (map first))
80 | deps)))
81 |
82 | ;; We also have to manually attach Android SDK libraries to the
83 | ;; classpath. The reason for this is that Leiningen doesn't handle
84 | ;; external dependencies at the high level, and Android jars are not
85 | ;; distributed in a convenient fashion (using Maven repositories). To
86 | ;; solve this we hack into `get-classpath` function.
87 |
88 | (defn classpath-hook
89 | "Takes the original `get-classpath` function and the project map,
90 | extracting the path to the Android SDK and the target version from it.
91 | Then the path to the actual `android.jar` file is constructed and
92 | appended to the rest of the classpath list."
93 | [f {{:keys [external-classes-paths]} :android :as project}]
94 | (let [classpath (f project)
95 | result (conj (concat classpath external-classes-paths)
96 | (get-sdk-android-jar project)
97 | (get-sdk-annotations-jar project))]
98 | result))
99 |
100 | (defn init-hooks []
101 | (add-hook #'leiningen.core.classpath/get-dependencies #'dependencies-hook)
102 | (add-hook #'leiningen.core.classpath/resolve-dependencies #'resolve-dependencies-hook)
103 | (when (leiningen-2-p-7-or-later?)
104 | (add-hook (resolve 'leiningen.core.classpath/resolve-managed-dependencies)
105 | #'resolve-managed-dependencies-hook))
106 | (add-hook #'leiningen.core.classpath/get-classpath #'classpath-hook))
107 |
--------------------------------------------------------------------------------
/src/leiningen/droid/code_gen.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.code-gen
2 | "Tasks and related functions for build-specific code generation."
3 | (:require [clojure.java.io :as io]
4 | [clojure.string :as str]
5 | [clostache.parser :as clostache]
6 | [leiningen.core.main :refer [debug info abort]]
7 | [leiningen.droid.aar :refer [get-aar-files]]
8 | [leiningen.droid.manifest :refer [get-package-name generate-manifest]]
9 | [leiningen.droid.sideload :as sideload]
10 | [leiningen.droid.utils :refer [get-sdk-android-jar sdk-binary
11 | ensure-paths sh dev-build?]]
12 | [leiningen.new.templates :refer [slurp-resource]])
13 | (:import java.io.File))
14 |
15 | ;; ### BuildConfig.java generation
16 |
17 | (defn- java-type
18 | "Mapping of classes to type strings as they should appear in BuildConfig."
19 | [x]
20 | (condp = (type x)
21 | Boolean "boolean"
22 | String "String"
23 | Long "long"
24 | Double "double"
25 | (abort ":build-config only supports boolean, String, long and double types.")))
26 |
27 | (defn map-constants
28 | "Transform a map of constants return to form readable by Clostache."
29 | [constants]
30 | (map (fn [[k v]]
31 | (binding [*print-dup* true]
32 | {:key k
33 | :value (pr-str v)
34 | :type (java-type v)}))
35 | constants))
36 |
37 | (defn generate-build-constants
38 | [{{:keys [manifest-path gen-path build-config rename-manifest-package]}
39 | :android, version :version :as project}]
40 | (ensure-paths manifest-path)
41 | (let [res (io/resource "templates/BuildConfig.java")
42 | package-name (get-package-name manifest-path)
43 | gen-package-path (apply io/file gen-path (str/split package-name #"\."))
44 | application-id (or rename-manifest-package package-name)
45 | template-constants (-> (merge {"VERSION_NAME" version
46 | "APPLICATION_ID" application-id}
47 | build-config)
48 | map-constants)]
49 | (ensure-paths gen-package-path)
50 | (->> {:debug (dev-build? project)
51 | :package-name package-name
52 | :constants template-constants}
53 | (clostache/render (slurp-resource res))
54 | (spit (io/file gen-package-path "BuildConfig.java")))))
55 |
56 | ;; ### R.java generation
57 |
58 | (defn create-r-file
59 | "Generates R.java file given full symbols file, library symbols file and
60 | library package name. Symbols file are loaded from respective R.txt files."
61 | [full-symbols lib-r-txt lib-package gen-path]
62 | (debug "Generating R.java file for:" lib-package)
63 | (let [symbols (sideload/symbol-loader lib-r-txt)
64 | writer (sideload/symbol-writer (str gen-path) lib-package full-symbols)]
65 | (.load symbols)
66 | (.addSymbolsToWrite writer symbols)
67 | (.write writer)))
68 |
69 | (defn generate-r-files
70 | "Generates R.java files for the project and all dependency libraries, having
71 | R.txt for project and each library."
72 | [{{:keys [sdk-path gen-path manifest-path]} :android :as project}]
73 | (sideload/sideload-jars sdk-path)
74 | (let [full-r-txt (io/file gen-path "R.txt")
75 | full-symbols (sideload/symbol-loader full-r-txt)]
76 | (.load full-symbols)
77 | (dorun
78 | (map (fn [manifest, ^File r-txt]
79 | (when (.exists r-txt)
80 | (let [package-name (get-package-name manifest)
81 | lib-gen-path gen-path]
82 | (create-r-file full-symbols r-txt package-name lib-gen-path))))
83 | (get-aar-files project "AndroidManifest.xml")
84 | (get-aar-files project "R.txt")))))
85 |
86 | (defn generate-resource-code
87 | "Generates R.java files for both the project and the libraries."
88 | [{{:keys [sdk-path target-version manifest-path res-path gen-path
89 | out-res-path external-res-paths library]} :android
90 | java-only :java-only :as project}]
91 | (info "Generating R.java files...")
92 | (let [aapt-bin (sdk-binary project :aapt)
93 | android-jar (get-sdk-android-jar sdk-path target-version)
94 | manifest-file (io/file manifest-path)
95 | library-specific (if library ["--non-constant-id"] [])
96 | aar-resources (for [res (get-aar-files project "res")] ["-S" (str res)])
97 | external-resources (for [res external-res-paths] ["-S" res])]
98 | (ensure-paths manifest-path res-path android-jar)
99 | (.mkdirs (io/file gen-path))
100 | (.mkdirs (io/file out-res-path))
101 | (sh aapt-bin "package" library-specific "-f" "-m"
102 | "-M" manifest-path
103 | "-S" out-res-path
104 | "-S" res-path
105 | aar-resources
106 | external-resources
107 | "-I" android-jar
108 | "-J" gen-path
109 | "--output-text-symbols" gen-path
110 | "--auto-add-overlay"
111 | "--generate-dependencies")
112 | ;; Finally generate R.java files having R.txt keys
113 | (when-not library
114 | (generate-r-files project))))
115 |
116 | (defn code-gen
117 | "Generates R.java and builds a manifest with the appropriate version
118 | code and substitutions."
119 | [{{:keys [library]} :android :as project}]
120 | (doto project
121 | generate-manifest generate-resource-code
122 | generate-build-constants))
123 |
--------------------------------------------------------------------------------
/src/leiningen/droid/compile.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.compile
2 | "This part of the plugin is responsible for the project compilation."
3 | (:refer-clojure :exclude [compile])
4 | (:require [bultitude.core :as bultitude]
5 | [clojure.java.io :as io]
6 | [clojure.set :as set]
7 | [leiningen.compile :refer [stale-namespaces]]
8 | [leiningen.core.classpath :refer [get-classpath]]
9 | [leiningen.core.eval :as eval]
10 | [leiningen.core.main :refer [debug info abort]]
11 | [leiningen.droid.manifest :refer [get-package-name]]
12 | [leiningen.droid.utils :refer [ensure-paths dev-build?]]
13 | leiningen.javac)
14 | (:import java.util.regex.Pattern))
15 |
16 | ;; ### Pre-compilation tasks
17 |
18 | (defn eval-in-project
19 | ([project form init]
20 | (eval/prep project)
21 | (eval/eval-in project
22 | `(do ~@(map (fn [[k v]] `(set! ~k ~v)) (:global-vars project))
23 | ~init
24 | ~@(:injections project)
25 | ~form)))
26 | ([project form] (eval-in-project project form nil)))
27 |
28 | (defn save-data-readers-to-resource
29 | "Save project's *data-readers* value to application's resources so
30 | it can be later retrieved in runtime. This is necessary to be able
31 | to use data readers when developing in REPL on the device."
32 | [{{:keys [assets-gen-path]} :android :as project}]
33 | (.mkdirs (io/file assets-gen-path))
34 | (eval-in-project
35 | project
36 | `(do (require 'clojure.java.io)
37 | (spit (clojure.java.io/file ~assets-gen-path "data_readers.clj")
38 | (into {} (map (fn [[k# v#]]
39 | [k# (symbol (subs (str v#) 2))])
40 | clojure.core/*data-readers*))))))
41 |
42 | ;; ### Compilation
43 |
44 | (defn namespaces-to-compile
45 | "Takes project and returns a set of namespaces that should be AOT-compiled."
46 | [{{:keys [aot aot-exclude-ns]} :android :as project}]
47 | (let [all-nses (bultitude/namespaces-on-classpath
48 | :classpath (map io/file (get-classpath project))
49 | :ignore-unreadable? false)
50 | include (case aot
51 | :all (stale-namespaces (assoc project :aot :all))
52 | :all-with-unused all-nses
53 | aot)
54 | exclude aot-exclude-ns
55 |
56 | {include-nses false, include-regexps true}
57 | (group-by #(instance? Pattern %) include)
58 |
59 | {exclude-nses false, exclude-regexps true}
60 | (group-by #(instance? Pattern %) exclude)]
61 | (->> (set/difference (set (map str (if (seq include-regexps)
62 | all-nses include-nses)))
63 | (set exclude-nses))
64 | (filter (fn [ns] (if (seq include-regexps)
65 | (some #(re-matches % ns) include-regexps)
66 | true)))
67 | (remove (fn [ns] (if (seq exclude-regexps)
68 | (some #(re-matches % ns) exclude-regexps)
69 | false)))
70 | (concat (if (seq include-regexps)
71 | include-nses ()))
72 | (map symbol))))
73 |
74 | (defn compile-clojure
75 | "Compiles Clojure files into .class files.
76 |
77 | If `:aot` project parameter equals `:all` then compiles the
78 | necessary dependencies. If `:aot` equals `:all-with-unused` then
79 | compiles all namespaces of the dependencies whether they were
80 | referenced in the code or not. The latter is useful for the
81 | REPL-driven development.
82 |
83 | Uses neko to set compilation flags. Some neko macros and
84 | subsequently project code depends on them to eliminate
85 | debug-specific code when building the release."
86 | [{{:keys [enable-dynamic-compilation start-nrepl-server
87 | manifest-path repl-device-port ignore-log-priority
88 | lean-compile skummet-skip-vars]}
89 | :android
90 | {:keys [nrepl-middleware]} :repl-options
91 | :as project}]
92 | (info "Compiling Clojure files...")
93 | (debug "Project classpath:" (get-classpath project))
94 | (let [nses (namespaces-to-compile project)
95 | dev-build (dev-build? project)
96 | package-name (try (get-package-name manifest-path)
97 | (catch Exception ex nil))
98 | opts (cond-> {:neko.init/release-build (not dev-build)
99 | :neko.init/start-nrepl-server start-nrepl-server
100 | :neko.init/nrepl-port repl-device-port
101 | :neko.init/enable-dynamic-compilation
102 | enable-dynamic-compilation
103 | :neko.init/ignore-log-priority ignore-log-priority
104 | :neko.init/nrepl-middleware (list 'quote nrepl-middleware)
105 | :neko.init/package-name package-name}
106 | (not dev-build) (assoc :elide-meta
107 | [:doc :file :line :column :added :author
108 | :static :arglists :forms]))]
109 | (info (format "Build type: %s, dynamic compilation: %s, remote REPL: %s."
110 | (if dev-build "debug" (if lean-compile "lean" "release"))
111 | (if (or dev-build start-nrepl-server
112 | enable-dynamic-compilation)
113 | "enabled" "disabled")
114 | (if (or dev-build start-nrepl-server) "enabled" "disabled")))
115 | (let [form
116 | (if lean-compile
117 | `(let [lean-var?# (fn [var#] (not (#{~@skummet-skip-vars}
118 | (str var#))))]
119 | (binding [~'clojure.core/*lean-var?* lean-var?#
120 | ~'clojure.core/*lean-compile* true
121 | ~'clojure.core/*compiler-options* ~opts]
122 | (doseq [namespace# '~nses]
123 | (println "Compiling" namespace#)
124 | (clojure.core/compile namespace#))
125 | (shutdown-agents)))
126 | `(binding [*compiler-options* ~opts]
127 | ;; If expectations is present, don't run it during compilation.
128 | (doseq [namespace# '~nses]
129 | (println "Compiling" namespace#)
130 | (clojure.core/compile namespace#))
131 | (try (require 'expectations)
132 | ((resolve 'expectations/disable-run-on-shutdown))
133 | (catch Throwable _# nil))
134 | (shutdown-agents)))]
135 | (.mkdirs (io/file (:compile-path project)))
136 | (try (eval-in-project project form)
137 | (info "Compilation succeeded.")
138 | (catch Exception e
139 | (abort "Compilation failed."))))))
140 |
141 | (defn compile
142 | "Compiles both Java and Clojure source files."
143 | [{{:keys [sdk-path gen-path lean-compile]} :android,
144 | java-only :java-only :as project}]
145 | (ensure-paths sdk-path)
146 | (let [project (-> project
147 | (update-in [:prep-tasks] (partial remove #{"compile"})))]
148 | (leiningen.javac/javac project)
149 | (when-not java-only
150 | (save-data-readers-to-resource project)
151 | (compile-clojure project))))
152 |
--------------------------------------------------------------------------------
/src/leiningen/droid/deploy.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.deploy
2 | "Functions and subtasks that install and run the application on the
3 | device and manage its runtime."
4 | (:use [leiningen.core.main :only [debug info abort *debug*]]
5 | [leiningen.droid.manifest :only (get-launcher-activity
6 | get-package-name)]
7 | [leiningen.droid.utils :only [sh ensure-paths append-suffix
8 | prompt-user sdk-binary]]
9 | [reply.main :only (launch-nrepl)])
10 | (:require [clojure.java.io :as io]
11 | [cemerick.pomegranate.aether :as aether]
12 | [reply.initialization :as reply-init]))
13 |
14 | (defn- device-list
15 | "Returns the list of currently attached devices."
16 | [adb-bin]
17 | (let [output (rest (sh adb-bin "devices"))] ;; Ignore the first line
18 | (remove nil?
19 | (map #(let [[_ serial type] (re-find #"([^\t]+)\t([^\t]+)" %)]
20 | (when serial
21 | {:serial serial, :type type}))
22 | output))))
23 |
24 | (defn- choose-device
25 | "If there is only one device attached returns its serial number,
26 | otherwise prompts user to choose the device to work with. If no
27 | devices are attached aborts the execution."
28 | [adb-bin]
29 | (let [devices (device-list adb-bin)]
30 | (case (count devices)
31 | 0 (abort "No devices are attached.")
32 | 1 (:serial (first devices))
33 | (do
34 | (dotimes [i (count devices)]
35 | (println (format "%d. %s\t%s" (inc i) (:serial (nth devices i))
36 | (:type (nth devices i)))))
37 | (print (format "Enter the number 1..%d to choose the device: "
38 | (count devices)))
39 | (flush)
40 | (if-let [answer (try (Integer/parseInt (read-line))
41 | (catch Exception ex))]
42 | (:serial (nth devices (dec answer)))
43 | (abort "Cannot recognize device number."))))))
44 |
45 | (defn get-device-args
46 | "Returns a list of adb arguments that specify the device adb should be
47 | working against. Calls `choose-device` if `device-args` parameter is
48 | nil."
49 | [adb-bin device-args]
50 | (or device-args
51 | (list "-s" (choose-device adb-bin))))
52 |
53 | (def ^{:doc "Messages which `adb install` prints as the result."
54 | :private true}
55 | adb-responses
56 | {"Success" :success
57 | "Failure [INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES]"
58 | :inconsistent-certificates})
59 |
60 | (def ^:private uninstall-prompt
61 | (str "Certificates of the installed application and the application being "
62 | "installed mismatch.\nDo you want to uninstall the old application "
63 | "first? (y/n): "))
64 |
65 | ;; Since `adb` command always returns exit code zero, we have to
66 | ;; manually parse its output to figure out what is going on. This is
67 | ;; why this subtask is full of low-level stuff.
68 | (defn install
69 | "Installs the APK on the only (or specified) device or emulator."
70 | [{{:keys [out-apk-path manifest-path rename-manifest-package]}
71 | :android :as project} & device-args]
72 | (info "Installing APK...")
73 | (let [adb-bin (sdk-binary project :adb)
74 | _ (ensure-paths out-apk-path)
75 | device (get-device-args adb-bin device-args)
76 | output (java.io.StringWriter.)]
77 | ;; Rebind *out* to get the output `adb` produces.
78 | (binding [*out* output, *debug* true]
79 | (sh adb-bin device "install" "-r" out-apk-path))
80 | (let [output (str output)
81 | response (some
82 | adb-responses
83 | (.split output (System/getProperty "line.separator")))]
84 | (case response
85 | :success (debug output)
86 |
87 | :inconsistent-certificates
88 | (let [resp (prompt-user uninstall-prompt)
89 | package-name (or rename-manifest-package
90 | (get-package-name manifest-path))]
91 | (if (.equalsIgnoreCase "y" resp)
92 | (do
93 | (sh adb-bin device "uninstall" package-name)
94 | (sh adb-bin device "install" out-apk-path))
95 | (abort "Cannot proceed with installation.")))
96 |
97 | (do (info output)
98 | (abort "Abort execution."))))))
99 |
100 | (defn run
101 | "Launches the installed APK on the connected device."
102 | [{{:keys [manifest-path launch-activity]} :android :as project}
103 | & device-args]
104 | (ensure-paths manifest-path)
105 | (when-let [activity (or launch-activity (get-launcher-activity project))]
106 | (info "Launching APK...")
107 | (let [adb-bin (sdk-binary project :adb)
108 | device (get-device-args adb-bin device-args)]
109 | (sh adb-bin device "shell" "am" "start" "-n" activity))))
110 |
111 | (defn forward-port
112 | "Binds a port on the local machine to the port on the device.
113 |
114 | This allows to connect to the remote REPL from the current machine."
115 | [{{:keys [repl-device-port repl-local-port]} :android, root :root :as project}
116 | & device-args]
117 | (info "Binding device port" repl-device-port
118 | "to local port" repl-local-port "...")
119 | (spit (io/file root ".nrepl-port") repl-local-port)
120 | (let [adb-bin (sdk-binary project :adb)
121 | device (get-device-args adb-bin device-args)]
122 | (sh adb-bin device "forward"
123 | (str "tcp:" repl-local-port)
124 | (str "tcp:" repl-device-port))))
125 |
126 | (defn default-init
127 | "Substitution for REPLy's own `default-init-function`."
128 | [{:keys [custom-help] :as options}]
129 | `(do
130 | ~@reply-init/prelude
131 |
132 | (use '[clojure.repl :only ~'[source apropos dir doc pst find-doc]])
133 | (use '[clojure.pprint :only ~'[pp pprint]])
134 |
135 | (defn ~'help
136 | "Prints a list of helpful commands."
137 | []
138 | (println " Exit: Control+D or (exit) or (quit)")
139 | (println " Commands: (user/help)")
140 | (println " Docs: (doc function-name-here)")
141 | (println " (find-doc \"part-of-name-here\")")
142 | (println " Source: (source function-name-here)"))
143 |
144 | (user/help)
145 |
146 | nil))
147 |
148 | (defn repl
149 | "Connects to a remote nREPL server on the device using REPLy."
150 | [{{:keys [repl-local-port]} :android}]
151 | (with-redefs [reply-init/default-init-code default-init]
152 | (launch-nrepl {:attach (str "localhost:" repl-local-port)})))
153 |
154 | (defn deploy
155 | "Metatask. Runs `install, `run`, `forward-port`."
156 | [project & device-args]
157 | (let [adb-bin (sdk-binary project :adb)
158 | device (get-device-args adb-bin device-args)]
159 | (apply install project device)
160 | (apply run project device)
161 | (apply forward-port project device)))
162 |
163 | (defn local-repo
164 | "Install the generated AAR package to the local Maven repository."
165 | [{:keys [target-path name group version root] :as project}]
166 | (leiningen.pom/pom (assoc project :packaging "aar"))
167 | (let [aar-file (io/file target-path (format "%s-%s.aar" name version))]
168 | (ensure-paths aar-file)
169 | (->> {[:extension "pom"] (io/file root "pom.xml")
170 | [:extension "aar"] aar-file}
171 | (#'aether/artifacts-for [(symbol group name) version])
172 | (aether/install-artifacts :files))))
173 |
--------------------------------------------------------------------------------
/src/leiningen/droid/manifest.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.manifest
2 | "Contains functions to manipulate AndroidManifest.xml file"
3 | (:require [clojure.data.zip.xml :refer :all]
4 | [clojure.xml :as xml]
5 | [clojure.java.io :as jio]
6 | [clojure.string :as str]
7 | [clojure.zip :refer [up xml-zip]]
8 | [clostache.parser :as clostache]
9 | [leiningen.core.main :refer [info debug abort]]
10 | [leiningen.droid.aar :refer [get-aar-files]]
11 | [leiningen.droid.utils :refer [dev-build?]]
12 | [leiningen.release :refer [parse-semantic-version]])
13 | (:import com.android.manifmerger.ManifestMerger
14 | com.android.manifmerger.MergerLog
15 | [com.android.utils StdLogger StdLogger$Level]
16 | java.io.File))
17 |
18 | ;; ### Constants
19 |
20 | ;; Name of the category for the launcher activities.
21 | (def ^{:private true} launcher-category "android.intent.category.LAUNCHER")
22 |
23 | ;; Attribute name for target SDK version.
24 | (def ^:private target-sdk-attribute (keyword :android:targetSdkVersion))
25 |
26 | ;; Attribute name for minimal SDK version.
27 | (def ^:private min-sdk-attribute (keyword :android:minSdkVersion))
28 |
29 | ;; ### Local functions
30 |
31 | (defn- load-manifest
32 | "Parses given XML manifest file and creates a zipper from it."
33 | [manifest-path]
34 | (xml-zip (xml/parse manifest-path)))
35 |
36 | (defn- get-all-launcher-activities
37 | "Returns a list of zipper trees of Activities which belong to the
38 | _launcher_ category."
39 | [manifest]
40 | (xml-> manifest :application :activity :intent-filter :category
41 | (attr= :android:name launcher-category)))
42 |
43 | ;; ### Manifest parsing and data extraction
44 |
45 | (defn get-package-name
46 | "Returns the name of the application's package."
47 | [manifest-path]
48 | (first (xml-> (load-manifest manifest-path) (attr :package))))
49 |
50 | (defn get-launcher-activity
51 | "Returns the package-qualified name of the first activity from the
52 | manifest that belongs to the _launcher_ category."
53 | [{{:keys [manifest-path rename-manifest-package]} :android}]
54 | (let [manifest (load-manifest manifest-path)
55 | [activity-name] (some-> manifest
56 | get-all-launcher-activities
57 | first
58 | up up
59 | (xml-> (attr :android:name)))
60 | pkg-name (first (xml-> manifest (attr :package)))]
61 | (when activity-name
62 | (str (or rename-manifest-package pkg-name) "/"
63 | (if (.startsWith activity-name ".")
64 | (str pkg-name activity-name)
65 | activity-name)))))
66 |
67 | (defn get-target-sdk-version
68 | "Extracts the target SDK version from the provided manifest file. If
69 | target SDK is not specified returns minimal SDK."
70 | [manifest-path]
71 | (let [[uses-sdk] (xml-> (load-manifest manifest-path) :uses-sdk)]
72 | (or (first (xml-> uses-sdk (attr target-sdk-attribute)))
73 | (first (xml-> uses-sdk (attr min-sdk-attribute))))))
74 |
75 | ;; ### Manifest templating
76 |
77 | (def ^:private version-bit-sizes
78 | "Amount of bits allocated for each version bucket."
79 | [9 9 9 5])
80 |
81 | (def ^:private version-maximums
82 | "Maximum values per each version bucket."
83 | (mapv (partial bit-shift-left 1) version-bit-sizes))
84 |
85 | (def ^:private version-coefficients
86 | "Each part of the version number will be multiplied by the respective
87 | coefficient, all of which are calculated here."
88 | (->> version-bit-sizes
89 | (reductions +)
90 | (mapv (fn [offset] (bit-shift-left 1 (- 32 offset))))))
91 |
92 | (defn- assert>
93 | "Asserts that a>b in version segments"
94 | [a b]
95 | (when-not (> a b)
96 | (abort (format "Version number segment too large to fit in the
97 | version-code scheme: %s > %s, maximum version in each segment is %s"
98 | b a (str/join "." version-maximums))))
99 | b)
100 |
101 | (defn version-code
102 | "Given a version map containing :major :minor :patch
103 | :build and :priority version numbers, returns an integer which is
104 | guaranteed to be greater for semantically larger version numbers.
105 |
106 | Splitting the 32 bit version code into 5 segments such that each
107 | semantically greater version will have a larger version code. The
108 | segments represent major, minor, patch, build and package
109 | priority (multiple builds of the same android apk where one takes
110 | precedence over another, for instance in the case where higher
111 | resolution assets are available, but a fallback is made available
112 | for devices which do not support the configuration).
113 |
114 | Largest possible version number: v512.512.512 (32)"
115 | [version-map]
116 | (->> version-map
117 | ((juxt :major :minor :patch :priority))
118 | (map (fnil assert> 0 0) version-maximums)
119 | (map * version-coefficients)
120 | (reduce +)))
121 |
122 | (defn merge-manifests
123 | "Merges the main application manifest file with manifests from AAR files."
124 | [{{:keys [manifest-path manifest-main-app-path]} :android :as project}]
125 | (let [merger (ManifestMerger. (MergerLog/wrapSdkLog
126 | (StdLogger. StdLogger$Level/VERBOSE)) nil)
127 | lib-manifests (get-aar-files project "AndroidManifest.xml")]
128 | (debug "Merging secondary manifests:" lib-manifests)
129 | (.process merger (jio/file manifest-path) (jio/file manifest-main-app-path)
130 | (into-array File lib-manifests) nil nil)))
131 |
132 | (defn generate-manifest
133 | "If a :manifest-template-path is specified, perform template substitution with
134 | the values in :android :manifest, including the version-name and version-code
135 | which are automatically generated, placing the output in :manifest-path."
136 | [{{:keys [manifest-path manifest-template-path manifest-options manifest-main-app-path
137 | target-version]} :android, version :version :as project}]
138 | (info "Generating manifest...")
139 | (let [full-manifest-map (merge {:version-name version
140 | :version-code (-> version
141 | parse-semantic-version
142 | version-code)
143 | :target-version target-version
144 | :debug-build (dev-build? project)}
145 | manifest-options)]
146 | (jio/make-parents manifest-path)
147 | (->> full-manifest-map
148 | (clostache/render (slurp manifest-template-path))
149 | (spit manifest-main-app-path))
150 | (merge-manifests project)))
151 |
--------------------------------------------------------------------------------
/src/leiningen/droid/new.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.new
2 | "Provides tasks for creating a new project or initialiaing plugin
3 | support in an existing one."
4 | (:require [clojure.string :as string]
5 | [clojure.java.io :as io]
6 | [leiningen.core.main :refer [info abort]]
7 | [leiningen.core.project :as project]
8 | [leiningen.droid.manifest :refer [get-target-sdk-version]]
9 | [leiningen.droid.utils :refer [get-dependencies]]
10 | [leiningen.new.templates :refer [render-text slurp-resource
11 | sanitize ->files]]))
12 |
13 | (defn renderer
14 | "Taken from lein-newnew.
15 |
16 | Create a renderer function that looks for mustache templates in the
17 | right place given the name of your template. If no data is passed, the
18 | file is simply slurped and the content returned unchanged."
19 | [name]
20 | (fn [template & [data]]
21 | (let [res (io/resource (str name "/" (sanitize template)))]
22 | (if data
23 | (render-text (slurp-resource res) data)
24 | (io/input-stream res)))))
25 |
26 | (defn package-to-path [package-name]
27 | (string/replace package-name #"\." "/"))
28 |
29 | (defn package-name-valid? [package-name]
30 | (and (not (.startsWith package-name "."))
31 | (> (.indexOf package-name ".") -1)
32 | (= (.indexOf package-name "-") -1)))
33 |
34 | (defn- latest-version
35 | "Downloads the latest version of the given artifact symbol, and returns the
36 | version string."
37 | [artifact default]
38 | (let [version (try (->> (get-dependencies
39 | {:dependencies [[artifact "RELEASE"]]
40 | :repositories project/default-repositories})
41 | keys
42 | (some #(when (= (first %) artifact) %))
43 | second)
44 | (catch Exception ex nil))]
45 | (if version
46 | (do (info "Found" artifact version)
47 | version)
48 | (do (info "Couldn't resolve the latest" artifact
49 | "version, using default" default)
50 | default))))
51 |
52 | (defn- current-plugin-version
53 | "Returns the version of this very lein-droid plugin currently being run."
54 | []
55 | (try (->> (io/resource "META-INF/maven/lein-droid/lein-droid/pom.properties")
56 | io/reader line-seq
57 | (keep #(second (re-matches #"^version=(.+)" %)))
58 | (some identity))
59 | (catch Exception ex nil)))
60 |
61 | (defn init
62 | "Creates project.clj file within an existing Android library folder.
63 |
64 | Presumes default directory names (like src, res and gen) and
65 | AndroidManifest.xml file to be already present in the project."
66 | [current-dir]
67 | (let [manifest (io/file current-dir "AndroidManifest.xml")]
68 | (when-not (.exists manifest)
69 | (abort "ERROR: AndroidManifest.xml not found - have to be in an existing"
70 | "Android project. Use `lein droid new` to create a new project."))
71 | (let [manifest-path (.getAbsolutePath manifest)
72 | data {:name (.getName (io/file current-dir))
73 | :target-sdk (or (get-target-sdk-version manifest-path) "15")
74 | :lein-droid-version (or (current-plugin-version) "0.4.3")}
75 | render (renderer "templates")]
76 | (info "Creating project.clj...")
77 | (io/copy (render "library.project.clj" data)
78 | (io/file current-dir "project.clj")))))
79 |
80 | (defn new-library
81 | "Creates new Android library."
82 | [library-name package-name data]
83 | (let [render (renderer "templates")]
84 | (info "Creating library" library-name "...")
85 | (->files
86 | data
87 | "assets"
88 | [".gitignore" (render "gitignore")]
89 | ["LICENSE" (render "LICENSE")]
90 | ["README.md" (render "README.library.md" data)]
91 | ["AndroidManifest.template.xml" (render "AndroidManifest.library.xml" data)]
92 | ["project.clj" (render "library.project.clj" data)]
93 | ["res/values/strings.xml" (render "strings.library.xml" data)]
94 | ["src/java/{{path}}/Util.java" (render "Util.java" data)]
95 | ["src/clojure/{{path}}/main.clj" (render "core.clj" data)])))
96 |
97 | (defn new-application
98 | "Creates new Android application."
99 | [project-name package-name data]
100 | (let [render (renderer "templates")]
101 | (info "Creating project" project-name "...")
102 | (->files
103 | data
104 | "assets"
105 | [".gitignore" (render "gitignore")]
106 | ["LICENSE" (render "LICENSE" data)]
107 | ["README.md" (render "README.md" data)]
108 | ["AndroidManifest.template.xml" (render "AndroidManifest.template.xml" data)]
109 | ["project.clj" (render "project.clj" data)]
110 | ["build/proguard-minify.cfg" (render "proguard_minify.cfg" data)]
111 | ["build/proguard-multi-dex.cfg" (render "proguard_multi_dex.cfg" data)]
112 | ["res/drawable-hdpi/splash_circle.png" (render "splash_circle.png")]
113 | ["res/drawable-hdpi/splash_droid.png" (render "splash_droid.png")]
114 | ["res/drawable-hdpi/splash_hands.png" (render "splash_hands.png")]
115 | ["res/drawable-hdpi/ic_launcher.png" (render "ic_launcher_hdpi.png")]
116 | ["res/drawable-mdpi/ic_launcher.png" (render "ic_launcher_mdpi.png")]
117 | ["res/drawable/splash_background.xml" (render "splash_background.xml")]
118 | ["res/anim/splash_rotation.xml" (render "splash_rotation.xml")]
119 | ["res/layout/splashscreen.xml" (render "splashscreen.xml")]
120 | ["res/values/strings.xml" (render "strings.xml" data)]
121 | ["src/java/{{path}}/SplashActivity.java" (render "SplashActivity.java" data)]
122 | ["src/clojure/{{path}}/main.clj" (render "main.clj" data)])))
123 |
124 | (defn new
125 | "Creates new Android project given the project's name and package name."
126 | [project-name package-name & options]
127 | (when-not (package-name-valid? package-name)
128 | (abort "ERROR: Package name should have at least two levels and"
129 | "not contain hyphens (you can replace them with underscores)."))
130 | (info "Resolving latest artifact versions...")
131 | (let [options (apply hash-map options)
132 | data {:name project-name
133 | :package package-name
134 | :package-sanitized (sanitize package-name)
135 | :path (package-to-path (sanitize package-name))
136 | :activity (get options ":activity" "MainActivity")
137 | :target-sdk (get options ":target-sdk" "18")
138 | :min-sdk (get options ":min-sdk" "15")
139 | :app-name (get options ":app-name" project-name)
140 | :library (get options ":library" false)
141 | :new-project true
142 | :lein-droid-version (or (current-plugin-version) "0.4.3")
143 | :clojure-version (latest-version 'org.clojure-android/clojure "1.7.0-r3")
144 | :neko-version (latest-version 'neko "4.0.0-alpha5")
145 | :skummet-version (latest-version 'org.skummet/clojure "1.7.0-r1")}]
146 | (if (= (:library data) "true")
147 | (new-library project-name package-name data)
148 | (new-application project-name package-name data))))
149 |
--------------------------------------------------------------------------------
/src/leiningen/droid/sdk.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.sdk
2 | "Functions to interact with Android SDK tools."
3 | (:use [leiningen.core.main :only [debug abort]])
4 | (:require [leiningen.droid.aar :refer [get-aar-native-paths]]
5 | [leiningen.droid.sideload :as sideload]
6 | [clojure.java.io :as io])
7 | (:import java.io.File))
8 |
9 | (defn- get-unpacked-natives-paths
10 | "Returns paths to unpacked native libraries if they exist, nil otherwise."
11 | []
12 | (let [path "target/native/linux/"]
13 | (when (.exists (io/file path))
14 | [path])))
15 |
16 | (defn create-apk
17 | "Delegates APK creation to ApkBuilder class in sdklib.jar."
18 | [{{:keys [sdk-path out-res-pkg-path out-dex-path native-libraries-paths]}
19 | :android :as project} & {:keys [apk-name resource-jars]}]
20 | (sideload/sideload-jars sdk-path)
21 | (let [apkbuilder (sideload/apk-builder apk-name out-res-pkg-path out-dex-path)
22 | all-native-libraries (concat native-libraries-paths
23 | (get-unpacked-natives-paths)
24 | (get-aar-native-paths project))
25 | dexes (filter #(re-matches #".*dex" (.getName %))
26 | (.listFiles (io/file out-dex-path)))]
27 | (when (seq resource-jars)
28 | (debug "Adding resource libraries: " resource-jars)
29 | (doseq [rj resource-jars]
30 | (.addResourcesFromJar apkbuilder rj)))
31 | (when (seq all-native-libraries)
32 | (debug "Adding native libraries: " all-native-libraries)
33 | (doseq [lib all-native-libraries]
34 | (.addNativeLibraries apkbuilder ^File (io/file lib))))
35 | (if (seq dexes)
36 | (do
37 | (debug "Adding DEX files: " dexes)
38 | (doseq [dex dexes]
39 | (.addFile apkbuilder dex (.getName dex))))
40 | (abort "No *.dex files found in " out-dex-path))
41 | (.sealApk apkbuilder)))
42 |
--------------------------------------------------------------------------------
/src/leiningen/droid/sideload.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.sideload
2 | "Wrappers around classes and methods that we pull dynamically from jars in
3 | Android SDK."
4 | (:require [cemerick.pomegranate :as cp]
5 | [clojure.java.io :as io])
6 | (:import [java.io File PrintStream]))
7 |
8 | (def sideload-jars
9 | "Dynamically adds jars from Android SDK on the classpath."
10 | (memoize (fn [sdk-path]
11 | (cp/add-classpath (io/file sdk-path "tools" "lib" "sdklib.jar")))))
12 |
13 | (defn apk-builder
14 | "Uses reflection to make an ApkBuilder instance."
15 | [apk-name res-path dex-path]
16 | (let [apkbuilder-class (Class/forName "com.android.sdklib.build.ApkBuilder")
17 | constructor (. apkbuilder-class getConstructor
18 | (into-array [File File File String PrintStream]))]
19 | (.newInstance constructor (into-array [(io/file apk-name) (io/file res-path)
20 | nil nil nil]))))
21 |
22 | (defn symbol-loader
23 | "Uses reflection to make an SymbolLoader instance."
24 | [file]
25 | (let [sl-class (Class/forName "com.android.sdklib.internal.build.SymbolLoader")
26 | constructor (. sl-class getConstructor (into-array [File]))]
27 | (.newInstance constructor (into-array [(io/file file)]))))
28 |
29 | (defn symbol-writer
30 | "Uses reflection to make an SymbolLoader instance."
31 | [out-folder package-name full-symbols]
32 | (let [sl-class (Class/forName "com.android.sdklib.internal.build.SymbolLoader")
33 | sw-class (Class/forName "com.android.sdklib.internal.build.SymbolWriter")
34 | constructor (. sw-class getConstructor (into-array [String String sl-class]))]
35 | (.newInstance constructor (into-array Object [out-folder package-name full-symbols]))))
36 |
--------------------------------------------------------------------------------
/src/leiningen/droid/test.clj:
--------------------------------------------------------------------------------
1 | (ns leiningen.droid.test
2 | (:refer-clojure :exclude [test])
3 | (:require [bultitude.core :as b]
4 | [clojure.java.io :as io]
5 | [clojure.string :as str]
6 | [clojure.set :as set]
7 | [leiningen.core.classpath :as cp]
8 | [leiningen.droid.code-gen :as code-gen]
9 | [leiningen.droid.compile :as compile]
10 | [leiningen.droid.utils :as utils]))
11 |
12 | (defn local-test
13 | "Runs tests locally using Robolectric."
14 | [{{:keys [cloverage-exclude-ns]} :android :as project} & [mode]]
15 | (when-not (-> project :android :library)
16 | (code-gen/code-gen project))
17 | (compile/compile project)
18 | (let [src-nses (b/namespaces-on-classpath
19 | :classpath (map io/file (distinct (:source-paths project)))
20 | :ignore-unreadable? false)
21 | src-nses (set/difference (set src-nses)
22 | (set (map symbol cloverage-exclude-ns)))
23 | test-nses (b/namespaces-on-classpath
24 | :classpath (map io/file (distinct (:test-paths project)))
25 | :ignore-unreadable? false)
26 | cpath (cp/get-classpath project)
27 | mode (or mode "clojuretest")]
28 | (binding [utils/*sh-print-output* true]
29 | (utils/sh "java" "-cp" (str/join ":" cpath)
30 | "coa.droid_test.internal.TestRunner" "-mode" mode
31 | ":src" (map str src-nses)
32 | ":test" (map str test-nses)))))
33 |
--------------------------------------------------------------------------------
/src/leiningen/droid/utils.clj:
--------------------------------------------------------------------------------
1 | ;; Provides utilities for the plugin.
2 | ;;
3 | (ns leiningen.droid.utils
4 | (:require [leiningen.core.project :as pr]
5 | [leiningen.core.classpath :as cp]
6 | robert.hooke)
7 | (:use [clojure.java.io :only (file reader)]
8 | [leiningen.core.main :only (info debug abort *debug*)]
9 | [clojure.string :only (join)])
10 | (:import [java.io File StringWriter]))
11 |
12 | ;; #### Convenient functions to run SDK binaries
13 |
14 | (defmacro ensure-paths
15 | "Checks if the given directories or files exist. Aborts Leiningen
16 | execution in case either of them doesn't or the value equals nil.
17 |
18 | We assume paths to be strings or lists/vectors. The latter case is
19 | used exclusively for Windows batch-files which are represented like
20 | `cmd.exe /C batch-file`, so we test third element of the list for
21 | the existence."
22 | [& paths]
23 | `(do
24 | ~@(for [p paths]
25 | `(cond (nil? ~p)
26 | (abort "The value of" (str '~p) "is nil. Abort execution.")
27 |
28 | (or
29 | (and (sequential? ~p) (not (.exists (file (nth ~p 2)))))
30 | (and (string? ~p) (not (.exists (file ~p)))))
31 | (abort "The path" ~p "doesn't exist. Abort execution.")))))
32 |
33 | (defn windows?
34 | "Returns true if we are running on Microsoft Windows"
35 | []
36 | (= java.io.File/separator "\\"))
37 |
38 | (defn get-sdk-build-tools-path
39 | "Returns a path to the correct Android Build Tools directory."
40 | ([{{:keys [sdk-path build-tools-version]} :android}]
41 | (get-sdk-build-tools-path sdk-path build-tools-version))
42 | ([sdk-path build-tools-version]
43 | (let [bt-root-dir (file sdk-path "build-tools")
44 | ;; build-tools directory contains a subdir which name we don't
45 | ;; know that has all the tools. Let's grab the last directory
46 | ;; inside build-tools/ and hope it is the one we need.
47 | bt-dir (or build-tools-version
48 | (->> (.list bt-root-dir)
49 | (filter #(.isDirectory (file bt-root-dir %)))
50 | sort last)
51 | (abort "Build tools not found."
52 | "Download them using the Android SDK Manager."))]
53 | (file bt-root-dir bt-dir))))
54 |
55 | (defn sdk-binary-paths
56 | "Returns a map of relative paths to different SDK binaries for both
57 | Unix and Windows platforms."
58 | [sdk-path build-tools-version]
59 | (ensure-paths sdk-path)
60 | (let [build-tools (get-sdk-build-tools-path sdk-path build-tools-version)]
61 | {:dx {:unix (file build-tools "dx")
62 | :win (file build-tools "dx.bat")}
63 | :adb {:unix (file sdk-path "platform-tools" "adb")
64 | :win (file sdk-path "platform-tools" "adb.exe")}
65 | :aapt {:unix (file build-tools "aapt")
66 | :win (file build-tools "aapt.exe")}
67 | :zipalign {:unix (file build-tools "zipalign")
68 | :win (file build-tools "zipalign.exe")}
69 | :proguard {:unix (file sdk-path "tools" "proguard" "bin" "proguard.sh")
70 | :win (file sdk-path "tools" "proguard" "bin" "proguard.bat")}}))
71 |
72 | (defn sdk-binary
73 | "Given the project map and the binary keyword, returns either a full
74 | path to the binary as a string, or a vector with call to cmd.exe for
75 | batch-files."
76 | [{{:keys [sdk-path build-tools-version]} :android} binary-kw]
77 | (let [binary-str (-> (sdk-binary-paths sdk-path build-tools-version)
78 | (get-in [binary-kw (if (windows?) :win :unix)])
79 | str)]
80 | (ensure-paths binary-str)
81 | (if (.endsWith binary-str ".bat")
82 | ["cmd.exe" "/C" binary-str]
83 | binary-str)))
84 |
85 | ;; ### Middleware section
86 |
87 | (defn absolutize
88 | "Taken from Leiningen source code.
89 |
90 | Absolutizes the `path` given `root` if it is relative. Leaves the
91 | path as is if it is absolute."
92 | [root path]
93 | (str (if (.isAbsolute (file path))
94 | path
95 | (.getCanonicalPath (file root path)))))
96 |
97 | (defn absolutize-android-paths
98 | "Taken from Leiningen source code.
99 |
100 | Absolutizes all values with keys ending with `path` or `paths` in
101 | the `:android` map of the project."
102 | [{:keys [root android] :as project}]
103 | (assoc project :android
104 | (into {} (for [[key val] android]
105 | [key (cond (re-find #"-path$" (name key))
106 | (absolutize root val)
107 |
108 | (re-find #"-paths$" (name key))
109 | (map (partial absolutize root) val)
110 |
111 | :else val)]))))
112 |
113 | (defn get-default-android-params
114 | "Returns a map of the default android-specific parameters."
115 | [{root :root, name :name, target-path :target-path
116 | java-paths :java-source-paths}]
117 | {:out-dex-path target-path
118 | :proguard-execute false
119 | :proguard-conf-path "proguard.conf"
120 | :proguard-output-jar-path (file target-path "mininified-classes.jar")
121 | :multi-dex-root-classes-path (file target-path "root-classes.jar")
122 | :multi-dex-main-dex-list-path (file target-path "main-dex-list.txt")
123 | :manifest-path (file target-path "AndroidManifest.xml")
124 | :manifest-main-app-path (file target-path "AndroidManifest.app.xml")
125 | :manifest-template-path "AndroidManifest.template.xml"
126 | :manifest-options {:app-name "@string/app_name"}
127 | :res-path "res"
128 | :gen-path (file target-path "gen")
129 | :out-res-path (file target-path "res")
130 | :assets-paths ["assets"]
131 | :assets-gen-path (file target-path "assets-gen")
132 | :out-res-pkg-path (file target-path (str name ".ap_"))
133 | :out-apk-path (file target-path (str name ".apk"))
134 | :keystore-path (file (System/getProperty "user.home")
135 | ".android" "debug.keystore")
136 | :key-alias "androiddebugkey"
137 | :repl-device-port 9999
138 | :repl-local-port 9999
139 | :target-version 15})
140 |
141 | (defn android-parameters
142 | "Merges project's `:android` map with default Android parameters and
143 | absolutizes paths in the `:android` map."
144 | [{:keys [android root] :as project}]
145 | (if-not (:sdk-path android)
146 | ;; :sdk-path might be nil when this function is called from middleware
147 | ;; before the :sdk-path is merged from some external profile. In this case
148 | ;; just do nothing to project map.
149 | project
150 | (let [android-params (merge (get-default-android-params project) android)
151 | sdk-path (absolutize root (:sdk-path android))
152 | support-repo (file sdk-path "extras" "android" "m2repository")
153 | ps-repo (file sdk-path "extras" "google" "m2repository")]
154 | (-> project
155 | (update-in [:java-source-paths] conj (str (:gen-path android-params)))
156 | (update-in [:repositories] concat
157 | [["android-support" {:url (str "file://" support-repo)}]
158 | ["android-play-services" {:url (str "file://" ps-repo)}]])
159 | (assoc :android android-params)
160 | absolutize-android-paths))))
161 |
162 | ;; ### General utilities
163 |
164 | (defn read-project
165 | "Reads and initializes a Leiningen project and applies Android
166 | middleware to it."
167 | [project-file]
168 | (android-parameters (pr/init-project (pr/read (str project-file)))))
169 |
170 | (defn proj
171 | ([] (proj "sample/project.clj"))
172 | ([project-clj]
173 | (let [profiles (.split (or (System/getenv "LEIN_DROID_PROFILES") "dev") ",")
174 | project (read-project project-clj)]
175 | (if (seq profiles)
176 | (pr/set-profiles project
177 | (distinct
178 | (mapcat #(pr/expand-profile project (keyword %))
179 | profiles)))
180 | project))))
181 |
182 | (defn sdk-version-number
183 | "If version keyword is passed (for example, `:ics` or `:jelly-bean`), resolves
184 | it to the version number. Otherwise just returns the input."
185 | [kw-or-number]
186 | (if (keyword? kw-or-number)
187 | (case kw-or-number
188 | :ics 15
189 | :jelly-bean 18
190 | :kitkat 19
191 | :lollipop 21
192 | (abort "Unknown Android SDK version: " kw-or-number))
193 | kw-or-number))
194 |
195 | (defn get-sdk-platform-path
196 | "Returns a version-specific path to the Android platform tools."
197 | [sdk-root version]
198 | (str (file sdk-root "platforms" (str "android-"
199 | (sdk-version-number version)))))
200 |
201 | (defn get-sdk-android-jar
202 | "Returns a version-specific path to the `android.jar` SDK file."
203 | ([{{:keys [sdk-path target-version]} :android :as project}]
204 | (get-sdk-android-jar sdk-path target-version))
205 | ([sdk-root version]
206 | (str (file (get-sdk-platform-path sdk-root version) "android.jar"))))
207 |
208 | (defn get-sdk-annotations-jar
209 | "Returns a path to annotations.jar file."
210 | [sdk-root-or-project]
211 | (let [sdk-root (if (map? sdk-root-or-project)
212 | (get-in sdk-root-or-project [:android :sdk-path])
213 | sdk-root-or-project)]
214 | (str (file sdk-root "tools" "support" "annotations.jar"))))
215 |
216 | (declare leiningen-2-p-7-or-later?)
217 | (declare resolve-dependencies)
218 |
219 | (defn get-resource-jars
220 | "Get the list of dependency libraries that has `:use-resources true`
221 | in their definition."
222 | [{:keys [dependencies] :as project}]
223 | (let [res-deps (filter (fn [[_ _ & {:as options}]]
224 | (:use-resources options))
225 | (:dependencies project))
226 | mod-proj (assoc project :dependencies res-deps)]
227 | ;; Resolve dependencies with hooks disabled.
228 | (let [resolve-var (if (leiningen-2-p-7-or-later?)
229 | (resolve 'leiningen.core.classpath/resolve-managed-dependencies)
230 | (resolve 'leiningen.core.classpath/resolve-dependencies))]
231 | (with-redefs-fn {resolve-var (#'robert.hooke/original resolve-var)}
232 | ;; Call our wrapper resolve-dependencies function
233 | #(resolve-dependencies mod-proj)))))
234 |
235 | (defn sdk-sanity-check
236 | "Ensures that :sdk-path is present in the project, and necessary modules are
237 | installed."
238 | [{{:keys [sdk-path target-version]} :android :as project}]
239 | (ensure-paths sdk-path)
240 | (let [check (fn [name file] (when-not (.exists file)
241 | (abort name "is not installed.
242 | Please install it from your Android SDK manager.")))]
243 | (when-not target-version
244 | (abort ":target-version is not specified. Abort execution."))
245 | (check (str "SDK platform " target-version)
246 | (file (get-sdk-platform-path sdk-path target-version)))
247 | (check "Android Support Repository"
248 | (file sdk-path "extras" "android" "m2repository"))))
249 |
250 | (def ^:dynamic *sh-print-output*
251 | "If true, print the output of the shell command regardless of *debug*."
252 | false)
253 |
254 | (defmacro with-process
255 | "Executes the subprocess specified in the binding list and applies
256 | `body` do it while it is running. The binding list consists of a var
257 | name for the process and the list of strings that represents shell
258 | command.
259 |
260 | After body is executed waits for a subprocess to finish, then checks
261 | the exit code. If code is not zero then prints the subprocess'
262 | output. If in DEBUG mode print both the command and it's output even
263 | for the successful run."
264 | [[process-name command] & body]
265 | `(do
266 | (apply debug ~command)
267 | (let [builder# (ProcessBuilder. ~command)
268 | _# (.redirectErrorStream builder# true)
269 | ~process-name (.start builder#)
270 | output# (line-seq (reader (.getInputStream ~process-name)))
271 | out-stream# (StringWriter.)
272 | print-output?# (or *debug* *sh-print-output*)]
273 | ~@body
274 | (doseq [line# output#]
275 | (if print-output?#
276 | (info line#)
277 | (binding [*out* out-stream#]
278 | (println line#))))
279 | (.waitFor ~process-name)
280 | (when-not (and (= (.exitValue ~process-name) 0)
281 | (not print-output?#))
282 | (info (str out-stream#)))
283 | (when-not (= (.exitValue ~process-name) 0)
284 | (abort "Abort execution."))
285 | output#)))
286 |
287 | (defn sh
288 | "Executes the command given by `args` in a subprocess. Flattens the
289 | given list. Turns files into canonical paths."
290 | [& args]
291 | (let [str-args (for [arg (flatten args)]
292 | (if (instance? File arg)
293 | (.getCanonicalPath ^File arg)
294 | (str arg)))]
295 | (with-process [process str-args])))
296 |
297 | (defn dev-build?
298 | "Checks the build type of the current project, assuming dev build if
299 | not a release build"
300 | [project]
301 | (not= (get-in project [:android :build-type]) :release))
302 |
303 | (defn wrong-usage
304 | "Returns a string with the information about the proper function usage."
305 | ([task-name function-var]
306 | (wrong-usage task-name function-var 0))
307 | ([task-name function-var arglist-number]
308 | (let [arglist (-> function-var
309 | meta :arglists (nth arglist-number))
310 | argcount (count arglist)
311 | parametrify #(str "<" % ">")
312 | ;; Replace the destructuring construction after & with
313 | ;; [optional-args].
314 | arglist (if (= (nth arglist (- argcount 2)) '&)
315 | (concat (map parametrify
316 | (take (- argcount 2) arglist))
317 | ["[optional-args]"])
318 | (map parametrify arglist))]
319 | (format "Wrong number of argumets. USAGE: %s %s"
320 | task-name (join (interpose " " arglist))))))
321 |
322 | (defn prompt-user
323 | "Reads a string from the console until the newline character."
324 | [prompt]
325 | (print prompt)
326 | (flush)
327 | (read-line))
328 |
329 | (defn read-password
330 | "Reads the password from the console without echoing the
331 | characters."
332 | [prompt]
333 | (if-let [console (System/console)]
334 | (join (.readPassword console prompt nil))
335 | (prompt-user prompt)))
336 |
337 | (defn append-suffix
338 | "Appends a suffix to a filename, e.g. transforming `sample.apk` into
339 | `sample-signed.apk`"
340 | [filename suffix]
341 | (let [[_ without-ext ext] (re-find #"(.+)(\.\w+)" filename)]
342 | (str without-ext "-" suffix ext)))
343 |
344 | (defn create-debug-keystore
345 | "Creates a keystore for signing debug APK files."
346 | [keystore-path]
347 | (sh "keytool" "-genkey" "-v"
348 | "-keystore" keystore-path
349 | "-alias" "androiddebugkey"
350 | "-sigalg" "SHA1withRSA"
351 | "-keyalg" "RSA"
352 | "-keysize" "1024"
353 | "-validity" "365"
354 | "-keypass" "android"
355 | "-storepass" "android"
356 | "-dname" "CN=Android Debug,O=Android,C=US"))
357 |
358 | (defn relativize-path [^File dir ^File to-relativize]
359 | (.getPath (.relativize (.toURI dir)
360 | (.toURI to-relativize))))
361 |
362 | ;; ### Compatibility
363 |
364 | (def leiningen-2-p-7-or-later?
365 | "Returns true if Leiningen version is 2.7 or later."
366 | (memoize
367 | (fn []
368 | (let [[_ major minor] (re-matches #"(\d+)\.(\d+)\..+"
369 | (leiningen.core.main/leiningen-version))
370 | major (Integer/parseInt major)
371 | minor (Integer/parseInt minor)]
372 | (or (> major 2)
373 | (and (= major 2) (>= minor 7)))))))
374 |
375 | (defn get-dependencies
376 | "Leiningen 2.7.0 introduced managed dependencies and broke
377 | `cp/get-dependencies`. We must handle both versions of the function."
378 | [project & args]
379 | (if (leiningen-2-p-7-or-later?)
380 | (apply cp/get-dependencies :dependencies :managed-dependencies project args)
381 | (apply cp/get-dependencies :dependencies project args)))
382 |
383 | (defn resolve-dependencies
384 | "Leiningen 2.7.0 introduced managed dependencies and deprecated
385 | `resolve-dependencies`."
386 | [project & args]
387 | (if (leiningen-2-p-7-or-later?)
388 | (apply (resolve 'leiningen.core.classpath/resolve-managed-dependencies)
389 | :dependencies :managed-dependencies project args)
390 | (apply (resolve 'leiningen.core.classpath/resolve-dependencies)
391 | :dependencies project args)))
392 |
--------------------------------------------------------------------------------
/travis/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | if [[ $(lein with-profile travis pprint :version) =~ .*\-SNAPSHOT ]]; then
3 | lein deploy clojars
4 | fi
5 |
--------------------------------------------------------------------------------
/travis/profiles.clj:
--------------------------------------------------------------------------------
1 | ;; These profiles should be used as profiles.clj on Travis CI.
2 | {:auth {:repository-auth {#"https://clojars.org/repo"
3 | {:username :env, :password :env}}}
4 | :travis {:plugins [[lein-pprint "1.1.1"]]
5 | :android {:sdk-path "/usr/local/android-sdk/"}
6 | :deploy-repositories [["releases" :clojars]]}
7 | :android-user {:dependencies [[cider/cider-nrepl "0.9.0-SNAPSHOT"]]
8 | :android {:aot-exclude-ns ["cider.nrepl.middleware.util.java.parser"
9 | "cider.nrepl" "cider-nrepl.plugin"]}}}
10 |
11 |
--------------------------------------------------------------------------------