├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle ├── gradle-mvn-push.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── card.gif ├── complexcard.png ├── customer.png ├── demo.png ├── example.gif └── logo.jpg ├── kratos-annotation ├── build.gradle ├── gradle.properties └── src │ └── main │ └── java │ └── kratos │ ├── Bind.java │ ├── BindLayout.java │ ├── BindText.java │ ├── Binds.java │ ├── LBindLayout.java │ ├── LBindText.java │ ├── OnKBooleanChanged.java │ ├── OnKStringChanged.java │ ├── PackageName.java │ └── WithBind.java ├── kratos-compiler ├── build.gradle ├── gradle.properties └── src │ └── main │ └── java │ └── kratos │ └── compiler │ ├── BindingClass.java │ ├── FieldViewBinding.java │ ├── KBinding.java │ ├── KBindings.java │ ├── KratosProcessor.java │ ├── UpdateKStringBinding.java │ └── binding │ ├── KBindingGeneric.java │ ├── KBooleanBinding.java │ └── KStringBinding.java ├── kratos-sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── me │ │ └── ele │ │ └── kratos_sample │ │ ├── CardSampleActivity.java │ │ ├── SimpleActivity.java │ │ ├── TextCard.java │ │ ├── entity │ │ ├── Customer.java │ │ └── KText.java │ │ └── package-info.java │ └── res │ ├── layout │ ├── activity_simple.xml │ └── kcard_text.xml │ ├── raw │ └── sample.json │ └── values │ ├── colors.xml │ └── strings.xml ├── kratos ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── kratos │ │ ├── Kratos.java │ │ ├── card │ │ ├── KCard.kt │ │ ├── KCardActivity.kt │ │ ├── RenderExt.kt │ │ ├── entity │ │ │ └── KData.kt │ │ ├── event │ │ │ ├── ExceptionEvent.java │ │ │ ├── FailEvent.java │ │ │ ├── KMenuClickEvent.java │ │ │ ├── KOnClickEvent.java │ │ │ └── SuccessEvent.java │ │ ├── render │ │ │ ├── Header.kt │ │ │ ├── Menu.kt │ │ │ ├── SearchMenu.kt │ │ │ ├── SearchStyle.kt │ │ │ ├── Style.kt │ │ │ └── Template.kt │ │ └── utils │ │ │ ├── ActivityUtils.java │ │ │ ├── DelegateExt.kt │ │ │ ├── DrawableUtils.java │ │ │ ├── FixSwipeRefreshLayout.kt │ │ │ ├── GsonUtils.java │ │ │ ├── GsonUtilsCreator.java │ │ │ ├── JsonVerify.java │ │ │ ├── NotNullCardRenderListenter.kt │ │ │ ├── OnCardRenderListener.kt │ │ │ ├── Skip.java │ │ │ └── StringUtils.java │ │ ├── internal │ │ ├── Binding.java │ │ ├── KBase.kt │ │ ├── KBinder.java │ │ ├── KBoolean.kt │ │ ├── KFinder.java │ │ ├── KString.kt │ │ ├── KStringDeserializer.java │ │ └── ViewExt.kt │ │ └── package-info.java │ └── res │ ├── layout │ ├── inc_toolbar.xml │ └── kcard_main.xml │ ├── menu │ └── empty.xml │ └── values │ └── style.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | /kratos/build 9 | /kratos-annotation/build 10 | /kratos-compiler/build 11 | /kratos-sample/build 12 | *.hprof -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kratos 2 | ============= 3 | 4 | 本项目旨在用来展示一个学习的样本,而不是用于生产环境,如果要用于生产的话,我建议使用React Native。Kratos展示了如何利用Annotation Processor和Javapoet在编译时期生成代码,达到类似Lisp宏一样的基础效果(实际差得远),实现Annotation free;还展示了利用Kolin语言的Delegate,实现检测变量的变化;最重要的是,展示了一套利用Json来渲染View的思路,并在Json中做配置,利用反射实现数据的View的绑定。这些代码,有助于帮助你理解React Native,比如Kotlin对应着Js,Json配置文件作为外部DSL对应着Jsx。 5 | 6 | 其实本想着实现一个用Lisp在Android上开发的框架,无奈本人太菜,算了,以后再说,先研究React Native好了 ╮(╯▽╰)╭ 7 | 8 | ![Logo](images/logo.jpg) 9 | 10 | Provide basic __Double Binding(Data Binding)__ feature on Android. 11 | 12 | * Using annotation to generate boilerplate code. 13 | * Bind view and data to help you clean your code. 14 | 15 | 16 | Example 17 | ---------------- 18 | 19 | ### Data Binding 20 | 21 | The following code demostrate that two views(EditText and TextView) bound to one single data(which in the code `boundData` holds the data. you can later access or change the data by using `boundData.get()` and `boundData.set("some string")`) 22 | 23 | ```java 24 | public class SimpleActivity extends Activity { 25 | 26 | @BindText({R.id.test_doublebinding_input, R.id.test_doublebinding_presenter}) 27 | KString boundData = new KString(); 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_simple); 33 | Kratos.bind(this); 34 | } 35 | } 36 | ``` 37 | The presenter(TextView) will behave exactly the same as input(EditText) since they were bound to the same data: 38 | 39 | ![Example](images/example.gif) 40 | 41 | What if you want yourself a custom update behavior than the default update method when data changes? You can add custom update function to update view: 42 | 43 | ```java 44 | @BindText(R.id.some_edittext_id) 45 | KString boundData = new KString(new KString.Update() { 46 | @Override 47 | public void update(@NotNull View view, @NotNull String s) { 48 | EditText et = (EditText)view; //Add custom update function here. 49 | int start = et.getSelectionStart(); // This code is the same as: public KString boundData1 = new KString(), 50 | et.setText(s); // since this custom function is the same as the default function. 51 | et.setSelection(start); 52 | } 53 | }); 54 | ``` 55 | If you are using Kotlin, the code is more simple: 56 | 57 | ```java 58 | @KBindText("some_edittext_id") 59 | public var boundData = KString { 60 | it, new -> 61 | it as EditText 62 | val position = this.selectionStart 63 | this.setText(new) 64 | this.setSelection(position) 65 | } 66 | ``` 67 | 68 | ### Card 69 | 70 | __The concept of Card is very important in Kratos.__ 71 | 72 | Basically, every view is a card, each activity is generated by one single json file, a little bit like html. 73 | 74 | The goal is to implement each view as a card, 75 | and then easily reuse them by manipulating json file. 76 | 77 | For example, the following activity is generated from [`sample.json`][4] file. 78 | 79 | ![Card](images/card.gif) 80 | 81 | You can easily handle click event by override `onEventMainThread` method. 82 | 83 | #### Notice that 84 | 85 | * Kratos uses toolbar by default, so you need a `NoActionBar` theme in your style file, or you can just use `KratosTheme`. 86 | * When you run into non-final id problem in your library project, just use annotation start with "L", for example `LBindText`. But remember that you should never use both kind of annotation in the same file! 87 | 88 | #### What's the benefit 89 | 90 | * No more fear of the changing requirements. Since activities are generated from json file, you can manipulate the layout on the fly(imagine put the json file on a server, like html on website). 91 | * No more redundant code. Since each view is a card, you can reuse views easily by manipulate the json file. 92 | * Easily create and compose custom views. All you need to do is to extend KCard, create you own KData and bind the data to the card(must write full package name, so that kratos can find your card). You can implement rather complex card using kratos depending on your skills. For example, all the view in the following activity are card: 93 | 94 | ![ComplexCard](images/complexcard.png) 95 | 96 | For more code see kratos-sample. 97 | 98 | How To 99 | ----------------- 100 | 101 | ### Create custom card 102 | 103 | To Create a card consists of two TextView that can also handle click even2 104 | 105 | 1. Create a class that extends KData, for example: 106 | 107 | ```java 108 | public class KText extends KData { 109 | public KString text1; 110 | public KString text2; 111 | } 112 | ``` 113 | 2. Create a class that extends KCard, use KData as its Generic, for example: 114 | 115 | ```java 116 | @BindLayout(R.layout.kcard_text) //@LBindLayout("kcard_text") 117 | @Binds({@Bind(id = R.id.kcard_text_text1, data = "text1"), 118 | @Bind(id = R.id.kcard_text_text2, data = "text2")}) 119 | public class TextCard extends KCard { 120 | public TextCard(@NotNull Context context) { 121 | super(context); 122 | } 123 | 124 | @Override 125 | public void init() { 126 | setOnLinkListener(); 127 | } 128 | } 129 | ``` 130 | 131 | You should initailize your stuff inside `init` function, not constructor; 132 | 133 | Notice that it uses `BindLayout` to specify its layout: 134 | 135 | ```xml 136 | 137 | 144 | 145 | 152 | 159 | 160 | ``` 161 | 162 | 3. Create a Activity that extends KCardActivity: 163 | 164 | ```java 165 | public class CardSampleActivity extends KCardActivity { 166 | 167 | private void showToast(String text) { 168 | Toast.makeText(CardSampleActivity.this, text, Toast.LENGTH_SHORT).show(); 169 | } 170 | 171 | @Override 172 | public void onEventMainThread(@NotNull KOnClickEvent event) { 173 | super.onEventMainThread(event); 174 | switch (event.id) { 175 | case "textCard1": 176 | showToast("Handle click on textCard1!"); 177 | break; 178 | } 179 | } 180 | } 181 | ``` 182 | Notice that it handles click event by overriding o`nEventMainThread` method. 183 | 184 | 4. Pass your json layout to your activity: 185 | 186 | ```json 187 | { 188 | "header": { 189 | "title": "Card Sample" 190 | }, 191 | "body": [ 192 | { 193 | "data": { 194 | "text": "this is text1", 195 | "text": "this is text2" 196 | }, 197 | "type": "me.ele.kratos_sample.TextCard", 198 | "id": "textCard1", 199 | "style": { 200 | "margin_top": 20, 201 | "margin_left": 20, 202 | "margin_right": 20 203 | } 204 | } 205 | ] 206 | } 207 | ``` 208 | Notice that it uses full package name in `type` property. 209 | 210 | You can use kratos' util function to pass json file to your next activity: 211 | 212 | ```java 213 | ActivityUtils.jump(SimpleActivity.this, CardSampleActivity.class, CODE_CARD_SAMPLE, R.raw.sample); 214 | ``` 215 | 216 | 5. Create a package-info.java file in your source folder like this: 217 | 218 | ```java 219 | @PackageName package me.ele.kratos_sample; 220 | 221 | import kratos.PackageName; 222 | ``` 223 | 224 | You will get something like this: 225 | 226 | ![Deme](images/demo.png) 227 | 228 | ### Add Custom updater: 229 | 230 | If you want a custom update behavior when data been changed, add a function annotated with `OnKStringChanged` to your card, like this: 231 | 232 | ```java 233 | @OnKStringChanged("text1") 234 | public void updateText1(@NotNull TextView v, @NotNull String s) { 235 | v.setText(s); 236 | Log.d("TextCard", "custom updater!"); 237 | } 238 | ``` 239 | 240 | ### Wait 241 | 242 | *You may say:"That's no big deal. what's the point?"* 243 | 244 | *But* 245 | 246 | This is where the magic happens! 247 | ----------------- 248 | 249 | Assume you have this data object which implements `Parcelable`: 250 | 251 | ```java 252 | public class Customer implements Parcelable { 253 | 254 | public KString name = new KString(); 255 | 256 | @Override 257 | public int describeContents() { 258 | return 0; 259 | } 260 | 261 | @Override 262 | public void writeToParcel(Parcel dest, int flags) { 263 | dest.writeParcelable(this.name, flags); 264 | } 265 | 266 | public Customer() { 267 | } 268 | 269 | protected Customer(Parcel in) { 270 | this.name = in.readParcelable(KString.class.getClassLoader()); 271 | } 272 | 273 | public static final Creator CREATOR = new Creator() { 274 | public Customer createFromParcel(Parcel source) { 275 | return new Customer(source); 276 | } 277 | 278 | public Customer[] newArray(int size) { 279 | return new Customer[size]; 280 | } 281 | }; 282 | } 283 | ``` 284 | This Customer has a property -- name, and this is what you do. 285 | 286 | 1. Change the `text1` element in the json file above to this: 287 | 288 | ```json 289 | "text1": "{Customer.name}", 290 | ``` 291 | 292 | 2. Add this code to your activity: 293 | 294 | ```java 295 | Customer customer; 296 | @Override 297 | public void onFinishRender() { 298 | bind(this, customer); 299 | } 300 | ``` 301 | Tada!!From now on, if you want to change the view that has Customer's name in it, all you need to do is change the value of `customer.name`, say you do this: 302 | 303 | ```java 304 | customer.name.set("Merlin"); 305 | ``` 306 | This is what you get: 307 | 308 | ![Customer](images/customer.png) 309 | 310 | Dude, Holy ****, This is Magic.. 311 | 312 | 313 | Download 314 | ----------------- 315 | __Kratos is still under development and a lot of features haven't been added to it yet.__ But the basic idea is here. If you are interested in this project, feel free to fork. 316 | 317 | Kratos is available from maven central: 318 | 319 | ```groovy 320 | apply plugin: 'com.neenbedankt.android-apt' 321 | buildscript { 322 | ext.kratos_version = '0.2.4' 323 | repositories { 324 | mavenCentral() 325 | } 326 | dependencies { 327 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 328 | } 329 | } 330 | 331 | dependencies { 332 | compile "com.github.acemerlin:kratos:$kratos_version" 333 | apt "com.github.acemerlin:kratos-compiler:$kratos_version" 334 | } 335 | ``` 336 | 337 | How It Works 338 | ---------------- 339 | * Use Kotlin's [Observable Delegate][1] to listen to changes made to certain property. 340 | * Use Kotlin's [Extension][2] feature to add functions to View. 341 | * Use Annotation Processor to generate code which binds the data and the view(or views). 342 | 343 | 344 | Lisence 345 | ---------------- 346 | [GNU GENERAL PUBLIC LICENSE Version 3][3] 347 | 348 | [1]: https://kotlinlang.org/docs/reference/delegated-properties.html#observable 349 | [2]: https://kotlinlang.org/docs/reference/extensions.html 350 | [3]: http://www.gnu.org/licenses/gpl-3.0.en.html 351 | [4]: https://github.com/ACEMerlin/Kratos/blob/feature/card/kratos-sample/src/main/res/raw/sample.json -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | subprojects { project -> 3 | group = GROUP 4 | version = VERSION_NAME 5 | buildscript { 6 | repositories { 7 | jcenter() 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:1.5.0' 12 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 13 | } 14 | } 15 | } 16 | 17 | ext { 18 | minSdkVersion = 15 19 | targetSdkVersion = 23 20 | compileSdkVersion = 23 21 | buildToolsVersion = '23.0.1' 22 | sourceCompatibilityVersion = JavaVersion.VERSION_1_7 23 | targetCompatibilityVersion = JavaVersion.VERSION_1_7 24 | } 25 | 26 | allprojects { 27 | repositories { 28 | jcenter() 29 | mavenCentral() 30 | } 31 | } 32 | 33 | task clean(type: Delete) { 34 | delete rootProject.buildDir 35 | } 36 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-XX:MaxPermSize=1024m 2 | 3 | GROUP=com.github.acemerlin 4 | VERSION_NAME=0.2.4 5 | 6 | POM_DESCRIPTION=Add basic double binding support to android. 7 | 8 | POM_URL=https://github.com/ACEMerlin/Kratos 9 | POM_SCM_URL=https://github.com/ACEMerlin/Kratos 10 | POM_SCM_CONNECTION=scm:git:git://github.com/ACEMerlin/Kratos.git 11 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/ACEMerlin/Kratos.git 12 | 13 | POM_LICENCE_NAME=GNU GENERAL PUBLIC LICENSE, Version 3.0 14 | POM_LICENCE_URL=http://www.gnu.org/licenses/gpl-3.0.en.html 15 | POM_LICENCE_DIST=repo 16 | 17 | POM_DEVELOPER_ID=merlin 18 | POM_DEVELOPER_NAME=Ge Chen 19 | -------------------------------------------------------------------------------- /gradle/gradle-mvn-push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | version = VERSION_NAME 21 | group = GROUP 22 | 23 | repositories { 24 | mavenCentral() 25 | } 26 | 27 | def isReleaseBuild() { 28 | return VERSION_NAME.contains("SNAPSHOT") == false 29 | } 30 | 31 | def getReleaseRepositoryUrl() { 32 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 33 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 34 | } 35 | 36 | def getSnapshotRepositoryUrl() { 37 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 38 | : "https://oss.sonatype.org/content/repositories/snapshots/" 39 | } 40 | 41 | def getRepositoryUsername() { 42 | return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : "" 43 | } 44 | 45 | def getRepositoryPassword() { 46 | return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : "" 47 | } 48 | 49 | afterEvaluate { project -> 50 | uploadArchives { 51 | repositories { 52 | mavenDeployer { 53 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 54 | 55 | pom.groupId = GROUP 56 | pom.artifactId = POM_ARTIFACT_ID 57 | pom.version = VERSION_NAME 58 | 59 | repository(url: getReleaseRepositoryUrl()) { 60 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 61 | } 62 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 63 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 64 | } 65 | 66 | pom.project { 67 | name POM_NAME 68 | packaging POM_PACKAGING 69 | description POM_DESCRIPTION 70 | url POM_URL 71 | 72 | scm { 73 | url POM_SCM_URL 74 | connection POM_SCM_CONNECTION 75 | developerConnection POM_SCM_DEV_CONNECTION 76 | } 77 | 78 | licenses { 79 | license { 80 | name POM_LICENCE_NAME 81 | url POM_LICENCE_URL 82 | distribution POM_LICENCE_DIST 83 | } 84 | } 85 | 86 | developers { 87 | developer { 88 | id POM_DEVELOPER_ID 89 | name POM_DEVELOPER_NAME 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | signing { 98 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 99 | sign configurations.archives 100 | } 101 | 102 | if (project.getPlugins().hasPlugin('com.android.application') || 103 | project.getPlugins().hasPlugin('com.android.library')) { 104 | task install(type: Upload, dependsOn: assemble) { 105 | repositories.mavenInstaller { 106 | configuration = configurations.archives 107 | 108 | pom.groupId = GROUP 109 | pom.artifactId = POM_ARTIFACT_ID 110 | pom.version = VERSION_NAME 111 | 112 | pom.project { 113 | name POM_NAME 114 | packaging POM_PACKAGING 115 | description POM_DESCRIPTION 116 | url POM_URL 117 | 118 | scm { 119 | url POM_SCM_URL 120 | connection POM_SCM_CONNECTION 121 | developerConnection POM_SCM_DEV_CONNECTION 122 | } 123 | 124 | licenses { 125 | license { 126 | name POM_LICENCE_NAME 127 | url POM_LICENCE_URL 128 | distribution POM_LICENCE_DIST 129 | } 130 | } 131 | 132 | developers { 133 | developer { 134 | id POM_DEVELOPER_ID 135 | name POM_DEVELOPER_NAME 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | task androidJavadocs(type: Javadoc) { 143 | if (!project.plugins.hasPlugin('kotlin-android')) { 144 | source = android.sourceSets.main.java.srcDirs 145 | } 146 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 147 | exclude '**/internal/*' 148 | 149 | if (JavaVersion.current().isJava8Compatible()) { 150 | options.addStringOption('Xdoclint:none', '-quiet') 151 | } 152 | } 153 | 154 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 155 | classifier = 'javadoc' 156 | from androidJavadocs.destinationDir 157 | } 158 | 159 | task androidSourcesJar(type: Jar) { 160 | classifier = 'sources' 161 | from android.sourceSets.main.java.source 162 | } 163 | 164 | } else { 165 | install { 166 | repositories.mavenInstaller { 167 | pom.groupId = GROUP 168 | pom.artifactId = POM_ARTIFACT_ID 169 | pom.version = VERSION_NAME 170 | 171 | pom.project { 172 | name POM_NAME 173 | packaging POM_PACKAGING 174 | description POM_DESCRIPTION 175 | url POM_URL 176 | 177 | scm { 178 | url POM_SCM_URL 179 | connection POM_SCM_CONNECTION 180 | developerConnection POM_SCM_DEV_CONNECTION 181 | } 182 | 183 | licenses { 184 | license { 185 | name POM_LICENCE_NAME 186 | url POM_LICENCE_URL 187 | distribution POM_LICENCE_DIST 188 | } 189 | } 190 | 191 | developers { 192 | developer { 193 | id POM_DEVELOPER_ID 194 | name POM_DEVELOPER_NAME 195 | } 196 | } 197 | } 198 | } 199 | } 200 | 201 | task sourcesJar(type: Jar, dependsOn:classes) { 202 | classifier = 'sources' 203 | from sourceSets.main.allSource 204 | } 205 | 206 | task javadocJar(type: Jar, dependsOn:javadoc) { 207 | classifier = 'javadoc' 208 | from javadoc.destinationDir 209 | } 210 | } 211 | 212 | artifacts { 213 | if (project.getPlugins().hasPlugin('com.android.application') || 214 | project.getPlugins().hasPlugin('com.android.library')) { 215 | archives androidSourcesJar 216 | archives androidJavadocsJar 217 | } else { 218 | archives sourcesJar 219 | archives javadocJar 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACEMerlin/Kratos/c11bb855940c063e7adb775ae366dfd3dd534a0a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /images/card.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACEMerlin/Kratos/c11bb855940c063e7adb775ae366dfd3dd534a0a/images/card.gif -------------------------------------------------------------------------------- /images/complexcard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACEMerlin/Kratos/c11bb855940c063e7adb775ae366dfd3dd534a0a/images/complexcard.png -------------------------------------------------------------------------------- /images/customer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACEMerlin/Kratos/c11bb855940c063e7adb775ae366dfd3dd534a0a/images/customer.png -------------------------------------------------------------------------------- /images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACEMerlin/Kratos/c11bb855940c063e7adb775ae366dfd3dd534a0a/images/demo.png -------------------------------------------------------------------------------- /images/example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACEMerlin/Kratos/c11bb855940c063e7adb775ae366dfd3dd534a0a/images/example.gif -------------------------------------------------------------------------------- /images/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACEMerlin/Kratos/c11bb855940c063e7adb775ae366dfd3dd534a0a/images/logo.jpg -------------------------------------------------------------------------------- /kratos-annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | sourceCompatibility = rootProject.ext.sourceCompatibilityVersion 4 | targetCompatibility = rootProject.ext.targetCompatibilityVersion 5 | 6 | def logger = new com.android.build.gradle.internal.LoggerWrapper(project.logger) 7 | def sdkHandler = new com.android.build.gradle.internal.SdkHandler(project, logger) 8 | for (File file : sdkHandler.sdkLoader.repositories) { 9 | repositories.maven { 10 | url = file.toURI() 11 | } 12 | } 13 | 14 | dependencies { 15 | compile 'com.android.support:support-annotations:23.1.1' 16 | } 17 | 18 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') -------------------------------------------------------------------------------- /kratos-annotation/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Kratos Annotations 2 | POM_ARTIFACT_ID=kratos-annotations 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/Bind.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import android.support.annotation.IdRes; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Created by merlin on 15/12/22. 12 | */ 13 | @Retention(RetentionPolicy.CLASS) 14 | @Target(ElementType.TYPE) 15 | public @interface Bind { 16 | String data(); 17 | 18 | @IdRes int id(); 19 | } 20 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/BindLayout.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import android.support.annotation.LayoutRes; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Created by merlin on 15/12/21. 12 | */ 13 | @Retention(RetentionPolicy.CLASS) 14 | @Target(ElementType.TYPE) 15 | public @interface BindLayout { 16 | @LayoutRes int value(); 17 | } 18 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/BindText.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import android.support.annotation.IdRes; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Created by merlin on 15/12/7. 12 | */ 13 | @Retention(RetentionPolicy.CLASS) 14 | @Target(ElementType.FIELD) 15 | public @interface BindText { 16 | @IdRes int[] value(); 17 | } 18 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/Binds.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by merlin on 15/12/22. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.TYPE) 13 | public @interface Binds { 14 | Bind[] value(); 15 | } 16 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/LBindLayout.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by merlin on 15/12/21. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.TYPE) 13 | public @interface LBindLayout { 14 | String value(); 15 | } 16 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/LBindText.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by merlin on 15/12/4. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.FIELD) 13 | public @interface LBindText { 14 | String[] value(); 15 | } 16 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/OnKBooleanChanged.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by merlin on 15/12/24. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.METHOD) 13 | public @interface OnKBooleanChanged { 14 | String value(); 15 | } 16 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/OnKStringChanged.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by merlin on 15/12/24. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.METHOD) 13 | public @interface OnKStringChanged { 14 | String value(); 15 | } 16 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/PackageName.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by merlin on 15/12/21. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.PACKAGE) 13 | public @interface PackageName { 14 | } 15 | -------------------------------------------------------------------------------- /kratos-annotation/src/main/java/kratos/WithBind.java: -------------------------------------------------------------------------------- 1 | package kratos; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Created by merlin on 16/1/28. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.TYPE) 13 | public @interface WithBind { 14 | } 15 | -------------------------------------------------------------------------------- /kratos-compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | sourceCompatibility = rootProject.ext.sourceCompatibilityVersion 4 | targetCompatibility = rootProject.ext.targetCompatibilityVersion 5 | 6 | def logger = new com.android.build.gradle.internal.LoggerWrapper(project.logger) 7 | def sdkHandler = new com.android.build.gradle.internal.SdkHandler(project, logger) 8 | for (File file : sdkHandler.sdkLoader.repositories) { 9 | repositories.maven { 10 | url = file.toURI() 11 | } 12 | } 13 | 14 | dependencies { 15 | compile project (':kratos-annotation') 16 | compile 'com.google.auto.service:auto-service:1.0-rc2' 17 | compile 'com.squareup:javapoet:1.4.0' 18 | } 19 | 20 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') -------------------------------------------------------------------------------- /kratos-compiler/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Kratos Compiler 2 | POM_ARTIFACT_ID=kratos-compiler 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/BindingClass.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.JavaFile; 5 | import com.squareup.javapoet.MethodSpec; 6 | import com.squareup.javapoet.ParameterizedTypeName; 7 | import com.squareup.javapoet.TypeSpec; 8 | import com.squareup.javapoet.TypeVariableName; 9 | 10 | import java.util.Collection; 11 | import java.util.LinkedHashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import javax.lang.model.element.Modifier; 16 | 17 | import kratos.compiler.binding.KBindingGeneric; 18 | 19 | import static javax.lang.model.element.Modifier.FINAL; 20 | import static javax.lang.model.element.Modifier.PUBLIC; 21 | 22 | /** 23 | * Created by merlin on 15/12/7. 24 | */ 25 | public class BindingClass { 26 | 27 | private final String classPackage; 28 | private final String className; 29 | private final String targetClass; 30 | private final ClassName resClass; 31 | private String parentViewBinder; 32 | private static final ClassName KBINDER = ClassName.get("kratos.internal", "KBinder"); 33 | private static final ClassName KFINDER = ClassName.get("kratos.internal", "KFinder"); 34 | private static final ClassName VIEW = ClassName.get("android.view", "View"); 35 | static final int NO_ID = -1; 36 | private final boolean isLibrary; 37 | private final boolean isKotlin; 38 | 39 | private final Map viewIdMap = new LinkedHashMap<>(); 40 | private final Map updateKStringBindingMap = new LinkedHashMap<>(); 41 | private String layoutId; 42 | 43 | public void setLayoutId(String layoutId) { 44 | this.layoutId = layoutId; 45 | } 46 | 47 | BindingClass(String classPackage, String className, String targetClass, String resPackage, Boolean isLibrary, Boolean isKotlin) { 48 | this.classPackage = classPackage; 49 | this.className = className; 50 | this.targetClass = targetClass; 51 | this.resClass = ClassName.get(resPackage, "R"); 52 | this.isLibrary = isLibrary; 53 | this.isKotlin = isKotlin; 54 | } 55 | 56 | void setParentViewBinder(String parentViewBinder) { 57 | this.parentViewBinder = parentViewBinder; 58 | } 59 | 60 | JavaFile brewJava() { 61 | TypeSpec.Builder result = TypeSpec.classBuilder(className) 62 | .addModifiers(PUBLIC) 63 | .addTypeVariable(TypeVariableName.get("T", ClassName.bestGuess(targetClass))); 64 | 65 | if (parentViewBinder != null) { 66 | result.superclass(ParameterizedTypeName.get(ClassName.bestGuess(parentViewBinder), 67 | TypeVariableName.get("T"))); 68 | } else { 69 | result.addSuperinterface(ParameterizedTypeName.get(KBINDER, TypeVariableName.get("T"))); 70 | } 71 | 72 | result.addMethod(createBindMethod()); 73 | 74 | return JavaFile.builder(classPackage, result.build()) 75 | .addFileComment("Generated code from Kratos. Do not modify!") 76 | .build(); 77 | } 78 | 79 | private MethodSpec createBindMethod() { 80 | MethodSpec.Builder result = MethodSpec.methodBuilder("bind") 81 | .addAnnotation(Override.class) 82 | .addModifiers(PUBLIC) 83 | .addParameter(TypeVariableName.get("T"), "target", FINAL) 84 | .addParameter(KFINDER, "finder", FINAL); 85 | if (parentViewBinder != null) { 86 | result.addStatement("super.bind(target, finder)"); 87 | } 88 | if (layoutId != null) { 89 | if (!isLibrary) 90 | result.addStatement("target.setLayoutId($L)", Integer.parseInt(layoutId)); 91 | else 92 | result.addStatement("target.setLayoutId($T.layout.$L)", resClass, layoutId); 93 | result.addStatement("target.init()"); 94 | } 95 | //if (!updateKStringBindingMap.isEmpty()) { 96 | // for (Map.Entry entry : updateKStringBindingMap.entrySet()) { 97 | // addKStringUpdateBindings(result, entry); 98 | // } 99 | //} 100 | if (!map.isEmpty()) { 101 | for (Map generics : map.values()) { 102 | for (Map.Entry entry : generics.entrySet()) { 103 | entry.getValue().addGenericCode(result, entry.getKey()); 104 | } 105 | } 106 | } 107 | if (!viewIdMap.isEmpty()) { 108 | result.addStatement("$T view", VIEW); 109 | // Loop over each view bindings and emit it. 110 | for (KBindings bindings : viewIdMap.values()) { 111 | addKBindings(result, bindings); 112 | } 113 | } 114 | if (!doubleBindingMap.isEmpty()) { 115 | if (viewIdMap.isEmpty()) { 116 | result.addStatement("$T view", VIEW); 117 | } 118 | for (Map.Entry entry : doubleBindingMap.entrySet()) { 119 | addDoubleBindings(result, entry); 120 | } 121 | } 122 | return result.build(); 123 | } 124 | 125 | private void addDoubleBindings(MethodSpec.Builder result, Map.Entry entry) { 126 | result.addStatement("view = finder.findRequiredView(target, $L)", entry.getKey()); 127 | result.addStatement("target.getData().$L.bind(view)", entry.getValue()); 128 | result.addStatement("target.getData().$L.set(target.getData().$L.getInitData())", entry.getValue(), entry.getValue()); 129 | } 130 | 131 | private void addKBindings(MethodSpec.Builder result, KBindings bindings) { 132 | List requiredViewBindings = bindings.getRequiredBindings(); 133 | if (!isLibrary) { 134 | if (requiredViewBindings.isEmpty()) { 135 | result.addStatement("view = finder.findOptionalView(target, $L)", bindings.getId()); 136 | } else { 137 | int id = Integer.parseInt(bindings.getId()); 138 | if (id == NO_ID) { 139 | result.addStatement("view = target"); 140 | } else { 141 | result.addStatement("view = finder.findRequiredView(target, $L)", id); 142 | } 143 | } 144 | } else { 145 | if (requiredViewBindings.isEmpty()) { 146 | result.addStatement("view = finder.findOptionalView(target, $T.id.$L)", resClass, bindings.getId()); 147 | } else { 148 | if (bindings.getId() == null) { 149 | result.addStatement("view = target"); 150 | } else { 151 | result.addStatement("view = finder.findRequiredView(target, $T.id.$L)", resClass, bindings.getId()); 152 | } 153 | } 154 | } 155 | addFieldBindings(result, bindings); 156 | } 157 | 158 | private void addFieldBindings(MethodSpec.Builder result, KBindings bindings) { 159 | Collection fieldBindings = bindings.getFieldBindings(); 160 | for (FieldViewBinding fieldBinding : fieldBindings) { 161 | if (fieldBinding.requiresCast()) { 162 | result.addStatement("$T $L = finder.castView(view)", VIEW, fieldBinding.getName() + bindings.getId()); 163 | } else { 164 | result.addStatement("$T $L = view", VIEW, fieldBinding.getName() + bindings.getId()); 165 | } 166 | if (isKotlin) { 167 | String name = fieldBinding.getName(); 168 | result.addStatement("target.get$L().bind($L)", name.substring(0, 1).toUpperCase() + name.substring(1, name.length()), name + bindings.getId()); 169 | } else { 170 | result.addStatement("target.$L.bind($L)", fieldBinding.getName(), fieldBinding.getName() + bindings.getId()); 171 | } 172 | } 173 | } 174 | 175 | public KBindings getKBindings(String id) { 176 | return viewIdMap.get(id); 177 | } 178 | 179 | void addField(String id, FieldViewBinding binding) { 180 | getOrCreateViewBindings(id).addFieldBinding(binding); 181 | } 182 | 183 | private KBindings getOrCreateViewBindings(String id) { 184 | KBindings viewId = viewIdMap.get(id); 185 | if (viewId == null) { 186 | viewId = new KBindings(id); 187 | viewIdMap.put(id, viewId); 188 | } 189 | return viewId; 190 | } 191 | 192 | private Map doubleBindingMap = new LinkedHashMap<>(); 193 | 194 | public void addDoubleBinding(int id, String data) { 195 | String mData = doubleBindingMap.get(id); 196 | if (mData == null) { 197 | doubleBindingMap.put(id, data); 198 | } 199 | } 200 | 201 | public void addKStringUpdateBinding(String kstring, UpdateKStringBinding binding) { 202 | UpdateKStringBinding mData = updateKStringBindingMap.get(kstring); 203 | if (mData == null) { 204 | updateKStringBindingMap.put(kstring, binding); 205 | } 206 | } 207 | 208 | private final Map> map = new LinkedHashMap<>(); 209 | 210 | public void putGeneric(String kstring, KBindingGeneric binding) { 211 | String key = binding.getClass().getSimpleName(); 212 | Map result = map.get(key); 213 | if (result == null) { 214 | result = new LinkedHashMap<>(); 215 | } 216 | KBindingGeneric mData = result.get(kstring); 217 | if (mData == null) { 218 | result.put(kstring, binding); 219 | } 220 | map.put(key, result); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/FieldViewBinding.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler; 2 | 3 | import com.squareup.javapoet.TypeName; 4 | 5 | import static kratos.compiler.KratosProcessor.VIEW_TYPE; 6 | 7 | /** 8 | * Created by merlin on 15/12/7. 9 | */ 10 | public final class FieldViewBinding implements KBinding { 11 | private final String name; 12 | private final TypeName type; 13 | private final boolean required; 14 | 15 | FieldViewBinding(String name, TypeName type, boolean required) { 16 | this.name = name; 17 | this.type = type; 18 | this.required = required; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public TypeName getType() { 26 | return type; 27 | } 28 | 29 | public boolean isRequired() { 30 | return required; 31 | } 32 | 33 | public boolean requiresCast() { 34 | return !VIEW_TYPE.equals(type.toString()); 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/KBinding.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler; 2 | 3 | /** 4 | * Created by merlin on 15/12/7. 5 | */ 6 | public interface KBinding { 7 | } 8 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/KBindings.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.LinkedHashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | /** 10 | * Created by merlin on 15/12/7. 11 | */ 12 | public class KBindings { 13 | private final Set fieldBindings = new LinkedHashSet<>(); 14 | private final String id; 15 | 16 | KBindings(String id) { 17 | this.id = id; 18 | } 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public void addFieldBinding(FieldViewBinding fieldBinding) { 25 | fieldBindings.add(fieldBinding); 26 | } 27 | 28 | public Collection getFieldBindings() { 29 | return fieldBindings; 30 | } 31 | 32 | public List getRequiredBindings() { 33 | List requiredViewBindings = new ArrayList<>(); 34 | for (FieldViewBinding fieldBinding : fieldBindings) { 35 | if (fieldBinding.isRequired()) { 36 | requiredViewBindings.add(fieldBinding); 37 | } 38 | } 39 | return requiredViewBindings; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/KratosProcessor.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler; 2 | 3 | import com.google.auto.common.SuperficialValidation; 4 | import com.google.auto.service.AutoService; 5 | import com.squareup.javapoet.TypeName; 6 | 7 | import java.io.IOException; 8 | import java.io.PrintWriter; 9 | import java.io.StringWriter; 10 | import java.lang.annotation.Annotation; 11 | import java.util.Iterator; 12 | import java.util.LinkedHashMap; 13 | import java.util.LinkedHashSet; 14 | import java.util.Map; 15 | import java.util.Set; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | import javax.annotation.processing.AbstractProcessor; 20 | import javax.annotation.processing.Filer; 21 | import javax.annotation.processing.ProcessingEnvironment; 22 | import javax.annotation.processing.Processor; 23 | import javax.annotation.processing.RoundEnvironment; 24 | import javax.lang.model.SourceVersion; 25 | import javax.lang.model.element.AnnotationMirror; 26 | import javax.lang.model.element.Element; 27 | import javax.lang.model.element.PackageElement; 28 | import javax.lang.model.element.TypeElement; 29 | import javax.lang.model.type.DeclaredType; 30 | import javax.lang.model.type.TypeKind; 31 | import javax.lang.model.type.TypeMirror; 32 | import javax.lang.model.type.TypeVariable; 33 | import javax.lang.model.util.Elements; 34 | import javax.lang.model.util.Types; 35 | 36 | import kratos.Bind; 37 | import kratos.BindLayout; 38 | import kratos.BindText; 39 | import kratos.Binds; 40 | import kratos.LBindLayout; 41 | import kratos.LBindText; 42 | import kratos.OnKBooleanChanged; 43 | import kratos.OnKStringChanged; 44 | import kratos.PackageName; 45 | import kratos.compiler.binding.KBooleanBinding; 46 | import kratos.compiler.binding.KStringBinding; 47 | 48 | import static javax.lang.model.SourceVersion.latestSupported; 49 | import static javax.tools.Diagnostic.Kind.ERROR; 50 | 51 | @AutoService(Processor.class) 52 | public class KratosProcessor extends AbstractProcessor { 53 | 54 | private Elements elementUtils; 55 | private Types typeUtils; 56 | private Filer filer; 57 | private static final String BINDING_CLASS_SUFFIX = "$$KBinder"; 58 | private static final String NULLABLE_ANNOTATION_NAME = "Nullable"; 59 | static final String VIEW_TYPE = "android.view.View"; 60 | private static String packageName = null; 61 | 62 | @Override 63 | public Set getSupportedAnnotationTypes() { 64 | Set types = new LinkedHashSet<>(); 65 | //支持的annotation类型 66 | types.add(BindText.class.getCanonicalName()); 67 | types.add(LBindText.class.getCanonicalName()); 68 | types.add(BindLayout.class.getCanonicalName()); 69 | types.add(LBindLayout.class.getCanonicalName()); 70 | types.add(Binds.class.getCanonicalName()); 71 | types.add(Bind.class.getCanonicalName()); 72 | types.add(OnKStringChanged.class.getCanonicalName()); 73 | types.add(OnKBooleanChanged.class.getCanonicalName()); 74 | return types; 75 | } 76 | 77 | @Override 78 | public synchronized void init(ProcessingEnvironment env) { 79 | super.init(env); 80 | elementUtils = env.getElementUtils(); 81 | typeUtils = env.getTypeUtils(); 82 | filer = env.getFiler(); 83 | } 84 | 85 | @Override 86 | public SourceVersion getSupportedSourceVersion() { 87 | return latestSupported(); 88 | } 89 | 90 | @Override 91 | public boolean process(Set annotations, RoundEnvironment env) { 92 | Map targetClassMap = findAndParseTargets(env); 93 | 94 | for (Map.Entry entry : targetClassMap.entrySet()) { 95 | TypeElement typeElement = entry.getKey(); 96 | BindingClass BindingClass = entry.getValue(); 97 | 98 | try { 99 | BindingClass.brewJava().writeTo(filer); 100 | } catch (IOException e) { 101 | error(typeElement, "Unable to write view binder for type %s: %s", typeElement, 102 | e.getMessage()); 103 | } 104 | } 105 | 106 | return true; 107 | } 108 | 109 | private Map findAndParseTargets(RoundEnvironment env) { 110 | Map targetClassMap = new LinkedHashMap<>(); 111 | Set erasedTargetNames = new LinkedHashSet<>(); 112 | 113 | // Process each @Bind element. 114 | for (Element element : env.getElementsAnnotatedWith(PackageName.class)) { 115 | if (!SuperficialValidation.validateElement(element)) continue; 116 | try { 117 | parsePackageName(element, targetClassMap, erasedTargetNames); 118 | } catch (Exception e) { 119 | logParsingError(element, PackageName.class, e); 120 | } 121 | } 122 | 123 | for (Element element : env.getElementsAnnotatedWith(Binds.class)) { 124 | if (!SuperficialValidation.validateElement(element)) continue; 125 | try { 126 | parseBinds(element, targetClassMap, erasedTargetNames); 127 | } catch (Exception e) { 128 | logParsingError(element, Binds.class, e); 129 | } 130 | } 131 | 132 | for (Element element : env.getElementsAnnotatedWith(Bind.class)) { 133 | if (!SuperficialValidation.validateElement(element)) continue; 134 | try { 135 | parseBind(element, targetClassMap, erasedTargetNames); 136 | } catch (Exception e) { 137 | logParsingError(element, Bind.class, e); 138 | } 139 | } 140 | 141 | for (Element element : env.getElementsAnnotatedWith(BindLayout.class)) { 142 | if (!SuperficialValidation.validateElement(element)) continue; 143 | try { 144 | parseBindLayout(element, targetClassMap, erasedTargetNames); 145 | } catch (Exception e) { 146 | logParsingError(element, BindLayout.class, e); 147 | } 148 | } 149 | 150 | for (Element element : env.getElementsAnnotatedWith(LBindLayout.class)) { 151 | if (!SuperficialValidation.validateElement(element)) continue; 152 | try { 153 | parseLBindLayout(element, targetClassMap, erasedTargetNames); 154 | } catch (Exception e) { 155 | logParsingError(element, LBindLayout.class, e); 156 | } 157 | } 158 | 159 | parseOnChangedTarget(env, targetClassMap, erasedTargetNames, 160 | OnKStringChanged.class, OnKBooleanChanged.class); 161 | 162 | for (Element element : env.getElementsAnnotatedWith(BindText.class)) { 163 | if (!SuperficialValidation.validateElement(element)) continue; 164 | try { 165 | parseBindText(element, targetClassMap, erasedTargetNames); 166 | } catch (Exception e) { 167 | logParsingError(element, BindText.class, e); 168 | } 169 | } 170 | 171 | for (Element element : env.getElementsAnnotatedWith(LBindText.class)) { 172 | if (!SuperficialValidation.validateElement(element)) continue; 173 | try { 174 | parseLBindText(element, targetClassMap, erasedTargetNames); 175 | } catch (Exception e) { 176 | logParsingError(element, LBindText.class, e); 177 | } 178 | } 179 | 180 | // Try to find a parent binder for each. 181 | for (Map.Entry entry : targetClassMap.entrySet()) { 182 | String parentClassFqcn = findParentFqcn(entry.getKey(), erasedTargetNames); 183 | if (parentClassFqcn != null) { 184 | entry.getValue().setParentViewBinder(parentClassFqcn + BINDING_CLASS_SUFFIX); 185 | } 186 | } 187 | 188 | return targetClassMap; 189 | } 190 | 191 | private void parseOnChangedTarget(RoundEnvironment env, Map targetClassMap, Set erasedTargetNames, Class... annotationClasses) { 192 | for (Class clazz : annotationClasses) { 193 | for (Element element : env.getElementsAnnotatedWith(clazz)) { 194 | if (!SuperficialValidation.validateElement(element)) continue; 195 | try { 196 | parseOnChanged(element, targetClassMap, erasedTargetNames, clazz); 197 | } catch (Exception e) { 198 | logParsingError(element, clazz, e); 199 | } 200 | } 201 | } 202 | } 203 | 204 | private void parseOnChanged(Element element, Map targetClassMap, 205 | Set erasedTargetNames, Class annotationClass) { 206 | TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 207 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement, false, false); 208 | TypeMirror mirror = element.asType(); 209 | if (!(mirror.getKind() == TypeKind.EXECUTABLE)) 210 | return; 211 | String method = element.toString().trim(); 212 | String methodName = method.substring(0, method.indexOf("(")); 213 | Matcher m = Pattern.compile("\\(([^)]+)\\)").matcher(method); 214 | if (m.find()) { 215 | String[] methodTypes = m.group(1).split(","); 216 | String key = null; 217 | if (annotationClass.equals(OnKStringChanged.class)) { 218 | KStringBinding binding = new KStringBinding(methodName, methodTypes); 219 | key = element.getAnnotation(OnKStringChanged.class).value(); 220 | bindingClass.putGeneric(key, binding); 221 | } else if (annotationClass.equals(OnKBooleanChanged.class)) { 222 | KBooleanBinding binding = new KBooleanBinding(methodName, methodTypes); 223 | key = element.getAnnotation(OnKBooleanChanged.class).value(); 224 | bindingClass.putGeneric(key, binding); 225 | } else { 226 | error(element, "unknow annotation class type @%s", 227 | annotationClass.getSimpleName()); 228 | } 229 | } 230 | erasedTargetNames.add(enclosingElement.toString()); 231 | } 232 | 233 | private void parseOnKStringUpdate(Element element, Map targetClassMap, 234 | Set erasedTargetNames) { 235 | TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 236 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement, false, false); 237 | TypeMirror mirror = element.asType(); 238 | if (!(mirror.getKind() == TypeKind.EXECUTABLE)) 239 | return; 240 | String method = element.toString().trim(); 241 | String methodName = method.substring(0, method.indexOf("(")); 242 | Matcher m = Pattern.compile("\\(([^)]+)\\)").matcher(method); 243 | if (m.find()) { 244 | String[] methodTypes = m.group(1).split(","); 245 | UpdateKStringBinding binding = new UpdateKStringBinding(methodName, methodTypes); 246 | String kstring = element.getAnnotation(OnKStringChanged.class).value(); 247 | bindingClass.addKStringUpdateBinding(kstring, binding); 248 | } 249 | erasedTargetNames.add(enclosingElement.toString()); 250 | } 251 | 252 | private void parseBinds(Element element, Map targetClassMap, 253 | Set erasedTargetNames) { 254 | Bind[] binds = element.getAnnotation(Binds.class).value(); 255 | for (Bind bind : binds) { 256 | String data = bind.data(); 257 | int id = bind.id(); 258 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, (TypeElement) element, false, false); 259 | bindingClass.addDoubleBinding(id, data); 260 | } 261 | } 262 | 263 | private void parseBind(Element element, Map targetClassMap, 264 | Set erasedTargetNames) { 265 | String data = element.getAnnotation(Bind.class).data(); 266 | int id = element.getAnnotation(Bind.class).id(); 267 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, (TypeElement) element, false, false); 268 | bindingClass.addDoubleBinding(id, data); 269 | } 270 | 271 | private void parsePackageName(Element element, Map targetClassMap, 272 | Set erasedTargetNames) { 273 | packageName = ((PackageElement) element).getQualifiedName().toString(); 274 | } 275 | 276 | private void parseBindLayout(Element element, Map targetClassMap, 277 | Set erasedTargetNames) { 278 | int id = element.getAnnotation(BindLayout.class).value(); 279 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, (TypeElement) element, false, false); 280 | bindingClass.setLayoutId(String.valueOf(id)); 281 | } 282 | 283 | private void parseLBindLayout(Element element, Map targetClassMap, 284 | Set erasedTargetNames) { 285 | String id = element.getAnnotation(LBindLayout.class).value(); 286 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, (TypeElement) element, true, false); 287 | bindingClass.setLayoutId(id); 288 | } 289 | 290 | private void parseLBindText(Element element, Map targetClassMap, 291 | Set erasedTargetNames) { 292 | TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 293 | 294 | TypeMirror elementType = element.asType(); 295 | if (elementType.getKind() == TypeKind.TYPEVAR) { 296 | TypeVariable typeVariable = (TypeVariable) elementType; 297 | elementType = typeVariable.getUpperBound(); 298 | } 299 | // Assemble information on the field. 300 | String[] ids = element.getAnnotation(LBindText.class).value(); 301 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement, true, false); 302 | for (String id : ids) { 303 | if (bindingClass != null) { 304 | KBindings bindings = bindingClass.getKBindings(id); 305 | if (bindings != null) { 306 | Iterator iterator = bindings.getFieldBindings().iterator(); 307 | if (iterator.hasNext()) { 308 | FieldViewBinding existingBinding = iterator.next(); 309 | error(element, "Attempt to use @%s for an already bound ID %s on '%s'. (%s.%s)", 310 | LBindText.class.getSimpleName(), id, existingBinding.getName(), 311 | enclosingElement.getQualifiedName(), element.getSimpleName()); 312 | return; 313 | } 314 | } 315 | } else { 316 | bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement, true, false); 317 | } 318 | String name = element.getSimpleName().toString(); 319 | TypeName type = TypeName.get(elementType); 320 | boolean required = isRequiredBinding(element); 321 | 322 | FieldViewBinding binding = new FieldViewBinding(name, type, required); 323 | bindingClass.addField(String.valueOf(id), binding); 324 | } 325 | 326 | // Add the type-erased version to the valid binding targets set. 327 | erasedTargetNames.add(enclosingElement.toString()); 328 | } 329 | 330 | private void parseBindText(Element element, Map targetClassMap, 331 | Set erasedTargetNames) { 332 | TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 333 | 334 | TypeMirror elementType = element.asType(); 335 | if (elementType.getKind() == TypeKind.TYPEVAR) { 336 | TypeVariable typeVariable = (TypeVariable) elementType; 337 | elementType = typeVariable.getUpperBound(); 338 | } 339 | // Assemble information on the field. 340 | int[] ids = element.getAnnotation(BindText.class).value(); 341 | BindingClass bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement, false, false); 342 | for (int id : ids) { 343 | if (bindingClass != null) { 344 | KBindings bindings = bindingClass.getKBindings(String.valueOf(id)); 345 | if (bindings != null) { 346 | Iterator iterator = bindings.getFieldBindings().iterator(); 347 | if (iterator.hasNext()) { 348 | FieldViewBinding existingBinding = iterator.next(); 349 | error(element, "Attempt to use @%s for an already bound ID %s on '%s'. (%s.%s)", 350 | BindText.class.getSimpleName(), id, existingBinding.getName(), 351 | enclosingElement.getQualifiedName(), element.getSimpleName()); 352 | return; 353 | } 354 | } 355 | } else { 356 | bindingClass = getOrCreateTargetClass(targetClassMap, enclosingElement, false, false); 357 | } 358 | String name = element.getSimpleName().toString(); 359 | TypeName type = TypeName.get(elementType); 360 | boolean required = isRequiredBinding(element); 361 | 362 | FieldViewBinding binding = new FieldViewBinding(name, type, required); 363 | bindingClass.addField(String.valueOf(id), binding); 364 | } 365 | 366 | // Add the type-erased version to the valid binding targets set. 367 | erasedTargetNames.add(enclosingElement.toString()); 368 | } 369 | 370 | private static boolean isRequiredBinding(Element element) { 371 | return !hasAnnotationWithName(element, NULLABLE_ANNOTATION_NAME); 372 | } 373 | 374 | private static boolean hasAnnotationWithName(Element element, String simpleName) { 375 | for (AnnotationMirror mirror : element.getAnnotationMirrors()) { 376 | String annotationName = mirror.getAnnotationType().asElement().getSimpleName().toString(); 377 | if (simpleName.equals(annotationName)) { 378 | return true; 379 | } 380 | } 381 | return false; 382 | } 383 | 384 | private String findParentFqcn(TypeElement typeElement, Set parents) { 385 | TypeMirror type; 386 | while (true) { 387 | type = typeElement.getSuperclass(); 388 | if (type.getKind() == TypeKind.NONE) { 389 | return null; 390 | } 391 | typeElement = (TypeElement) ((DeclaredType) type).asElement(); 392 | if (parents.contains(typeElement.toString())) { 393 | String packageName = getPackageName(typeElement); 394 | return packageName + "." + getClassName(typeElement, packageName); 395 | } 396 | } 397 | } 398 | 399 | private BindingClass getOrCreateTargetClass(Map targetClassMap, 400 | TypeElement enclosingElement, Boolean isLibrary, Boolean isKotlin) { 401 | BindingClass bindingClass = targetClassMap.get(enclosingElement); 402 | if (bindingClass == null) { 403 | String targetType = enclosingElement.getQualifiedName().toString(); 404 | String classPackage = getPackageName(enclosingElement); 405 | String className = getClassName(enclosingElement, classPackage) + BINDING_CLASS_SUFFIX; 406 | bindingClass = new BindingClass(classPackage, className, targetType, packageName, isLibrary, isKotlin); 407 | targetClassMap.put(enclosingElement, bindingClass); 408 | } 409 | return bindingClass; 410 | } 411 | 412 | private String getPackageName(TypeElement type) { 413 | return elementUtils.getPackageOf(type).getQualifiedName().toString(); 414 | } 415 | 416 | private static String getClassName(TypeElement type, String packageName) { 417 | int packageLen = packageName.length() + 1; 418 | return type.getQualifiedName().toString().substring(packageLen).replace('.', '$'); 419 | } 420 | 421 | private void error(Element element, String message, Object... args) { 422 | if (args.length > 0) { 423 | message = String.format(message, args); 424 | } 425 | processingEnv.getMessager().printMessage(ERROR, message, element); 426 | } 427 | 428 | private void logParsingError(Element element, Class annotation, 429 | Exception e) { 430 | StringWriter stackTrace = new StringWriter(); 431 | e.printStackTrace(new PrintWriter(stackTrace)); 432 | error(element, "Unable to parse @%s binding.\n\n%s", annotation.getSimpleName(), stackTrace); 433 | } 434 | 435 | } 436 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/UpdateKStringBinding.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler; 2 | 3 | /** 4 | * Created by merlin on 15/12/24. 5 | */ 6 | public class UpdateKStringBinding { 7 | 8 | private String methodName; 9 | private String[] parameterTypes; 10 | 11 | public UpdateKStringBinding(String methodName, String[] parameterTypes) { 12 | this.methodName = methodName; 13 | this.parameterTypes = parameterTypes; 14 | } 15 | 16 | public String getMethodName() { 17 | return methodName; 18 | } 19 | 20 | public String[] getParameterTypes() { 21 | return parameterTypes; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/binding/KBindingGeneric.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler.binding; 2 | 3 | import com.squareup.javapoet.MethodSpec; 4 | 5 | /** 6 | * Created by sanvi on 1/8/16. 7 | */ 8 | public interface KBindingGeneric { 9 | public void addGenericCode(MethodSpec.Builder result, String key); 10 | 11 | public String getMethodName(); 12 | 13 | public String[] getParameterTypes(); 14 | } -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/binding/KBooleanBinding.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler.binding; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.MethodSpec; 5 | import com.squareup.javapoet.TypeSpec; 6 | 7 | import javax.lang.model.element.Modifier; 8 | 9 | /** 10 | * Created by merlin on 15/12/24. 11 | */ 12 | public class KBooleanBinding implements KBindingGeneric { 13 | 14 | private String methodName; 15 | private String[] parameterTypes; 16 | private static final ClassName ONUPDATELISTENER_KBOOLEAN = ClassName.get("kratos.internal.KBoolean", "OnUpdateListener"); 17 | private static final ClassName VIEW = ClassName.get("android.view", "View"); 18 | 19 | public KBooleanBinding(String methodName, String[] parameterTypes) { 20 | this.methodName = methodName; 21 | this.parameterTypes = parameterTypes; 22 | } 23 | 24 | @Override 25 | public String getMethodName() { 26 | return methodName; 27 | } 28 | 29 | @Override 30 | public String[] getParameterTypes() { 31 | return parameterTypes; 32 | } 33 | 34 | @Override 35 | public void addGenericCode(MethodSpec.Builder result, String key) { 36 | TypeSpec update = TypeSpec.anonymousClassBuilder("") 37 | .addSuperinterface(ONUPDATELISTENER_KBOOLEAN) 38 | .addMethod(MethodSpec.methodBuilder("update") 39 | .addAnnotation(Override.class) 40 | .addModifiers(Modifier.PUBLIC) 41 | .addParameter(VIEW, "v") 42 | .addParameter(boolean.class, "s") 43 | .returns(void.class) 44 | .addStatement("target.$L(($L)$N, $N)", getMethodName(), getParameterTypes()[0], "v", "s") 45 | .build()) 46 | .build(); 47 | result.addStatement("target.getData().$L.setOnUpdateListener($L)", key, update); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /kratos-compiler/src/main/java/kratos/compiler/binding/KStringBinding.java: -------------------------------------------------------------------------------- 1 | package kratos.compiler.binding; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.MethodSpec; 5 | import com.squareup.javapoet.TypeSpec; 6 | 7 | import javax.lang.model.element.Modifier; 8 | 9 | /** 10 | * Created by merlin on 15/12/24. 11 | */ 12 | public class KStringBinding implements KBindingGeneric { 13 | 14 | private String methodName; 15 | private String[] parameterTypes; 16 | private static final ClassName ONUPDATELISTENER_KSTRING = ClassName.get("kratos.internal.KString", "OnUpdateListener"); 17 | private static final ClassName VIEW = ClassName.get("android.view", "View"); 18 | 19 | public KStringBinding(String methodName, String[] parameterTypes) { 20 | this.methodName = methodName; 21 | this.parameterTypes = parameterTypes; 22 | } 23 | 24 | @Override 25 | public void addGenericCode(MethodSpec.Builder result, String key) { 26 | TypeSpec update = TypeSpec.anonymousClassBuilder("") 27 | .addSuperinterface(ONUPDATELISTENER_KSTRING) 28 | .addMethod(MethodSpec.methodBuilder("update") 29 | .addAnnotation(Override.class) 30 | .addModifiers(Modifier.PUBLIC) 31 | .addParameter(VIEW, "v") 32 | .addParameter(String.class, "s") 33 | .returns(void.class) 34 | .addStatement("target.$L(($L)$N, $N)", getMethodName(), getParameterTypes()[0], "v", "s") 35 | .build()) 36 | .build(); 37 | result.addStatement("target.getData().$L.setOnUpdateListener($L)", key, update); 38 | 39 | } 40 | 41 | @Override 42 | public String getMethodName() { 43 | return methodName; 44 | } 45 | 46 | @Override 47 | public String[] getParameterTypes() { 48 | return parameterTypes; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /kratos-sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /kratos-sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | 4 | android { 5 | compileSdkVersion rootProject.ext.compileSdkVersion 6 | buildToolsVersion rootProject.ext.buildToolsVersion 7 | 8 | defaultConfig { 9 | applicationId 'me.ele.kratos' 10 | minSdkVersion rootProject.ext.minSdkVersion 11 | targetSdkVersion rootProject.ext.targetSdkVersion 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | } 16 | 17 | dependencies { 18 | compile project(':kratos') 19 | apt project(':kratos-compiler') 20 | compile 'com.android.support:appcompat-v7:23.1.1' 21 | } 22 | -------------------------------------------------------------------------------- /kratos-sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/merlin/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /kratos-sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /kratos-sample/src/main/java/me/ele/kratos_sample/CardSampleActivity.java: -------------------------------------------------------------------------------- 1 | package me.ele.kratos_sample; 2 | 3 | import android.widget.Toast; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import kratos.card.KCardActivity; 8 | import kratos.card.entity.KData; 9 | import kratos.card.event.KOnClickEvent; 10 | import me.ele.kratos_sample.entity.Customer; 11 | 12 | /** 13 | * Created by merlin on 15/12/14. 14 | */ 15 | public class CardSampleActivity extends KCardActivity { 16 | 17 | Customer customer = new Customer(); 18 | 19 | private void showToast(String text) { 20 | Toast.makeText(CardSampleActivity.this, text, Toast.LENGTH_SHORT).show(); 21 | } 22 | 23 | @Override 24 | public void onEventMainThread(@NotNull KOnClickEvent event) { 25 | super.onEventMainThread(event); 26 | switch (event.id) { 27 | case "textCard1": 28 | showToast("Handle click on textCard1!"); 29 | break; 30 | } 31 | } 32 | 33 | @Override 34 | public void onFinishRender() { 35 | bind(this, customer); 36 | customer.name.set("Merlin"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /kratos-sample/src/main/java/me/ele/kratos_sample/SimpleActivity.java: -------------------------------------------------------------------------------- 1 | package me.ele.kratos_sample; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import kratos.BindText; 8 | import kratos.Kratos; 9 | import kratos.card.utils.ActivityUtils; 10 | import kratos.internal.KString; 11 | 12 | /** 13 | * Created by merlin on 15/12/10. 14 | */ 15 | public class SimpleActivity extends Activity { 16 | 17 | private final int CODE_CARD_SAMPLE = 123; 18 | 19 | @BindText({R.id.test_doublebinding_input, R.id.test_doublebinding_presenter}) 20 | KString boundData1 = new KString(); 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_simple); 26 | Kratos.bind(this); 27 | findViewById(R.id.card_sample).setOnClickListener(new View.OnClickListener() { 28 | @Override 29 | public void onClick(View v) { 30 | ActivityUtils.jump(SimpleActivity.this, CardSampleActivity.class, CODE_CARD_SAMPLE, R.raw.sample); 31 | } 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /kratos-sample/src/main/java/me/ele/kratos_sample/TextCard.java: -------------------------------------------------------------------------------- 1 | package me.ele.kratos_sample; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.widget.TextView; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import kratos.Bind; 10 | import kratos.BindLayout; 11 | import kratos.Binds; 12 | import kratos.OnKStringChanged; 13 | import kratos.card.KCard; 14 | import me.ele.kratos_sample.entity.KText; 15 | 16 | /** 17 | * Created by merlin on 15/12/17. 18 | */ 19 | @BindLayout(R.layout.kcard_text) //@LBindLayout("kcard_text") 20 | @Binds({@Bind(id = R.id.kcard_text_text1, data = "text1"), 21 | @Bind(id = R.id.kcard_text_text2, data = "text2")}) 22 | public class TextCard extends KCard { 23 | public TextCard(Context context) { 24 | super(context); 25 | } 26 | 27 | @Override 28 | public void init() { 29 | setOnLinkListener(); 30 | } 31 | 32 | @OnKStringChanged("text1") 33 | public void updateText1(@NotNull TextView v, @NotNull String s) { 34 | v.setText(s); 35 | Log.d("TextCard", "custom updater!"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kratos-sample/src/main/java/me/ele/kratos_sample/entity/Customer.java: -------------------------------------------------------------------------------- 1 | package me.ele.kratos_sample.entity; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import kratos.internal.KString; 7 | 8 | /** 9 | * Created by merlin on 16/2/18. 10 | */ 11 | public class Customer implements Parcelable { 12 | 13 | public KString name = new KString(); 14 | 15 | @Override 16 | public int describeContents() { 17 | return 0; 18 | } 19 | 20 | @Override 21 | public void writeToParcel(Parcel dest, int flags) { 22 | dest.writeParcelable(this.name, flags); 23 | } 24 | 25 | public Customer() { 26 | } 27 | 28 | protected Customer(Parcel in) { 29 | this.name = in.readParcelable(KString.class.getClassLoader()); 30 | } 31 | 32 | public static final Creator CREATOR = new Creator() { 33 | public Customer createFromParcel(Parcel source) { 34 | return new Customer(source); 35 | } 36 | 37 | public Customer[] newArray(int size) { 38 | return new Customer[size]; 39 | } 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /kratos-sample/src/main/java/me/ele/kratos_sample/entity/KText.java: -------------------------------------------------------------------------------- 1 | package me.ele.kratos_sample.entity; 2 | 3 | import kratos.card.entity.KData; 4 | import kratos.internal.KString; 5 | 6 | /** 7 | * Created by merlin on 15/12/17. 8 | */ 9 | public class KText extends KData { 10 | public KString text1; 11 | public KString text2; 12 | } 13 | -------------------------------------------------------------------------------- /kratos-sample/src/main/java/me/ele/kratos_sample/package-info.java: -------------------------------------------------------------------------------- 1 | @PackageName package me.ele.kratos_sample; 2 | 3 | import kratos.PackageName; -------------------------------------------------------------------------------- /kratos-sample/src/main/res/layout/activity_simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 28 | 29 |