diff --git a/.gitignore b/.gitignore
index 21b81e0..508886f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,3 @@
-<<<<<<< HEAD
-*.iml
-.gradle
-/local.properties
-/.idea/workspace.xml
-/.idea/libraries
-.DS_Store
-/build
-/captures
-.externalNativeBuild
-=======
# Built application files
*.apk
*.ap_
@@ -31,37 +20,16 @@ build/
# Local configuration file (sdk path, etc)
local.properties
-# Proguard folder generated by Eclipse
-proguard/
-
-# Log Files
-*.log
-
# Android Studio Navigation editor temp files
.navigation/
+.externalNativeBuild
# Android Studio captures folder
captures/
# Intellij
*.iml
-.idea/workspace.xml
-.idea/tasks.xml
-.idea/gradle.xml
-.idea/dictionaries
-.idea/libraries
+.idea/
# Keystore files
*.jks
-
-# External native build folder generated in Android Studio 2.2 and later
-.externalNativeBuild
-
-# Google Services (e.g. APIs or Firebase)
-google-services.json
-
-# Freeline
-freeline.py
-freeline/
-freeline_project_description.json
->>>>>>> 853a1936d46ab75165caadb5be45deae38a12237
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index 96cc43e..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e7bedf3..0000000
--- a/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 3227a2e..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index cfec270..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 2d063cd..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 94a25f7..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index a98354c..a57fd94 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
- Copyright EastWood Yang {name of copyright owner}
+ Copyright 2018 EastWood Yang
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index cffafef..df73c33 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,160 @@
# MicroModule
-Restrict code boundary, divide module file structure into multi-parts
+重新定义Android模块结构,在模块内部可以创建多个和模块结构一致的微模块(MicroModule)。每一个MicroModule的结构和Android模块结构保持一致,也会有自己的`build.gradle`。另外,你可以很方便的配置哪些MicroModule参与APK的编译。
-## More Detail
-[http://www.jianshu.com/p/7f31cd67d513](http://www.jianshu.com/p/7f31cd67d513)
-(Chinese/中文)
+## Usage
+
+### 在根项目`build.gradle`中添加MicroModule插件依赖:
+
+```
+buildscript {
+ dependencies {
+ ...
+ classpath 'com.eastwood.tools.plugins:micro-module:1.4.0'
+ }
+}
+```
+
+### 在`application`或`library`类型的模块`build.gradle`中添加MicroModule插件:
+
+```
+apply plugin: 'micro-module'
+apply plugin: 'com.android.library' // or 'com.android.application'
+
+android {}
+
+microModule {
+ ...
+}
+
+dependencies {}
+```
+
+注意:MicroModule插件需要添加在android相关插件之前,相关配置`microModule {}` 需要添加在 `android {}` 和 `dependencies {}`之间。
+
+### microModule属性说明
+
+* **`include`**
+
+ 声明一个或多个MicroModule,类似于`setting.gradle`中的`include`,MicroModule目录名即为MicroModule的名称。
+
+ ```
+ microModule {
+ include 'p_base', 'p_common'
+
+ // 可以根据条件动态声明
+ if(debug) {
+ include 'debug'
+ } else {
+ include 'debug'
+ }
+ }
+ ```
+
+* **`export`**
+
+ 配置参与APK编译的MicroModule。如果未配置`export`,则所有`include`的MicroModule都会参与APK编译。
+
+ ```
+ microModule {
+ include 'feature_A', 'feature_B', 'feature_C'
+
+ export 'feature_A', 'feature_B'
+ }
+ ```
+
+* **`includeMain`**
+
+ 指定主MicroModule。
+ 当前模块的其他MicroModule的`AndroidManifest.xml`,将会合入主MicroModule的`AndroidManifest.xml`,并存放在`build/microModule/merge-manifest/`下。另外,当前模块的R类包名也将由主模块`AndroidManifest.xml`的`package`决定。
+
+ 默认主MicroModule为目录名为`main`的MicroModule。通过[MicroModule Android Studio插件](#jump)的转换功能,将模块转换成MicroModule格式时,无需指定主模块。转换功能工作只是创建一个`main`目录,并将原先`src`移动到`main`目录下,以及其他操作。
+
+
+* **`codeCheckEnabled`**
+
+ 是否开启MicroModule代码边界检查,默认不开启检查。
+
+ 有些场景下可能想使MicroModule在模块中保持独立,其类或资源不被该模块的其他MicroModule引用。代码边界检查在`sync&build`的时候进行,检测到没有依赖而存在引用时,会报错以及停止`sync&build`,并输出相应日志提示。
+
+ 开启代码边界检查后,一个模块内的MicroModule之间,需要声明依赖关系。例如:
+ ```
+ // Module build.gradle
+
+ microModule {
+ codeCheckEnabled true
+
+ include 'p_base', 'p_common'
+ include 'feature_A', 'feature_B'
+
+ export 'feature_A'
+ }
+
+ // MicroModule feature_A build.gradle
+
+ dependencies {
+ implementation microModule(':p_base')
+ implementation microModule(':p_common')
+ }
+
+ // MicroModule feature_B build.gradle
+
+ dependencies {
+ implementation microModule(':p_base')
+ implementation microModule(':p_common')
+ }
+ ```
+
+ 另外MicroModule所需的依赖,也可以在各自的`build.gradle` `dependencies {}`中声明(此处的依赖不在代码边界检查范围之内)。
+
+ ```
+ dependencies {
+ implementation fileTree(dir: 'main/libs', include: ['*.jar'])
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.0'
+
+ implementation microModule(':p_base')
+ implementation microModule(':p_common')
+ }
+ ```
+
+## MicroModule Android Studio Plugin
+Provides an action which allow you quickly create MicroModule or convert module to MicroModule.
+* Right click at module dir, in [New] group, you will find "MicroModule" action.
+* Right click at module dir, in [Refactor] group, you will find "Convert to MicroModule" action.
+
+
+
+
+
+**Install Step**:
+1. open [File] -> [Settings...] -> [plugins] -> [Browse repositories...]
+2. and search name **MicroModule**
+
+
+
+**Plugin detail**:
+
+[https://plugins.jetbrains.com/plugin/10785-micromodule](https://plugins.jetbrains.com/plugin/10785-micromodule)
+
+## Question or Idea
+有问题或想法可以直接加我微信: EastWoodYang
+
+## License
+
+```
+ Copyright 2018 EastWood Yang
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+```
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index 3cd00d5..0000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-apply plugin: 'com.android.application'
-
-//apply from: '../micro.gradle'
-apply from: '../micro_reference.gradle'
-
-android {
- compileSdkVersion 25
- buildToolsVersion "26.0.0"
- defaultConfig {
- applicationId "com.ycdyng.module.micro"
- minSdkVersion 15
- targetSdkVersion 25
- versionCode 1
- versionName "1.0"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
\ No newline at end of file
diff --git a/app/main/micro.properties b/app/main/micro.properties
deleted file mode 100644
index 57d3050..0000000
--- a/app/main/micro.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# java.srcDir=
-# res.srcDir=
-# manifest.srcFile=
-# ...
-
-# use micro.gradle
-# enable=false or true
-
-# use micro_reference.gradle
-# android.library.reference.1=./***
-
-# e.g.
-android.library.reference.3=./p_home
\ No newline at end of file
diff --git a/app/main/src/main/java/com/ycdyng/module/micro/MainActivity.java b/app/main/src/main/java/com/ycdyng/module/micro/MainActivity.java
deleted file mode 100644
index 3f17c41..0000000
--- a/app/main/src/main/java/com/ycdyng/module/micro/MainActivity.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.ycdyng.module.micro;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-public class MainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
-}
diff --git a/app/main/src/main/res/layout/activity_main.xml b/app/main/src/main/res/layout/activity_main.xml
deleted file mode 100644
index d829e29..0000000
--- a/app/main/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/main/src/main/res/values/strings.xml b/app/main/src/main/res/values/strings.xml
deleted file mode 100644
index 0d20034..0000000
--- a/app/main/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Product
-
diff --git a/app/p_base/micro.properties b/app/p_base/micro.properties
deleted file mode 100644
index a131e74..0000000
--- a/app/p_base/micro.properties
+++ /dev/null
@@ -1,10 +0,0 @@
-# java.srcDir=
-# res.srcDir=
-# manifest.srcFile=
-# ...
-
-# use micro.gradle
-# enable=false or true
-
-# use micro_reference.gradle
-# android.library.reference.1=./***
\ No newline at end of file
diff --git a/app/p_base/src/AndroidManifest.xml b/app/p_base/src/AndroidManifest.xml
deleted file mode 100644
index e6b28e4..0000000
--- a/app/p_base/src/AndroidManifest.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/p_base/src/main/java/com.ycdyng.module.micro/a.java b/app/p_base/src/main/java/com.ycdyng.module.micro/a.java
deleted file mode 100644
index e69de29..0000000
diff --git a/app/p_common/micro.properties b/app/p_common/micro.properties
deleted file mode 100644
index 358d2d1..0000000
--- a/app/p_common/micro.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# java.srcDir=
-# res.srcDir=
-# manifest.srcFile=
-# ...
-
-# use micro.gradle
-# enable=false or true
-
-# use micro_reference.gradle
-# android.library.reference.1=./***
-
-# e.g.
-android.library.reference.1=./p_base
\ No newline at end of file
diff --git a/app/p_common/src/main/java/com.ycdyng.module.micro/a.java b/app/p_common/src/main/java/com.ycdyng.module.micro/a.java
deleted file mode 100644
index e69de29..0000000
diff --git a/app/p_home/micro.properties b/app/p_home/micro.properties
deleted file mode 100644
index ef8d0e7..0000000
--- a/app/p_home/micro.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# java.srcDir=
-# res.srcDir=
-# manifest.srcFile=
-# ...
-
-# use micro.gradle
-# enable=false or true
-
-# use micro_reference.gradle
-# android.library.reference.1=./***
-
-# e.g.
-android.library.reference.1=./p_common
\ No newline at end of file
diff --git a/app/p_home/src/AndroidManifest.xml b/app/p_home/src/AndroidManifest.xml
deleted file mode 100644
index d110f47..0000000
--- a/app/p_home/src/AndroidManifest.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/p_home/src/main/java/com.ycdyng.module.micro/HomeActivity.java b/app/p_home/src/main/java/com.ycdyng.module.micro/HomeActivity.java
deleted file mode 100644
index 57c3738..0000000
--- a/app/p_home/src/main/java/com.ycdyng.module.micro/HomeActivity.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.ycdyng.module.micro;
-
-import android.app.Activity;
-
-public class HomeActivity extends Activity {
-}
diff --git a/app/.gitignore b/application/.gitignore
similarity index 100%
rename from app/.gitignore
rename to application/.gitignore
diff --git a/application/build.gradle b/application/build.gradle
new file mode 100644
index 0000000..9bc2388
--- /dev/null
+++ b/application/build.gradle
@@ -0,0 +1,82 @@
+apply plugin: 'micro-module'
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 27
+
+ defaultConfig {
+ applicationId "com.eastwood.demo"
+ minSdkVersion 14
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+// flavorDimensions "api", "mode"
+//
+// productFlavors {
+//
+// minApi24 {
+// dimension "api"
+// minSdkVersion 24
+// versionCode 30000 + android.defaultConfig.versionCode
+// versionNameSuffix "-minApi24"
+// }
+//
+// minApi23 {
+// dimension "api"
+// minSdkVersion 23
+// versionCode 20000 + android.defaultConfig.versionCode
+// versionNameSuffix "-minApi23"
+// }
+//
+// minApi21 {
+// dimension "api"
+// minSdkVersion 21
+// versionCode 10001 + android.defaultConfig.versionCode
+// versionNameSuffix "-minApi21"
+// }
+//
+// demo {
+// dimension "mode"
+// }
+//
+// full {
+// dimension "mode"
+// }
+// }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+microModule {
+ codeCheckEnabled true
+
+ include ':p_base'
+ include ':p_common'
+ include ':p_home'
+
+ export ':main', ':p_home'
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+
+ implementation project(':library')
+ implementation project(':kotlin')
+
+}
+
diff --git a/application/main/build.gradle b/application/main/build.gradle
new file mode 100644
index 0000000..2a25276
--- /dev/null
+++ b/application/main/build.gradle
@@ -0,0 +1,7 @@
+dependencies {
+ implementation fileTree(dir: 'main/libs', include: ['*.jar'])
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.0'
+
+ implementation microModule(':p_common')
+}
diff --git a/application/main/src/androidTest/java/com/eastwood/demo/application/ExampleInstrumentedTest.java b/application/main/src/androidTest/java/com/eastwood/demo/application/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..64e8eb4
--- /dev/null
+++ b/application/main/src/androidTest/java/com/eastwood/demo/application/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.eastwood.demo.application;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.eastwood.demo.application", appContext.getPackageName());
+ }
+}
diff --git a/application/main/src/demo/AndroidManifest.xml b/application/main/src/demo/AndroidManifest.xml
new file mode 100644
index 0000000..39442db
--- /dev/null
+++ b/application/main/src/demo/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/application/main/src/demo/java/com/eastwood/demo/application/TestDemo.java b/application/main/src/demo/java/com/eastwood/demo/application/TestDemo.java
new file mode 100644
index 0000000..0073099
--- /dev/null
+++ b/application/main/src/demo/java/com/eastwood/demo/application/TestDemo.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.application;
+
+import android.content.Context;
+
+/**
+ * @author eastwood
+ * createDate: 2018-10-15
+ */
+public class TestDemo {
+
+ void test(Context context) {
+// context.getResources().getString(R.string.home);
+ }
+}
diff --git a/application/main/src/demo/res/layout/activity_demo.xml b/application/main/src/demo/res/layout/activity_demo.xml
new file mode 100644
index 0000000..9bd6ad9
--- /dev/null
+++ b/application/main/src/demo/res/layout/activity_demo.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/application/main/src/full/AndroidManifest.xml b/application/main/src/full/AndroidManifest.xml
new file mode 100644
index 0000000..39442db
--- /dev/null
+++ b/application/main/src/full/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/application/main/src/full/java/com/eastwood/demo/application/TestFull.java b/application/main/src/full/java/com/eastwood/demo/application/TestFull.java
new file mode 100644
index 0000000..0500cee
--- /dev/null
+++ b/application/main/src/full/java/com/eastwood/demo/application/TestFull.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.application;
+
+import android.content.Context;
+
+/**
+ * @author eastwood
+ * createDate: 2018-10-15
+ */
+public class TestFull {
+
+ void test(Context context) {
+// context.getResources().getString(R.string.home);
+ }
+}
diff --git a/application/main/src/full/res/layout/activity_full.xml b/application/main/src/full/res/layout/activity_full.xml
new file mode 100644
index 0000000..2e3e002
--- /dev/null
+++ b/application/main/src/full/res/layout/activity_full.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/main/src/AndroidManifest.xml b/application/main/src/main/AndroidManifest.xml
similarity index 57%
rename from app/main/src/AndroidManifest.xml
rename to application/main/src/main/AndroidManifest.xml
index 9342ca4..23a7e7a 100644
--- a/app/main/src/AndroidManifest.xml
+++ b/application/main/src/main/AndroidManifest.xml
@@ -1,19 +1,21 @@
+
+ package="com.eastwood.demo.application" >
-
-
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme" >
+
-
\ No newline at end of file
diff --git a/application/main/src/main/java/com/eastwood/demo/application/MainActivity.java b/application/main/src/main/java/com/eastwood/demo/application/MainActivity.java
new file mode 100644
index 0000000..17380b4
--- /dev/null
+++ b/application/main/src/main/java/com/eastwood/demo/application/MainActivity.java
@@ -0,0 +1,17 @@
+package com.eastwood.demo.application;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+public class MainActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ // can't use [R.string.home] which from microModule ':p_home'.
+// getString(R.string.home);
+
+ }
+}
diff --git a/application/main/src/main/res/drawable-v24/ic_launcher_foreground.xml b/application/main/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..c7bd21d
--- /dev/null
+++ b/application/main/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/main/src/main/res/drawable/ic_launcher_background.xml b/application/main/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..01f0af0
--- /dev/null
+++ b/application/main/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/application/main/src/main/res/layout/activity_main.xml b/application/main/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..59a0b5a
--- /dev/null
+++ b/application/main/src/main/res/layout/activity_main.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/application/main/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/application/main/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/application/main/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/application/main/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/application/main/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..bbd3e02
--- /dev/null
+++ b/application/main/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/application/main/src/main/res/mipmap-hdpi/ic_launcher.png b/application/main/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a2f5908
Binary files /dev/null and b/application/main/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/application/main/src/main/res/mipmap-hdpi/ic_launcher_round.png b/application/main/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1b52399
Binary files /dev/null and b/application/main/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/application/main/src/main/res/mipmap-mdpi/ic_launcher.png b/application/main/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..ff10afd
Binary files /dev/null and b/application/main/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/application/main/src/main/res/mipmap-mdpi/ic_launcher_round.png b/application/main/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..115a4c7
Binary files /dev/null and b/application/main/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/application/main/src/main/res/mipmap-xhdpi/ic_launcher.png b/application/main/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..dcd3cd8
Binary files /dev/null and b/application/main/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/application/main/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/application/main/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..459ca60
Binary files /dev/null and b/application/main/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/application/main/src/main/res/mipmap-xxhdpi/ic_launcher.png b/application/main/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..8ca12fe
Binary files /dev/null and b/application/main/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/application/main/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/application/main/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..8e19b41
Binary files /dev/null and b/application/main/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/application/main/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/application/main/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b824ebd
Binary files /dev/null and b/application/main/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/application/main/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/application/main/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4c19a13
Binary files /dev/null and b/application/main/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/application/main/src/main/res/values/colors.xml b/application/main/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/application/main/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/application/main/src/main/res/values/strings.xml b/application/main/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ceea8af
--- /dev/null
+++ b/application/main/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Application
+
diff --git a/application/main/src/main/res/values/styles.xml b/application/main/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/application/main/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/application/main/src/minApi21/AndroidManifest.xml b/application/main/src/minApi21/AndroidManifest.xml
new file mode 100644
index 0000000..39442db
--- /dev/null
+++ b/application/main/src/minApi21/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/application/main/src/minApi21/java/com/eastwood/demo/application/TestMinApi21.java b/application/main/src/minApi21/java/com/eastwood/demo/application/TestMinApi21.java
new file mode 100644
index 0000000..1cd4b10
--- /dev/null
+++ b/application/main/src/minApi21/java/com/eastwood/demo/application/TestMinApi21.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.application;
+
+import android.content.Context;
+
+/**
+ * @author eastwood
+ * createDate: 2018-10-15
+ */
+public class TestMinApi21 {
+
+ void test(Context context) {
+// context.getResources().getString(R.string.home);
+ }
+}
diff --git a/application/main/src/minApi21/res/layout/activity_minapi21.xml b/application/main/src/minApi21/res/layout/activity_minapi21.xml
new file mode 100644
index 0000000..2e3e002
--- /dev/null
+++ b/application/main/src/minApi21/res/layout/activity_minapi21.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/application/main/src/minApi23/AndroidManifest.xml b/application/main/src/minApi23/AndroidManifest.xml
new file mode 100644
index 0000000..39442db
--- /dev/null
+++ b/application/main/src/minApi23/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/application/main/src/minApi23/java/com/eastwood/demo/application/TestMinApi23.java b/application/main/src/minApi23/java/com/eastwood/demo/application/TestMinApi23.java
new file mode 100644
index 0000000..c328140
--- /dev/null
+++ b/application/main/src/minApi23/java/com/eastwood/demo/application/TestMinApi23.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.application;
+
+import android.content.Context;
+
+/**
+ * @author eastwood
+ * createDate: 2018-10-15
+ */
+public class TestMinApi23 {
+
+ void test(Context context) {
+// context.getResources().getString(R.string.home);
+ }
+}
diff --git a/application/main/src/minApi23/res/layout/activity_minapi23.xml b/application/main/src/minApi23/res/layout/activity_minapi23.xml
new file mode 100644
index 0000000..2e3e002
--- /dev/null
+++ b/application/main/src/minApi23/res/layout/activity_minapi23.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/application/main/src/minApi24/AndroidManifest.xml b/application/main/src/minApi24/AndroidManifest.xml
new file mode 100644
index 0000000..39442db
--- /dev/null
+++ b/application/main/src/minApi24/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/application/main/src/minApi24/java/com/eastwood/demo/application/TestMinApi24.java b/application/main/src/minApi24/java/com/eastwood/demo/application/TestMinApi24.java
new file mode 100644
index 0000000..7ea46b8
--- /dev/null
+++ b/application/main/src/minApi24/java/com/eastwood/demo/application/TestMinApi24.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.application;
+
+import android.content.Context;
+
+/**
+ * @author eastwood
+ * createDate: 2018-10-15
+ */
+public class TestMinApi24 {
+
+ void test(Context context) {
+// context.getResources().getString(R.string.home);
+ }
+}
diff --git a/application/main/src/minApi24/res/layout/activity_minapi24.xml b/application/main/src/minApi24/res/layout/activity_minapi24.xml
new file mode 100644
index 0000000..2e3e002
--- /dev/null
+++ b/application/main/src/minApi24/res/layout/activity_minapi24.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/application/main/src/test/java/com/eastwood/demo/application/ExampleUnitTest.java b/application/main/src/test/java/com/eastwood/demo/application/ExampleUnitTest.java
new file mode 100644
index 0000000..bc542e6
--- /dev/null
+++ b/application/main/src/test/java/com/eastwood/demo/application/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.eastwood.demo.application;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/application/p_base/build.gradle b/application/p_base/build.gradle
new file mode 100644
index 0000000..2e3ca6a
--- /dev/null
+++ b/application/p_base/build.gradle
@@ -0,0 +1,3 @@
+dependencies {
+
+}
diff --git a/application/p_base/src/main/AndroidManifest.xml b/application/p_base/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..78a70ae
--- /dev/null
+++ b/application/p_base/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/application/p_base/src/main/java/com/eastwood/demo/application/base/Base.java b/application/p_base/src/main/java/com/eastwood/demo/application/base/Base.java
new file mode 100644
index 0000000..937e76c
--- /dev/null
+++ b/application/p_base/src/main/java/com/eastwood/demo/application/base/Base.java
@@ -0,0 +1,15 @@
+package com.eastwood.demo.application.base;
+
+import com.eastwood.demo.application.R;
+
+/**
+ * @author eastwood
+ * createDate: 2018-05-29
+ */
+public class Base {
+
+ void test() {
+ int i = R.string.base;
+ }
+
+}
diff --git a/application/p_base/src/main/res/values/strings.xml b/application/p_base/src/main/res/values/strings.xml
new file mode 100644
index 0000000..0c489c8
--- /dev/null
+++ b/application/p_base/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ base
+
+
\ No newline at end of file
diff --git a/application/p_common/build.gradle b/application/p_common/build.gradle
new file mode 100644
index 0000000..a2faa28
--- /dev/null
+++ b/application/p_common/build.gradle
@@ -0,0 +1,3 @@
+dependencies {
+ implementation microModule(':p_base')
+}
diff --git a/application/p_common/src/main/AndroidManifest.xml b/application/p_common/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b97d437
--- /dev/null
+++ b/application/p_common/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/application/p_common/src/main/java/com/eastwood/demo/application/common/Common.java b/application/p_common/src/main/java/com/eastwood/demo/application/common/Common.java
new file mode 100644
index 0000000..db55e08
--- /dev/null
+++ b/application/p_common/src/main/java/com/eastwood/demo/application/common/Common.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.application.common;
+
+import com.eastwood.demo.application.R;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-26
+ */
+public class Common {
+
+ void test() {
+ int i = R.string.common;
+ }
+}
diff --git a/application/p_common/src/main/res/values/strings.xml b/application/p_common/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ecdf4f9
--- /dev/null
+++ b/application/p_common/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ common
+
+
\ No newline at end of file
diff --git a/application/p_home/build.gradle b/application/p_home/build.gradle
new file mode 100644
index 0000000..d3c20b6
--- /dev/null
+++ b/application/p_home/build.gradle
@@ -0,0 +1,3 @@
+dependencies {
+ implementation microModule(':p_common')
+}
\ No newline at end of file
diff --git a/application/p_home/src/main/AndroidManifest.xml b/application/p_home/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3ceae98
--- /dev/null
+++ b/application/p_home/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/application/p_home/src/main/java/com/eastwood/demo/application/home/Home.java b/application/p_home/src/main/java/com/eastwood/demo/application/home/Home.java
new file mode 100644
index 0000000..c198f64
--- /dev/null
+++ b/application/p_home/src/main/java/com/eastwood/demo/application/home/Home.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.application.home;
+
+import com.eastwood.demo.application.R;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-26
+ */
+public class Home {
+
+ void test() {
+ int i = R.string.home;
+ }
+}
diff --git a/application/p_home/src/main/res/values/strings.xml b/application/p_home/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e61efd0
--- /dev/null
+++ b/application/p_home/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+
+
+ home
+
+
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/application/proguard-rules.pro
similarity index 70%
rename from app/proguard-rules.pro
rename to application/proguard-rules.pro
index 77de25c..f1b4245 100644
--- a/app/proguard-rules.pro
+++ b/application/proguard-rules.pro
@@ -1,14 +1,10 @@
# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in F:\Android\sdk/tools/proguard/proguard-android.txt
-# You can edit the include path and order by changing the proguardFiles
-# directive in build.gradle.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
-# Add any project specific keep options here:
-
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
diff --git a/build.gradle b/build.gradle
index c2eea8e..fa1eea0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,23 +1,25 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext.kotlin_version = '1.3.61'
repositories {
+ google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.3.3'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ classpath 'com.android.tools.build:gradle:3.2.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.eastwood.tools.plugins:micro-module:1.4.0'
}
}
allprojects {
repositories {
+ google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
-}
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index f1c49f3..87bc2cc 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,17 +1,17 @@
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
+## Project-wide Gradle settings.
+#
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
-
+#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
-
+# Default value: -Xmx1024m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+# org.gradle.parallel=true
+#Fri Nov 10 18:24:18 CST 2017
+org.gradle.jvmargs=-Xmx2048m
+# org.gradle.debug=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 13372ae..7a3265e 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index bfc67ee..85dd5ba 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Jul 24 10:26:08 CST 2017
+#Wed Jun 17 11:54:04 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/gradlew b/gradlew
index 9d82f78..cccdd3d 100644
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
##############################################################################
##
@@ -6,20 +6,38 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
-warn ( ) {
+warn () {
echo "$*"
}
-die ( ) {
+die () {
echo
echo "$*"
echo
@@ -30,6 +48,7 @@ die ( ) {
cygwin=false
msys=false
darwin=false
+nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -40,26 +59,11 @@ case "`uname`" in
MINGW* )
msys=true
;;
+ NONSTOP* )
+ nonstop=true
+ ;;
esac
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -85,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -150,11 +154,19 @@ if $cygwin ; then
esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282..f955316 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
diff --git a/kotlin/.gitignore b/kotlin/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/kotlin/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/kotlin/build.gradle b/kotlin/build.gradle
new file mode 100644
index 0000000..cd3b8bf
--- /dev/null
+++ b/kotlin/build.gradle
@@ -0,0 +1,52 @@
+apply plugin: 'micro-module'
+apply plugin: 'com.android.library'
+
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 27
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+
+ preview {
+
+ }
+ }
+
+ androidExtensions {
+ experimental = true
+ }
+}
+
+microModule {
+ include ':p_common'
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
+}
diff --git a/kotlin/main/build.gradle b/kotlin/main/build.gradle
new file mode 100644
index 0000000..bade2d6
--- /dev/null
+++ b/kotlin/main/build.gradle
@@ -0,0 +1,7 @@
+// MicroModule build file where you can add configuration options to publish MicroModule(aar) to Maven
+// and declare MicroModule dependencies.
+
+dependencies {
+ implementation fileTree(dir: 'main/libs', include: ['*.jar'])
+// implementation microModule(':p_common')
+}
diff --git a/kotlin/main/src/androidTest/java/com/eastwood/demo/kotlin/ExampleInstrumentedTest.java b/kotlin/main/src/androidTest/java/com/eastwood/demo/kotlin/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..c1d9baa
--- /dev/null
+++ b/kotlin/main/src/androidTest/java/com/eastwood/demo/kotlin/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.eastwood.demo.kotlin;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.eastwood.demo.kotlin", appContext.getPackageName());
+ }
+}
diff --git a/kotlin/main/src/main/AndroidManifest.xml b/kotlin/main/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6d9e8cf
--- /dev/null
+++ b/kotlin/main/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/kotlin/main/src/main/java/com/eastwood/demo/kotlin/Test.kt b/kotlin/main/src/main/java/com/eastwood/demo/kotlin/Test.kt
new file mode 100644
index 0000000..27aeeea
--- /dev/null
+++ b/kotlin/main/src/main/java/com/eastwood/demo/kotlin/Test.kt
@@ -0,0 +1,15 @@
+package com.eastwood.demo.kotlin
+
+/**
+ *
+ * @author eastwood
+ * createDate: 2019-01-18
+ */
+class Test {
+
+ fun test() {
+ // can't use [R.string.kotlin_common] which from microModule ':p_common'.
+// var i = R.string.kotlin_common
+ }
+
+}
\ No newline at end of file
diff --git a/kotlin/main/src/main/kotlin/com/eastwood/demo/kotlin/Test1.kt b/kotlin/main/src/main/kotlin/com/eastwood/demo/kotlin/Test1.kt
new file mode 100644
index 0000000..5b8cec8
--- /dev/null
+++ b/kotlin/main/src/main/kotlin/com/eastwood/demo/kotlin/Test1.kt
@@ -0,0 +1,15 @@
+package com.eastwood.demo.kotlin
+
+/**
+ *
+ * @author eastwood
+ * createDate: 2019-01-18
+ */
+class Test1 {
+
+ fun test() {
+ // can't use [R.string.kotlin_common] which from microModule ':p_common'.
+// var i = R.string.kotlin_common
+ }
+
+}
\ No newline at end of file
diff --git a/kotlin/main/src/main/res/values/strings.xml b/kotlin/main/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f11f745
--- /dev/null
+++ b/kotlin/main/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/kotlin/main/src/test/java/com/eastwood/demo/kotlin/ExampleUnitTest.java b/kotlin/main/src/test/java/com/eastwood/demo/kotlin/ExampleUnitTest.java
new file mode 100644
index 0000000..38b653c
--- /dev/null
+++ b/kotlin/main/src/test/java/com/eastwood/demo/kotlin/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.eastwood.demo.kotlin;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/kotlin/p_common/build.gradle b/kotlin/p_common/build.gradle
new file mode 100644
index 0000000..dd5852c
--- /dev/null
+++ b/kotlin/p_common/build.gradle
@@ -0,0 +1,6 @@
+// MicroModule build file where you can add configuration options to publish MicroModule(aar) to Maven
+// and declare MicroModule dependencies.
+
+dependencies {
+ implementation fileTree(dir: 'p_common/libs', include: ['*.jar'])
+}
diff --git a/kotlin/p_common/src/main/AndroidManifest.xml b/kotlin/p_common/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c204b27
--- /dev/null
+++ b/kotlin/p_common/src/main/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin/p_common/src/main/java/com/eastwood/demo/kotlin/common/Common.kt b/kotlin/p_common/src/main/java/com/eastwood/demo/kotlin/common/Common.kt
new file mode 100644
index 0000000..6ecf4a1
--- /dev/null
+++ b/kotlin/p_common/src/main/java/com/eastwood/demo/kotlin/common/Common.kt
@@ -0,0 +1,17 @@
+package com.eastwood.demo.kotlin.common
+
+import com.eastwood.demo.kotlin.R
+
+/**
+ *
+ * @author eastwood
+ * createDate: 2019-01-18
+ */
+class Common {
+
+ fun test() {
+
+ var i = R.string.kotlin_common
+ }
+
+}
\ No newline at end of file
diff --git a/kotlin/p_common/src/main/kotlin/com/eastwood/demo/kotlin/common/Common1.kt b/kotlin/p_common/src/main/kotlin/com/eastwood/demo/kotlin/common/Common1.kt
new file mode 100644
index 0000000..d44519b
--- /dev/null
+++ b/kotlin/p_common/src/main/kotlin/com/eastwood/demo/kotlin/common/Common1.kt
@@ -0,0 +1,16 @@
+package com.eastwood.demo.kotlin.common
+
+import com.eastwood.demo.kotlin.R
+
+/**
+ *
+ * @author eastwood
+ * createDate: 2019-01-18
+ */
+class Common1 {
+
+ fun test() {
+ var i = R.string.kotlin_common
+ }
+
+}
\ No newline at end of file
diff --git a/kotlin/p_common/src/main/res/values/strings.xml b/kotlin/p_common/src/main/res/values/strings.xml
new file mode 100644
index 0000000..fda18d5
--- /dev/null
+++ b/kotlin/p_common/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ kotlin_common
+
diff --git a/kotlin/proguard-rules.pro b/kotlin/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/kotlin/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/library/.gitignore b/library/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/library/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/library/build.gradle b/library/build.gradle
new file mode 100644
index 0000000..6079478
--- /dev/null
+++ b/library/build.gradle
@@ -0,0 +1,79 @@
+apply plugin: 'micro-module'
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 27
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+// flavorDimensions "api", "mode"
+//
+// productFlavors {
+//
+// minApi24 {
+// dimension "api"
+// minSdkVersion 24
+// versionCode 30000 + android.defaultConfig.versionCode
+// versionNameSuffix "-minApi24"
+// }
+//
+// minApi23 {
+// dimension "api"
+// minSdkVersion 23
+// versionCode 20000 + android.defaultConfig.versionCode
+// versionNameSuffix "-minApi23"
+// }
+//
+// minApi21 {
+// dimension "api"
+// minSdkVersion 21
+// versionCode 10000 + android.defaultConfig.versionCode
+// versionNameSuffix "-minApi21"
+// }
+//
+// demo {
+// dimension "mode"
+// }
+//
+// full {
+// dimension "mode"
+// }
+//
+// }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+}
+
+microModule {
+ codeCheckEnabled true
+
+ include ':p_base'
+ include ':p_common'
+ include ':p_utils'
+
+ export ':main'
+}
diff --git a/library/main/build.gradle b/library/main/build.gradle
new file mode 100644
index 0000000..39c415e
--- /dev/null
+++ b/library/main/build.gradle
@@ -0,0 +1,4 @@
+
+dependencies {
+ implementation microModule(':p_common')
+}
\ No newline at end of file
diff --git a/library/main/src/androidTest/java/com/eastwood/demo/library/ExampleInstrumentedTest.java b/library/main/src/androidTest/java/com/eastwood/demo/library/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..92672ac
--- /dev/null
+++ b/library/main/src/androidTest/java/com/eastwood/demo/library/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.eastwood.demo.library;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.eastwood.demo.library.test", appContext.getPackageName());
+ }
+}
diff --git a/library/main/src/demo/AndroidManifest.xml b/library/main/src/demo/AndroidManifest.xml
new file mode 100644
index 0000000..d6c4bd1
--- /dev/null
+++ b/library/main/src/demo/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/main/src/demo/java/com/eastwood/demo/library/TestDemo.java b/library/main/src/demo/java/com/eastwood/demo/library/TestDemo.java
new file mode 100644
index 0000000..6944e7b
--- /dev/null
+++ b/library/main/src/demo/java/com/eastwood/demo/library/TestDemo.java
@@ -0,0 +1,8 @@
+package com.eastwood.demo.library;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-24
+ */
+public class TestDemo {
+}
diff --git a/library/main/src/full/AndroidManifest.xml b/library/main/src/full/AndroidManifest.xml
new file mode 100644
index 0000000..d6c4bd1
--- /dev/null
+++ b/library/main/src/full/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/main/src/full/java/com/eastwood/demo/library/Full.java b/library/main/src/full/java/com/eastwood/demo/library/Full.java
new file mode 100644
index 0000000..b7b7a8a
--- /dev/null
+++ b/library/main/src/full/java/com/eastwood/demo/library/Full.java
@@ -0,0 +1,8 @@
+package com.eastwood.demo.library;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-24
+ */
+public class Full {
+}
diff --git a/library/main/src/main/AndroidManifest.xml b/library/main/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d6119aa
--- /dev/null
+++ b/library/main/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/library/main/src/main/java/com/eastwood/demo/library/Test.java b/library/main/src/main/java/com/eastwood/demo/library/Test.java
new file mode 100644
index 0000000..8aa62ba
--- /dev/null
+++ b/library/main/src/main/java/com/eastwood/demo/library/Test.java
@@ -0,0 +1,15 @@
+package com.eastwood.demo.library;
+
+import android.content.Context;
+
+/**
+ * @author eastwood
+ * createDate: 2018-05-30
+ */
+public class Test {
+
+ public void get(Context context) {
+ // can't use [R.string.test_code_check] which from microModule ':p_utils'.
+// context.getString(R.string.test_code_check_utils);
+ }
+}
diff --git a/library/main/src/main/res/values/strings.xml b/library/main/src/main/res/values/strings.xml
new file mode 100644
index 0000000..9d7a64b
--- /dev/null
+++ b/library/main/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/library/main/src/minApi21/AndroidManifest.xml b/library/main/src/minApi21/AndroidManifest.xml
new file mode 100644
index 0000000..d6c4bd1
--- /dev/null
+++ b/library/main/src/minApi21/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/main/src/minApi21/java/com/eastwood/demo/library/TestMinApi21.java b/library/main/src/minApi21/java/com/eastwood/demo/library/TestMinApi21.java
new file mode 100644
index 0000000..a8e5561
--- /dev/null
+++ b/library/main/src/minApi21/java/com/eastwood/demo/library/TestMinApi21.java
@@ -0,0 +1,8 @@
+package com.eastwood.demo.library;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-24
+ */
+public class TestMinApi21 {
+}
diff --git a/library/main/src/minApi23/AndroidManifest.xml b/library/main/src/minApi23/AndroidManifest.xml
new file mode 100644
index 0000000..d6c4bd1
--- /dev/null
+++ b/library/main/src/minApi23/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/main/src/minApi23/java/com/eastwood/demo/library/TestMinApi23.java b/library/main/src/minApi23/java/com/eastwood/demo/library/TestMinApi23.java
new file mode 100644
index 0000000..5a6080f
--- /dev/null
+++ b/library/main/src/minApi23/java/com/eastwood/demo/library/TestMinApi23.java
@@ -0,0 +1,8 @@
+package com.eastwood.demo.library;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-24
+ */
+public class TestMinApi23 {
+}
diff --git a/library/main/src/minApi24/AndroidManifest.xml b/library/main/src/minApi24/AndroidManifest.xml
new file mode 100644
index 0000000..d6c4bd1
--- /dev/null
+++ b/library/main/src/minApi24/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/main/src/minApi24/java/com/eastwood/demo/library/TestMinApi24.java b/library/main/src/minApi24/java/com/eastwood/demo/library/TestMinApi24.java
new file mode 100644
index 0000000..e2067af
--- /dev/null
+++ b/library/main/src/minApi24/java/com/eastwood/demo/library/TestMinApi24.java
@@ -0,0 +1,8 @@
+package com.eastwood.demo.library;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-24
+ */
+public class TestMinApi24 {
+}
diff --git a/library/main/src/test/java/com/eastwood/demo/library/ExampleUnitTest.java b/library/main/src/test/java/com/eastwood/demo/library/ExampleUnitTest.java
new file mode 100644
index 0000000..3ea3a0b
--- /dev/null
+++ b/library/main/src/test/java/com/eastwood/demo/library/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.eastwood.demo.library;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/library/p_base/build.gradle b/library/p_base/build.gradle
new file mode 100644
index 0000000..5fbab79
--- /dev/null
+++ b/library/p_base/build.gradle
@@ -0,0 +1,4 @@
+
+dependencies {
+
+}
\ No newline at end of file
diff --git a/library/p_base/src/main/AndroidManifest.xml b/library/p_base/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2969872
--- /dev/null
+++ b/library/p_base/src/main/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/library/p_base/src/main/java/com/eastwood/demo/library/base/Base.java b/library/p_base/src/main/java/com/eastwood/demo/library/base/Base.java
new file mode 100644
index 0000000..f6d973e
--- /dev/null
+++ b/library/p_base/src/main/java/com/eastwood/demo/library/base/Base.java
@@ -0,0 +1,15 @@
+package com.eastwood.demo.library.base;
+
+import com.eastwood.demo.library.R;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-09
+ */
+public class Base {
+
+ void test() {
+ int i = R.string.test_code_check_base;
+ }
+
+}
diff --git a/library/p_base/src/main/res/values/strings.xml b/library/p_base/src/main/res/values/strings.xml
new file mode 100644
index 0000000..a63156f
--- /dev/null
+++ b/library/p_base/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ base
+
\ No newline at end of file
diff --git a/library/p_common/build.gradle b/library/p_common/build.gradle
new file mode 100644
index 0000000..e9f1107
--- /dev/null
+++ b/library/p_common/build.gradle
@@ -0,0 +1,4 @@
+
+dependencies {
+ implementation microModule(':p_base')
+}
\ No newline at end of file
diff --git a/library/p_common/src/main/AndroidManifest.xml b/library/p_common/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..3d672d3
--- /dev/null
+++ b/library/p_common/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/library/p_common/src/main/java/com/eastwood/demo/library/common/Common.java b/library/p_common/src/main/java/com/eastwood/demo/library/common/Common.java
new file mode 100644
index 0000000..a62be15
--- /dev/null
+++ b/library/p_common/src/main/java/com/eastwood/demo/library/common/Common.java
@@ -0,0 +1,15 @@
+package com.eastwood.demo.library.common;
+
+import com.eastwood.demo.library.R;
+
+/**
+ * @author eastwood
+ * createDate: 2018-11-09
+ */
+public class Common {
+
+ void test() {
+ int i = R.string.test_code_check_common;
+ }
+
+}
diff --git a/library/p_common/src/main/res/values/strings.xml b/library/p_common/src/main/res/values/strings.xml
new file mode 100644
index 0000000..04c8039
--- /dev/null
+++ b/library/p_common/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ common
+
\ No newline at end of file
diff --git a/library/p_utils/build.gradle b/library/p_utils/build.gradle
new file mode 100644
index 0000000..5fbab79
--- /dev/null
+++ b/library/p_utils/build.gradle
@@ -0,0 +1,4 @@
+
+dependencies {
+
+}
\ No newline at end of file
diff --git a/library/p_utils/src/demo/AndroidManifest.xml b/library/p_utils/src/demo/AndroidManifest.xml
new file mode 100644
index 0000000..d6c4bd1
--- /dev/null
+++ b/library/p_utils/src/demo/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/p_utils/src/full/AndroidManifest.xml b/library/p_utils/src/full/AndroidManifest.xml
new file mode 100644
index 0000000..6335e63
--- /dev/null
+++ b/library/p_utils/src/full/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/library/p_utils/src/main/AndroidManifest.xml b/library/p_utils/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5affc42
--- /dev/null
+++ b/library/p_utils/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/library/p_utils/src/main/java/com/eastwood/demo/library/utils/Utils.java b/library/p_utils/src/main/java/com/eastwood/demo/library/utils/Utils.java
new file mode 100644
index 0000000..04cf3ab
--- /dev/null
+++ b/library/p_utils/src/main/java/com/eastwood/demo/library/utils/Utils.java
@@ -0,0 +1,14 @@
+package com.eastwood.demo.library.utils;
+
+import com.eastwood.demo.library.R;
+
+/**
+ * @author eastwood
+ * createDate: 2018-05-29
+ */
+public class Utils {
+
+ void test() {
+ int i = R.string.test_code_check_utils;
+ }
+}
diff --git a/library/p_utils/src/main/res/values/strings.xml b/library/p_utils/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e7de997
--- /dev/null
+++ b/library/p_utils/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ utils
+
\ No newline at end of file
diff --git a/library/p_utils/src/minApi21/AndroidManifest.xml b/library/p_utils/src/minApi21/AndroidManifest.xml
new file mode 100644
index 0000000..23f7038
--- /dev/null
+++ b/library/p_utils/src/minApi21/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/p_utils/src/minApi23/AndroidManifest.xml b/library/p_utils/src/minApi23/AndroidManifest.xml
new file mode 100644
index 0000000..23f7038
--- /dev/null
+++ b/library/p_utils/src/minApi23/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/p_utils/src/minApi24/AndroidManifest.xml b/library/p_utils/src/minApi24/AndroidManifest.xml
new file mode 100644
index 0000000..23f7038
--- /dev/null
+++ b/library/p_utils/src/minApi24/AndroidManifest.xml
@@ -0,0 +1 @@
+
diff --git a/library/proguard-rules.pro b/library/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/library/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/micro-module/.gitignore b/micro-module/.gitignore
new file mode 100644
index 0000000..eaeb95b
--- /dev/null
+++ b/micro-module/.gitignore
@@ -0,0 +1,35 @@
+# Built application files
+*.apk
+*.ap_
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Android Studio Navigation editor temp files
+.navigation/
+.externalNativeBuild
+
+# Android Studio captures folder
+captures/
+
+# Intellij
+*.iml
+.idea
+
+# Keystore files
+*.jks
diff --git a/micro-module/build.gradle b/micro-module/build.gradle
new file mode 100644
index 0000000..48668a4
--- /dev/null
+++ b/micro-module/build.gradle
@@ -0,0 +1,53 @@
+apply plugin: 'groovy'
+apply plugin: 'java'
+
+group 'com.eastwood.tools.plugins'
+sourceCompatibility = 1.8
+
+repositories {
+ google()
+ jcenter()
+}
+
+dependencies {
+ compile gradleApi()
+ compile localGroovy()
+ implementation 'com.android.tools.build:gradle:3.1.0'
+}
+
+apply plugin: 'maven'
+
+def groupId = 'com.eastwood.tools.plugins'
+def artifactId = 'micro-module'
+def version = '1.4.0'
+
+def localReleaseDest = "${buildDir}/release/${version}"
+
+uploadArchives {
+ repositories {
+ mavenDeployer {
+ pom.groupId = groupId
+ pom.artifactId = artifactId
+ pom.version = version
+ // Add other pom properties here if you want (developer details / licenses)
+ repository(url: "file://${localReleaseDest}")
+ }
+ }
+}
+
+
+task zipRelease(type: Zip) {
+ from localReleaseDest
+ destinationDir buildDir
+ archiveName "release-${version}.zip"
+}
+
+task generateRelease {
+ doLast {
+ println "Release ${version} can be found at ${localReleaseDest}/"
+ println "Release ${version} zipped can be found ${buildDir}/release-${version}.zip"
+ }
+}
+
+generateRelease.dependsOn(uploadArchives)
+generateRelease.dependsOn(zipRelease)
\ No newline at end of file
diff --git a/micro-module/settings.gradle b/micro-module/settings.gradle
new file mode 100644
index 0000000..6811b02
--- /dev/null
+++ b/micro-module/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'micro-module'
+
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/MicroModulePlugin.groovy b/micro-module/src/main/groovy/com/eastwood/tools/plugins/MicroModulePlugin.groovy
new file mode 100644
index 0000000..5e94f2b
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/MicroModulePlugin.groovy
@@ -0,0 +1,613 @@
+package com.eastwood.tools.plugins
+
+import com.android.build.gradle.*
+import com.android.build.gradle.api.BaseVariant
+import com.android.build.gradle.api.BaseVariantOutput
+import com.android.builder.model.ProductFlavor
+import com.android.manifmerger.ManifestMerger2
+import com.android.manifmerger.MergingReport
+import com.android.manifmerger.XmlDocument
+import com.android.utils.ILogger
+import com.eastwood.tools.plugins.core.MicroModule
+import com.eastwood.tools.plugins.core.MicroModuleInfo
+import com.eastwood.tools.plugins.core.ProductFlavorInfo
+import com.eastwood.tools.plugins.core.Utils
+import com.eastwood.tools.plugins.core.check.CodeChecker
+import com.eastwood.tools.plugins.core.extension.*
+import org.gradle.BuildListener
+import org.gradle.BuildResult
+import org.gradle.api.*
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.initialization.Settings
+import org.gradle.api.invocation.Gradle
+import org.gradle.api.tasks.compile.JavaCompile
+
+class MicroModulePlugin implements Plugin {
+
+ private final static String NORMAL = 'normal'
+ private final static String ASSEMBLE_OR_GENERATE = 'assemble_or_generate'
+
+ private final static String APPLY_NORMAL_MICRO_MODULE_SCRIPT = 'apply_normal_micro_module_script'
+ private final static String APPLY_INCLUDE_MICRO_MODULE_SCRIPT = 'apply_include_micro_module_script'
+ private final static String APPLY_EXPORT_MICRO_MODULE_SCRIPT = 'apply_export_micro_module_script'
+
+ private final static BuildListener buildListener = new BuildListener() {
+
+ @Override
+ void buildStarted(Gradle gradle) {
+
+ }
+
+ @Override
+ void settingsEvaluated(Settings settings) {
+
+ }
+
+ @Override
+ void projectsLoaded(Gradle gradle) {
+
+ }
+
+ @Override
+ void projectsEvaluated(Gradle gradle) {
+
+ }
+
+ @Override
+ void buildFinished(BuildResult buildResult) {
+ // generate microModules.xml for MicroModule IDEA plugin.
+ def ideaFile = new File(buildResult.gradle.rootProject.rootDir, '.idea')
+ if (!ideaFile.exists()) return
+
+ def microModuleInfo = '\n\n'
+ buildResult.gradle.rootProject.allprojects.each {
+ MicroModulePlugin microModulePlugin = it.plugins.findPlugin('micro-module')
+ if (microModulePlugin == null) return
+
+ def displayName = it.displayName
+ microModuleInfo += ' \n'
+ microModulePlugin.microModuleInfo.includeMicroModules.each {
+ MicroModule microModule = it.value
+ microModuleInfo += ' \n'
+ }
+ microModuleInfo += ' \n'
+ }
+ microModuleInfo += ''
+
+ def microModules = new File(ideaFile, 'microModules.xml')
+ microModules.write(microModuleInfo, 'utf-8')
+ }
+ }
+
+ Project project
+
+ String startTaskState = NORMAL
+
+ MicroModuleInfo microModuleInfo
+ ProductFlavorInfo productFlavorInfo
+
+ MicroModule currentMicroModule
+ String applyScriptState
+
+ boolean appliedLibraryPlugin
+
+ boolean clearedOriginSourceSets
+
+ void apply(Project project) {
+ this.project = project
+ this.microModuleInfo = new MicroModuleInfo(project)
+
+ project.gradle.removeListener(buildListener)
+ project.gradle.addBuildListener(buildListener)
+
+ if (project.gradle.getStartParameter().taskNames.size() == 0) {
+ startTaskState = NORMAL
+ } else {
+ startTaskState = ASSEMBLE_OR_GENERATE
+ }
+
+ if (startTaskState != NORMAL) {
+ project.getConfigurations().whenObjectAdded {
+ Configuration configuration = it
+ configuration.dependencies.whenObjectAdded {
+ if (applyScriptState == APPLY_INCLUDE_MICRO_MODULE_SCRIPT) {
+ configuration.dependencies.remove(it)
+ return
+ } else if (applyScriptState == APPLY_NORMAL_MICRO_MODULE_SCRIPT
+ || applyScriptState == APPLY_EXPORT_MICRO_MODULE_SCRIPT) {
+ return
+ } else if (currentMicroModule == null && startTaskState == ASSEMBLE_OR_GENERATE) {
+ return
+ } else if (it.group != null && it.group.startsWith('com.android.tools')) {
+ return
+ }
+
+ configuration.dependencies.remove(it)
+ }
+ }
+ }
+
+ DefaultMicroModuleExtension microModuleExtension = project.extensions.create(MicroModuleExtension, 'microModule', DefaultMicroModuleExtension, project)
+ microModuleExtension.onMicroModuleListener = new OnMicroModuleListener() {
+
+ @Override
+ void addIncludeMicroModule(MicroModule microModule, boolean mainMicroModule) {
+ if (mainMicroModule) {
+ microModuleInfo.setMainMicroModule(microModule)
+ } else {
+ microModuleInfo.addIncludeMicroModule(microModule)
+ }
+
+ if(!clearedOriginSourceSets) {
+ productFlavorInfo = new ProductFlavorInfo(project)
+ clearedOriginSourceSets = true
+ clearOriginSourceSet()
+
+ if(microModuleInfo.mainMicroModule != null) {
+ addMicroModuleSourceSet(microModuleInfo.mainMicroModule)
+ }
+ }
+
+ addMicroModuleSourceSet(microModule)
+ }
+
+ @Override
+ void addExportMicroModule(String... microModulePaths) {
+ microModulePaths.each {
+ microModuleInfo.addExportMicroModule(it)
+ }
+ }
+
+ }
+
+ project.dependencies.metaClass.microModule { String path ->
+ if (currentMicroModule == null || applyScriptState == APPLY_NORMAL_MICRO_MODULE_SCRIPT) {
+ return []
+ }
+
+ if (applyScriptState == APPLY_INCLUDE_MICRO_MODULE_SCRIPT) {
+ microModuleInfo.setMicroModuleDependency(currentMicroModule.name, path)
+ return []
+ }
+
+ MicroModule microModule = microModuleInfo.getMicroModule(path)
+
+ def result = []
+ if (startTaskState == ASSEMBLE_OR_GENERATE) {
+ addMicroModuleSourceSet(microModule)
+ applyMicroModuleScript(microModule)
+ microModule.appliedScript = true
+ }
+ return result
+ }
+
+ project.plugins.all {
+ Class extensionClass
+ if (it instanceof AppPlugin) {
+ extensionClass = AppExtension
+ } else if (it instanceof LibraryPlugin) {
+ extensionClass = LibraryExtension
+ } else {
+ return
+ }
+
+ project.extensions.configure(extensionClass, new Action extends TestedExtension>() {
+ @Override
+ void execute(TestedExtension testedExtension) {
+ boolean isLibrary
+ DomainObjectSet baseVariants
+ if (testedExtension instanceof AppExtension) {
+ AppExtension appExtension = (AppExtension) testedExtension
+ baseVariants = appExtension.applicationVariants
+ } else {
+ LibraryExtension libraryExtension = (LibraryExtension) testedExtension
+ baseVariants = libraryExtension.libraryVariants
+ isLibrary = true
+ }
+
+ baseVariants.all { BaseVariant variant ->
+ if (microModuleExtension.codeCheckEnabled) {
+ def taskNamePrefix = isLibrary ? 'package' : 'merge'
+ List sourceFolders = new ArrayList<>()
+ sourceFolders.add('main')
+ sourceFolders.add(variant.buildType.name)
+ if (variant.productFlavors.size() > 0) {
+ sourceFolders.add(variant.name)
+ sourceFolders.add(variant.flavorName)
+ for (ProductFlavor productFlavor : variant.productFlavors) {
+ sourceFolders.add(productFlavor.name)
+ }
+ checkMicroModuleBoundary(taskNamePrefix, variant.buildType.name, variant.flavorName, sourceFolders)
+ } else {
+ checkMicroModuleBoundary(taskNamePrefix, variant.buildType.name, null, sourceFolders)
+ }
+ }
+ }
+ }
+ })
+ }
+
+ project.afterEvaluate {
+ microModuleExtension.onMicroModuleListener = null
+ if (microModuleInfo.mainMicroModule == null) {
+ throw new GradleException("the main MicroModule could not be found in ${project.getDisplayName()}.")
+ }
+
+ appliedLibraryPlugin = project.pluginManager.hasPlugin('com.android.library')
+
+ productFlavorInfo = new ProductFlavorInfo(project)
+
+ applyScriptState = APPLY_INCLUDE_MICRO_MODULE_SCRIPT
+ microModuleInfo.includeMicroModules.each {
+ MicroModule microModule = it.value
+ microModuleInfo.dependencyGraph.add(microModule.name)
+ applyMicroModuleScript(microModule)
+ }
+
+ clearOriginSourceSet()
+ if (startTaskState == ASSEMBLE_OR_GENERATE) {
+ applyScriptState = APPLY_EXPORT_MICRO_MODULE_SCRIPT
+ boolean hasExportMainMicroModule = false
+ boolean isEmpty = microModuleInfo.exportMicroModules.isEmpty()
+ List dependencySort = microModuleInfo.dependencyGraph.topSort()
+ dependencySort.each {
+ if (isEmpty || microModuleInfo.exportMicroModules.containsKey(it)) {
+ MicroModule microModule = microModuleInfo.getMicroModule(it)
+ if (microModule == null) {
+ throw new GradleException("MicroModule with path '${it}' could not be found in ${project.getDisplayName()}.")
+ }
+
+ if (microModule == microModuleInfo.mainMicroModule) {
+ hasExportMainMicroModule = true
+ }
+
+ if (microModule.appliedScript) return
+
+ addMicroModuleSourceSet(microModule)
+ applyMicroModuleScript(microModule)
+ microModule.appliedScript = true
+ }
+ }
+
+ if (!hasExportMainMicroModule) {
+ throw new GradleException("the main MicroModule '${microModuleInfo.mainMicroModule.name}' is not in the export list.")
+ }
+ } else {
+ applyScriptState = APPLY_NORMAL_MICRO_MODULE_SCRIPT
+ microModuleInfo.includeMicroModules.each {
+ MicroModule microModule = it.value
+ addMicroModuleSourceSet(microModule)
+ applyMicroModuleScript(microModule)
+ }
+ }
+ currentMicroModule = null
+
+ generateAndroidManifest()
+
+ project.tasks.preBuild.doFirst {
+ clearOriginSourceSet()
+ if (startTaskState == ASSEMBLE_OR_GENERATE) {
+ microModuleInfo.includeMicroModules.each {
+ MicroModule microModule = it.value
+ if (microModule.appliedScript) {
+ addMicroModuleSourceSet(microModule)
+ }
+ }
+ } else {
+ microModuleInfo.includeMicroModules.each {
+ addMicroModuleSourceSet(it.value)
+ }
+ }
+ generateAndroidManifest()
+ }
+ }
+ }
+
+ def generateAndroidManifest() {
+ if ((startTaskState == ASSEMBLE_OR_GENERATE || !microModuleInfo.exportMicroModules.isEmpty()) && isMainSourceSetEmpty()) {
+ setMainSourceSetManifest()
+ return
+ }
+ mergeAndroidManifest('main')
+
+ productFlavorInfo.buildTypes.each {
+ mergeAndroidManifest(it)
+ }
+
+ if (!productFlavorInfo.singleDimension) {
+ productFlavorInfo.productFlavors.each {
+ mergeAndroidManifest(it)
+ }
+ }
+
+ productFlavorInfo.combinedProductFlavors.each {
+ mergeAndroidManifest(it)
+
+ def productFlavor = it
+ productFlavorInfo.buildTypes.each {
+ mergeAndroidManifest(productFlavor + Utils.upperCase(it))
+ }
+ }
+
+ def androidTest = 'androidTest'
+ mergeAndroidManifest(androidTest)
+ mergeAndroidManifest(androidTest + 'Debug')
+ if (!productFlavorInfo.singleDimension) {
+ productFlavorInfo.productFlavors.each {
+ mergeAndroidManifest(androidTest + Utils.upperCase(it))
+ }
+ }
+ productFlavorInfo.combinedProductFlavors.each {
+ mergeAndroidManifest(androidTest + Utils.upperCase(it))
+ mergeAndroidManifest(androidTest + Utils.upperCase(it) + 'Debug')
+ }
+ }
+
+ def mergeAndroidManifest(String variantName) {
+ File mainManifestFile = new File(microModuleInfo.mainMicroModule.microModuleDir, "/src/${variantName}/AndroidManifest.xml")
+ if (!mainManifestFile.exists()) return
+ ManifestMerger2.MergeType mergeType = ManifestMerger2.MergeType.APPLICATION
+ XmlDocument.Type documentType = XmlDocument.Type.MAIN
+ def logger = new ILogger() {
+ @Override
+ void error(Throwable t, String msgFormat, Object... args) {
+ println(msgFormat)
+ }
+
+ @Override
+ void warning(String msgFormat, Object... args) {
+
+ }
+
+ @Override
+ void info(String msgFormat, Object... args) {
+
+ }
+
+ @Override
+ void verbose(String msgFormat, Object... args) {
+
+ }
+ }
+ ManifestMerger2.Invoker invoker = new ManifestMerger2.Invoker(mainManifestFile, logger, mergeType, documentType)
+ invoker.withFeatures(ManifestMerger2.Invoker.Feature.NO_PLACEHOLDER_REPLACEMENT)
+
+ microModuleInfo.includeMicroModules.each {
+ MicroModule microModule = it.value
+ if (startTaskState == ASSEMBLE_OR_GENERATE && !microModule.appliedScript) return
+ if (microModule.name == microModuleInfo.mainMicroModule.name) return
+ def microManifestFile = new File(microModule.microModuleDir, "/src/${variantName}/AndroidManifest.xml")
+ if (microManifestFile.exists()) {
+ invoker.addLibraryManifest(microManifestFile)
+ }
+ }
+
+ def mergingReport = invoker.merge()
+ if (!mergingReport.result.success) {
+ mergingReport.log(logger)
+ throw new GradleException(mergingReport.reportString)
+ }
+ def moduleAndroidManifest = mergingReport.getMergedDocument(MergingReport.MergedManifestKind.MERGED)
+ moduleAndroidManifest = new String(moduleAndroidManifest.getBytes('UTF-8'))
+
+ def saveDir = new File(project.projectDir, "build/microModule/merge-manifest/${variantName}")
+ saveDir.mkdirs()
+ def AndroidManifestFile = new File(saveDir, 'AndroidManifest.xml')
+ AndroidManifestFile.createNewFile()
+ AndroidManifestFile.write(moduleAndroidManifest)
+
+ def extensionContainer = project.getExtensions()
+ BaseExtension android = extensionContainer.getByName('android')
+ def obj = android.sourceSets.findByName(variantName)
+ if (obj == null) {
+ return
+ }
+ obj.manifest.srcFile project.projectDir.absolutePath + "/build/microModule/merge-manifest/${variantName}/AndroidManifest.xml"
+ }
+
+ def addMicroModuleSourceSet(MicroModule microModule) {
+ addVariantSourceSet(microModule, 'main')
+
+ productFlavorInfo.buildTypes.each {
+ addVariantSourceSet(microModule, it)
+ }
+
+ if (!productFlavorInfo.singleDimension) {
+ productFlavorInfo.productFlavors.each {
+ addVariantSourceSet(microModule, it)
+ }
+ }
+
+ productFlavorInfo.combinedProductFlavors.each {
+ addVariantSourceSet(microModule, it)
+ def flavorName = it
+ productFlavorInfo.buildTypes.each {
+ addVariantSourceSet(microModule, flavorName + Utils.upperCase(it))
+ }
+ }
+
+ def testTypes = ['androidTest', 'test']
+ testTypes.each {
+ def testType = it
+ addVariantSourceSet(microModule, testType)
+
+ if (testType == 'test') {
+ productFlavorInfo.buildTypes.each {
+ addVariantSourceSet(microModule, testType + Utils.upperCase(it))
+ }
+ } else {
+ addVariantSourceSet(microModule, testType + 'Debug')
+ }
+
+ if (!productFlavorInfo.singleDimension) {
+ productFlavorInfo.productFlavors.each {
+ addVariantSourceSet(microModule, testType + Utils.upperCase(it))
+ }
+ }
+
+ productFlavorInfo.combinedProductFlavors.each {
+ def productFlavorName = testType + Utils.upperCase(it)
+ addVariantSourceSet(microModule, productFlavorName)
+
+ if (testType == 'test') {
+ productFlavorInfo.buildTypes.each {
+ addVariantSourceSet(microModule, productFlavorName + Utils.upperCase(it))
+ }
+ } else {
+ addVariantSourceSet(microModule, productFlavorName + 'Debug')
+ }
+ }
+ }
+ }
+
+ def clearOriginSourceSet() {
+ clearModuleSourceSet('main')
+
+ // buildTypes
+ productFlavorInfo.buildTypes.each {
+ clearModuleSourceSet(it)
+ }
+
+ if (!productFlavorInfo.singleDimension) {
+ productFlavorInfo.productFlavors.each {
+ clearModuleSourceSet(it)
+ }
+ }
+
+ productFlavorInfo.combinedProductFlavors.each {
+ clearModuleSourceSet(it)
+ def flavorName = it
+ productFlavorInfo.buildTypes.each {
+ clearModuleSourceSet(flavorName + Utils.upperCase(it))
+ }
+ }
+
+ def testTypes = ['androidTest', 'test']
+ testTypes.each {
+ def testType = it
+ clearModuleSourceSet(testType)
+
+ if (testType == 'test') {
+ productFlavorInfo.buildTypes.each {
+ clearModuleSourceSet(testType + Utils.upperCase(it))
+ }
+ } else {
+ clearModuleSourceSet(testType + 'Debug')
+ }
+
+ if (!productFlavorInfo.singleDimension) {
+ productFlavorInfo.productFlavors.each {
+ clearModuleSourceSet(testType + Utils.upperCase(it))
+ }
+ }
+
+ productFlavorInfo.combinedProductFlavors.each {
+ def productFlavorName = testType + Utils.upperCase(it)
+ clearModuleSourceSet(productFlavorName)
+
+ if (testType == 'test') {
+ productFlavorInfo.buildTypes.each {
+ clearModuleSourceSet(productFlavorName + Utils.upperCase(it))
+ }
+ } else {
+ clearModuleSourceSet(productFlavorName + 'Debug')
+ }
+ }
+ }
+ }
+
+ def isMainSourceSetEmpty() {
+ BaseExtension android = project.extensions.getByName('android')
+ def obj = android.sourceSets.findByName('main')
+ if (obj == null) {
+ return true
+ }
+ return obj.java.srcDirs.size() == 0;
+ }
+
+ def setMainSourceSetManifest() {
+ BaseExtension android = project.extensions.getByName('android')
+ def obj = android.sourceSets.findByName('main')
+ if (obj == null) {
+ obj = android.sourceSets.create('main')
+ }
+ File mainManifestFile = new File(microModuleInfo.mainMicroModule.microModuleDir, '/src/main/AndroidManifest.xml')
+ obj.manifest.srcFile mainManifestFile
+ }
+
+ def addVariantSourceSet(MicroModule microModule, def type) {
+ def absolutePath = microModule.microModuleDir.absolutePath
+ BaseExtension android = project.extensions.getByName('android')
+ def obj = android.sourceSets.findByName(type)
+ if (obj == null) {
+ obj = android.sourceSets.create(type)
+ }
+
+ obj.java.srcDir(absolutePath + "/src/${type}/java")
+ obj.java.srcDir(absolutePath + "/src/${type}/kotlin")
+ obj.res.srcDir(absolutePath + "/src/${type}/res")
+ obj.jni.srcDir(absolutePath + "/src/${type}/jni")
+ obj.jniLibs.srcDir(absolutePath + "/src/${type}/jniLibs")
+ obj.aidl.srcDir(absolutePath + "/src/${type}/aidl")
+ obj.assets.srcDir(absolutePath + "/src/${type}/assets")
+ obj.shaders.srcDir(absolutePath + "/src/${type}/shaders")
+ obj.resources.srcDir(absolutePath + "/src/${type}/resources")
+ obj.renderscript.srcDir(absolutePath + "/src/${type}/rs")
+ }
+
+ def clearModuleSourceSet(def type) {
+ def srcDirs = []
+ BaseExtension android = project.extensions.getByName('android')
+ def obj = android.sourceSets.findByName(type)
+ if (obj == null) {
+ return
+ }
+ obj.java.srcDirs = srcDirs
+ obj.res.srcDirs = srcDirs
+ obj.jni.srcDirs = srcDirs
+ obj.jniLibs.srcDirs = srcDirs
+ obj.aidl.srcDirs = srcDirs
+ obj.assets.srcDirs = srcDirs
+ obj.shaders.srcDirs = srcDirs
+ obj.resources.srcDirs = srcDirs
+ obj.renderscript.srcDirs = srcDirs
+ }
+
+ void applyMicroModuleScript(MicroModule microModule) {
+ def microModuleBuild = new File(microModule.microModuleDir, 'build.gradle')
+ if (microModuleBuild.exists()) {
+ MicroModule tempMicroModule = currentMicroModule
+ currentMicroModule = microModule
+ project.apply from: microModuleBuild.absolutePath
+ currentMicroModule = tempMicroModule
+ }
+ }
+
+ def checkMicroModuleBoundary(String taskPrefix, String buildType, String flavorName, List sourceFolders) {
+ CodeChecker codeChecker
+
+ def buildTypeFirstUp = Utils.upperCase(buildType)
+ def productFlavorFirstUp = flavorName != null ? Utils.upperCase(flavorName) : ""
+
+ def mergeResourcesTaskName = taskPrefix + productFlavorFirstUp + buildTypeFirstUp + 'Resources'
+ def packageResourcesTask = project.tasks.findByName(mergeResourcesTaskName)
+ if (packageResourcesTask != null) {
+ codeChecker = new CodeChecker(project, microModuleInfo, productFlavorInfo, buildType, flavorName)
+ packageResourcesTask.doLast {
+ codeChecker.checkResources(mergeResourcesTaskName, sourceFolders)
+ }
+ }
+
+ def compileJavaTaskName = "compile${productFlavorFirstUp}${buildTypeFirstUp}JavaWithJavac"
+ def compileJavaTask = project.tasks.findByName(compileJavaTaskName)
+ if (compileJavaTask != null) {
+ compileJavaTask.doLast {
+ if (codeChecker == null) {
+ codeChecker = new CodeChecker(project, microModuleInfo, productFlavorInfo, buildType, flavorName)
+ }
+ codeChecker.checkClasses(mergeResourcesTaskName, sourceFolders)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/Digraph.java b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/Digraph.java
new file mode 100644
index 0000000..5230853
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/Digraph.java
@@ -0,0 +1,180 @@
+package com.eastwood.tools.plugins.core;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Stack;
+
+/**
+ * An example class for directed graphs. The vertex type can be specified.
+ * There are no edge costs/weights.
+ *
+ * Written for CS211, Nov 2006.
+ *
+ * @author Paul Chew
+ */
+public class Digraph {
+
+ /**
+ * The implementation here is basically an adjacency list, but instead
+ * of an array of lists, a Map is used to map each vertex to its list of
+ * adjacent vertices.
+ */
+ private Map> neighbors = new HashMap>();
+
+ /**
+ * String representation of dependencyGraph.
+ */
+ public String toString () {
+ StringBuffer s = new StringBuffer();
+ for (V v: neighbors.keySet()) s.append("\n " + v + " -> " + neighbors.get(v));
+ return s.toString();
+ }
+
+ /**
+ * Add a vertex to the dependencyGraph. Nothing happens if vertex is already in dependencyGraph.
+ */
+ public void add (V vertex) {
+ if (neighbors.containsKey(vertex)) return;
+ neighbors.put(vertex, new ArrayList());
+ }
+
+ /**
+ * True iff dependencyGraph contains vertex.
+ */
+ public boolean contains (V vertex) {
+ return neighbors.containsKey(vertex);
+ }
+
+ /**
+ * Add an edge to the dependencyGraph; if either vertex does not exist, it's added.
+ * This implementation allows the creation of multi-edges and self-loops.
+ */
+ public void add (V from, V to) {
+ this.add(from); this.add(to);
+ neighbors.get(from).add(to);
+ }
+
+ /**
+ * Remove an edge from the dependencyGraph. Nothing happens if no such edge.
+ * @throws IllegalArgumentException if either vertex doesn't exist.
+ */
+ public void remove (V from, V to) {
+ if (!(this.contains(from) && this.contains(to)))
+ throw new IllegalArgumentException("Nonexistent vertex");
+ neighbors.get(from).remove(to);
+ }
+
+ /**
+ * Report (as a Map) the out-degree of each vertex.
+ */
+ public Map outDegree () {
+ Map result = new HashMap();
+ for (V v: neighbors.keySet()) result.put(v, neighbors.get(v).size());
+ return result;
+ }
+
+ /**
+ * Report (as a Map) the in-degree of each vertex.
+ */
+ public Map inDegree () {
+ Map result = new HashMap();
+ for (V v: neighbors.keySet()) result.put(v, 0); // All in-degrees are 0
+ for (V from: neighbors.keySet()) {
+ for (V to: neighbors.get(from)) {
+ result.put(to, result.get(to) + 1); // Increment in-degree
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Report (as a List) the topological sort of the vertices; null for no such sort.
+ */
+ public List topSort () {
+ Map degree = inDegree();
+ // Determine all vertices with zero in-degree
+ Stack zeroVerts = new Stack(); // Stack as good as any here
+ for (V v: degree.keySet()) {
+ if (degree.get(v) == 0) zeroVerts.push(v);
+ }
+ // Determine the topological order
+ List result = new ArrayList();
+ while (!zeroVerts.isEmpty()) {
+ V v = zeroVerts.pop(); // Choose a vertex with zero in-degree
+ result.add(v); // Vertex v is next in topol order
+ // "Remove" vertex v by updating its neighbors
+ for (V neighbor: neighbors.get(v)) {
+ degree.put(neighbor, degree.get(neighbor) - 1);
+ // Remember any vertices that now have zero in-degree
+ if (degree.get(neighbor) == 0) zeroVerts.push(neighbor);
+ }
+ }
+ // Check that we have used the entire dependencyGraph (if not, there was a cycle)
+ if (result.size() != neighbors.size()) return null;
+ return result;
+ }
+
+ /**
+ * True iff dependencyGraph is a dag (directed acyclic dependencyGraph).
+ */
+ public boolean isDag () {
+ return topSort() != null;
+ }
+
+ /**
+ * Report (as a Map) the bfs distance to each vertex from the start vertex.
+ * The distance is an Integer; the value null is used to represent infinity
+ * (implying that the corresponding node cannot be reached).
+ */
+ public Map bfsDistance (V start) {
+ Map distance = new HashMap();
+ // Initially, all distance are infinity, except start node
+ for (V v: neighbors.keySet()) distance.put(v, null);
+ distance.put(start, 0);
+ // Process nodes in queue order
+ Queue queue = new LinkedList();
+ queue.offer(start); // Place start node in queue
+ while (!queue.isEmpty()) {
+ V v = queue.remove();
+ int vDist = distance.get(v);
+ // Update neighbors
+ for (V neighbor: neighbors.get(v)) {
+ if (distance.get(neighbor) != null) continue; // Ignore if already done
+ distance.put(neighbor, vDist + 1);
+ queue.offer(neighbor);
+ }
+ }
+ return distance;
+ }
+
+// /**
+// * Main program (for testing).
+// */
+// public static void main (String[] args) {
+// // Create a Graph with Integer nodes
+// Digraph graph = new Digraph();
+// graph.add(0, 1); graph.add(0, 2); graph.add(0, 3);
+// graph.add(1, 2); graph.add(1, 3); graph.add(2, 3);
+// graph.add(2, 4); graph.add(4, 5); graph.add(5, 6); // Tetrahedron with tail
+// System.out.println("The current dependencyGraph: " + graph);
+// System.out.println("In-degrees: " + graph.inDegree());
+// System.out.println("Out-degrees: " + graph.outDegree());
+// System.out.println("A topological sort of the vertices: " + graph.topSort());
+// System.out.println("The dependencyGraph " + (graph.isDag()?"is":"is not") + " a dag");
+// System.out.println("BFS distances starting from " + 0 + ": " + graph.bfsDistance(0));
+// System.out.println("BFS distances starting from " + 1 + ": " + graph.bfsDistance(1));
+// System.out.println("BFS distances starting from " + 2 + ": " + graph.bfsDistance(2));
+// graph.add(4, 1); // Create a cycle
+// System.out.println("Cycle created");
+// System.out.println("The current dependencyGraph: " + graph);
+// System.out.println("In-degrees: " + graph.inDegree());
+// System.out.println("Out-degrees: " + graph.outDegree());
+// System.out.println("A topological sort of the vertices: " + graph.topSort());
+// System.out.println("The dependencyGraph " + (graph.isDag()?"is":"is not") + " a dag");
+// System.out.println("BFS distances starting from " + 2 + ": " + graph.bfsDistance(2));
+// }
+}
\ No newline at end of file
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/MicroModule.groovy b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/MicroModule.groovy
new file mode 100644
index 0000000..508a5d2
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/MicroModule.groovy
@@ -0,0 +1,11 @@
+package com.eastwood.tools.plugins.core
+
+
+class MicroModule {
+
+ String name
+ File microModuleDir
+
+ boolean appliedScript
+
+}
\ No newline at end of file
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/MicroModuleInfo.groovy b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/MicroModuleInfo.groovy
new file mode 100644
index 0000000..50b0e10
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/MicroModuleInfo.groovy
@@ -0,0 +1,77 @@
+package com.eastwood.tools.plugins.core
+
+import org.gradle.api.GradleException
+import org.gradle.api.Project
+
+class MicroModuleInfo {
+
+ Project project
+ MicroModule mainMicroModule
+ Map includeMicroModules
+ Map exportMicroModules
+
+ Digraph dependencyGraph
+
+ MicroModuleInfo(Project project) {
+ this.project = project
+ this.includeMicroModules = new HashMap<>()
+ this.exportMicroModules = new HashMap<>()
+ dependencyGraph = new Digraph()
+
+ MicroModule microModule = Utils.buildMicroModule(project, ':main')
+ if (microModule != null) {
+ setMainMicroModule(microModule)
+ }
+ }
+
+ void setMainMicroModule(MicroModule microModule) {
+ if (microModule == null) {
+ throw new GradleException("main MicroModule cannot be null.")
+ }
+ this.mainMicroModule = microModule
+ addIncludeMicroModule(microModule)
+ }
+
+ void addIncludeMicroModule(MicroModule microModule) {
+ includeMicroModules.put(microModule.name, microModule)
+ }
+
+ void addExportMicroModule(String name) {
+ MicroModule microModule = Utils.buildMicroModule(project, name)
+ if (microModule == null) {
+ throw new GradleException("MicroModule with path '${name}' could not be found in ${project.getDisplayName()}.")
+ }
+ exportMicroModules.put(name, null)
+ }
+
+ MicroModule getMicroModule(String name) {
+ return includeMicroModules.get(name)
+ }
+
+ void setMicroModuleDependency(String target, String dependency) {
+ MicroModule dependencyMicroModule = getMicroModule(dependency)
+ if(dependencyMicroModule == null) {
+ if(Utils.buildMicroModule(project, dependency) != null) {
+ throw new GradleException("MicroModule '${target}' dependency MicroModle '${dependency}', but its not included.")
+ } else {
+ throw new GradleException("MicroModule with path '${path}' could not be found in ${project.getDisplayName()}.")
+ }
+ }
+
+ dependencyGraph.add(target, dependency)
+ if(!dependencyGraph.isDag()) {
+ throw new GradleException("Circular dependency between MicroModule '${target}' and '${dependency}'.")
+ }
+ }
+
+ boolean hasDependency(String target, String dependency) {
+ Map bfsDistance = dependencyGraph.bfsDistance(target)
+ for(String key: bfsDistance.keySet()) {
+ if(key == dependency) {
+ return bfsDistance.get(key) != null
+ }
+ }
+ return false
+ }
+
+}
\ No newline at end of file
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/ProductFlavorInfo.groovy b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/ProductFlavorInfo.groovy
new file mode 100644
index 0000000..83fd825
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/ProductFlavorInfo.groovy
@@ -0,0 +1,102 @@
+package com.eastwood.tools.plugins.core
+
+import com.android.build.gradle.BaseExtension
+import org.gradle.api.Project
+
+class ProductFlavorInfo {
+
+ List flavorDimensions
+ List productFlavors
+ List buildTypes
+ List combinedProductFlavors
+ Map> combinedProductFlavorsMap
+ boolean singleDimension
+
+ private List> flavorGroups
+
+ ProductFlavorInfo(Project project) {
+ BaseExtension extension = (BaseExtension) project.extensions.getByName("android")
+ buildTypes = new ArrayList<>()
+ if(extension.buildTypes != null) {
+ extension.buildTypes.each {
+ buildTypes.add(it.name)
+ }
+ }
+
+ flavorDimensions = extension.flavorDimensionList
+ if (flavorDimensions == null) {
+ flavorDimensions = new ArrayList<>()
+ }
+
+ productFlavors = new ArrayList<>()
+ flavorGroups = new ArrayList<>()
+ for (int i = 0; i < flavorDimensions.size(); i++) {
+ flavorGroups.add(new ArrayList<>())
+ }
+ extension.productFlavors.each {
+ productFlavors.add(it.name)
+ def position = flavorDimensions.indexOf(it.dimension)
+ flavorGroups.get(position).add(it.name)
+ }
+ List> flavorGroupTemp = new ArrayList<>()
+ flavorGroups.each {
+ if (it.size() != 0) {
+ flavorGroupTemp.add(it)
+ }
+ }
+ flavorGroups = flavorGroupTemp
+
+ calculateFlavorCombination()
+ if (combinedProductFlavors.size() == extension.productFlavors.size()) {
+ singleDimension = true
+ }
+ }
+
+ private void calculateFlavorCombination() {
+ combinedProductFlavors = new ArrayList<>()
+ combinedProductFlavorsMap = new HashMap<>()
+
+ if (flavorGroups.size() == 0) {
+ return
+ }
+
+ List combination = new ArrayList()
+ int n = flavorGroups.size();
+ for (int i = 0; i < n; i++) {
+ combination.add(0);
+ }
+ int i = 0;
+ boolean isContinue = true;
+ while (isContinue) {
+ List items = new ArrayList<>()
+ String item = flavorGroups.get(0).get(combination.get(0))
+ items.add(item)
+ String combined = item
+ for (int j = 1; j < n; j++) {
+ item = flavorGroups.get(j).get(combination.get(j))
+ combined += Utils.upperCase(item)
+ items.add(item)
+ }
+ combinedProductFlavors.add(combined)
+ combinedProductFlavorsMap.put(combined, items)
+ i++;
+ combination.set(n - 1, i);
+ for (int j = n - 1; j >= 0; j--) {
+ if (combination.get(j) >= flavorGroups.get(j).size()) {
+ combination.set(j, 0);
+ i = 0;
+ if (j - 1 >= 0) {
+ combination.set(j - 1, combination.get(j - 1) + 1);
+ }
+ }
+ }
+ isContinue = false;
+ for (Integer integer : combination) {
+ if (integer != 0) {
+ isContinue = true;
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/Utils.groovy b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/Utils.groovy
new file mode 100644
index 0000000..afb8217
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/Utils.groovy
@@ -0,0 +1,57 @@
+package com.eastwood.tools.plugins.core
+
+import org.gradle.api.Project
+import org.w3c.dom.Element
+
+import javax.xml.parsers.DocumentBuilderFactory
+
+class Utils {
+
+ static String upperCase(String str) {
+ char[] ch = str.toCharArray()
+ if (ch[0] >= 'a' && ch[0] <= 'z') {
+ ch[0] -= 32
+ }
+ return String.valueOf(ch)
+ }
+
+ static String getAndroidManifestPackageName(File androidManifest) {
+ def builderFactory = DocumentBuilderFactory.newInstance()
+ builderFactory.setNamespaceAware(true)
+ Element manifestXml = builderFactory.newDocumentBuilder().parse(androidManifest).documentElement
+ return manifestXml.getAttribute("package")
+ }
+
+ static MicroModule buildMicroModule(Project project, String microModulePath) {
+ String[] pathElements = removeTrailingColon(microModulePath).split(":")
+ int pathElementsLen = pathElements.size()
+ File parentMicroModuleDir = project.projectDir
+ for (int j = 0; j < pathElementsLen; j++) {
+ parentMicroModuleDir = new File(parentMicroModuleDir, pathElements[j])
+ }
+ File microModuleDir = parentMicroModuleDir.canonicalFile
+ String microModuleName = microModuleDir.absolutePath.replace(project.projectDir.absolutePath, "")
+ if (File.separator == "\\") {
+ microModuleName = microModuleName.replaceAll("\\\\", ":")
+ } else {
+ microModuleName = microModuleName.replaceAll("/", ":")
+ }
+ // in windows and mac system, the file name is not case sensitive, micro-module name may not match file name but run correctly
+ // in linux system(as ci server), this case is wrong
+ // add this condition to find this error in windows
+ if (!microModuleDir.exists() || microModuleName != microModulePath) {
+ System.err.println("microModuleDir:" + microModuleDir.getAbsolutePath() + " not exit, or module name and file name case sensitivity wrong")
+ return null
+ }
+ MicroModule microModule = new MicroModule()
+ microModule.name = microModuleName
+ microModule.microModuleDir = microModuleDir
+ return microModule
+ }
+
+ private static String removeTrailingColon(String microModulePath) {
+ return microModulePath.startsWith(":") ? microModulePath.substring(1) : microModulePath
+ }
+
+
+}
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/check/CheckManifest.groovy b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/check/CheckManifest.groovy
new file mode 100644
index 0000000..baf523c
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/check/CheckManifest.groovy
@@ -0,0 +1,132 @@
+package com.eastwood.tools.plugins.core.check
+
+import org.w3c.dom.Document
+import org.w3c.dom.Element
+import org.w3c.dom.NodeList
+
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.transform.OutputKeys
+import javax.xml.transform.Transformer
+import javax.xml.transform.TransformerFactory
+import javax.xml.transform.dom.DOMSource
+import javax.xml.transform.stream.StreamResult
+
+class CheckManifest {
+
+ Document document
+ Element rootElement
+
+ String packageName
+ Map lastModifiedResourcesMap
+ Map lastModifiedClassesMap
+
+ void load(File sourceFile) {
+ if (!sourceFile.exists()) return
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance()
+ document = builderFactory.newDocumentBuilder().parse(sourceFile)
+ rootElement = document.documentElement
+ packageName = rootElement.getAttribute("package")
+ }
+
+ void setResourcesLastModified(long lastModified) {
+ resourcesLastModified = lastModified
+ }
+
+ Map getResourcesMap() {
+ if (lastModifiedResourcesMap != null) return lastModifiedResourcesMap
+
+ lastModifiedResourcesMap = new HashMap<>()
+ if (rootElement == null) return lastModifiedResourcesMap
+
+ NodeList resourcesNodeList = rootElement.getElementsByTagName("resources")
+ if (resourcesNodeList.length == 0) {
+ return lastModifiedResourcesMap
+ }
+ Element resourcesElement = (Element) resourcesNodeList.item(0)
+ NodeList fileNodeList = resourcesElement.getElementsByTagName("file")
+ for (int i = 0; i < fileNodeList.getLength(); i++) {
+ Element fileElement = (Element) fileNodeList.item(i)
+ MicroModuleFile microModuleFile = new MicroModuleFile()
+ microModuleFile.name = fileElement.getAttribute("name")
+ microModuleFile.microModuleName = fileElement.getAttribute("microModuleName")
+ microModuleFile.path = fileElement.getAttribute("path")
+ microModuleFile.lastModified = fileElement.getAttribute("lastModified").toLong()
+ lastModifiedResourcesMap.put(microModuleFile.path, microModuleFile)
+ }
+ return lastModifiedResourcesMap
+ }
+
+ Map getClassesMap() {
+ if (lastModifiedClassesMap != null) return lastModifiedClassesMap
+
+ lastModifiedClassesMap = new HashMap<>()
+ if (rootElement == null) return lastModifiedClassesMap
+
+ NodeList classesNodeList = rootElement.getElementsByTagName("classes")
+ if (classesNodeList.length == 0) {
+ return lastModifiedClassesMap
+ }
+ Element classesElement = (Element) classesNodeList.item(0)
+ NodeList fileNodeList = classesElement.getElementsByTagName("file")
+ for (int i = 0; i < fileNodeList.getLength(); i++) {
+ Element fileElement = (Element) fileNodeList.item(i)
+ MicroModuleFile microModuleFile = new MicroModuleFile()
+ microModuleFile.name = fileElement.getAttribute("name")
+ microModuleFile.microModuleName = fileElement.getAttribute("microModuleName")
+ microModuleFile.path = fileElement.getAttribute("path")
+ microModuleFile.lastModified = fileElement.getAttribute("lastModified").toLong()
+ lastModifiedClassesMap.put(microModuleFile.path, microModuleFile)
+ }
+ return lastModifiedClassesMap
+ }
+
+ void save(File destFile) {
+ DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance()
+ Document documentTemp = builderFactory.newDocumentBuilder().newDocument()
+ Element microModuleXmlTemp = documentTemp.createElement("micro-module")
+ microModuleXmlTemp.setAttribute("package", packageName)
+ // resources
+ Element resourcesElement = documentTemp.createElement("resources")
+ microModuleXmlTemp.appendChild(resourcesElement)
+ if (lastModifiedResourcesMap != null) {
+ lastModifiedResourcesMap.each {
+ MicroModuleFile resourceFile = it.value
+ Element fileElement = documentTemp.createElement("file")
+ fileElement.setAttribute("name", resourceFile.name)
+ fileElement.setAttribute("path", resourceFile.path)
+ fileElement.setAttribute("lastModified", resourceFile.lastModified.toString())
+ fileElement.setAttribute("microModuleName", resourceFile.microModuleName)
+ resourcesElement.appendChild(fileElement)
+ }
+ }
+
+ // classes
+ if (lastModifiedClassesMap != null) {
+ Element classesElement = documentTemp.createElement("classes")
+ microModuleXmlTemp.appendChild(classesElement)
+ lastModifiedClassesMap.each {
+ MicroModuleFile resourceFile = it.value
+ Element fileElement = documentTemp.createElement("file")
+ fileElement.setAttribute("name", resourceFile.name)
+ fileElement.setAttribute("path", resourceFile.path)
+ fileElement.setAttribute("lastModified", resourceFile.lastModified.toString())
+ fileElement.setAttribute("microModuleName", resourceFile.microModuleName)
+ classesElement.appendChild(fileElement)
+ }
+ microModuleXmlTemp.appendChild(classesElement)
+ } else if (rootElement != null) {
+ NodeList classesNodeList = rootElement.getElementsByTagName("classes")
+ if (classesNodeList.length == 1) {
+ Element classesElement = (Element) classesNodeList.item(0)
+ microModuleXmlTemp.appendChild(documentTemp.importNode(classesElement, true))
+ }
+ }
+ // save
+ Transformer transformer = TransformerFactory.newInstance().newTransformer()
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes")
+ transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, "yes")
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")
+ transformer.transform(new DOMSource(microModuleXmlTemp), new StreamResult(destFile))
+ }
+
+}
\ No newline at end of file
diff --git a/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/check/CodeChecker.groovy b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/check/CodeChecker.groovy
new file mode 100644
index 0000000..b07c65e
--- /dev/null
+++ b/micro-module/src/main/groovy/com/eastwood/tools/plugins/core/check/CodeChecker.groovy
@@ -0,0 +1,367 @@
+package com.eastwood.tools.plugins.core.check
+
+import com.eastwood.tools.plugins.core.MicroModule
+import com.eastwood.tools.plugins.core.MicroModuleInfo
+import com.eastwood.tools.plugins.core.ProductFlavorInfo
+import com.eastwood.tools.plugins.core.Utils
+import org.gradle.api.GradleScriptException
+import org.gradle.api.Project
+import org.w3c.dom.Element
+import org.w3c.dom.NodeList
+
+class CodeChecker {
+
+ Project project
+ MicroModuleInfo microModuleInfo
+ ProductFlavorInfo productFlavorInfo
+
+ String buildType
+ String productFlavor
+
+ CheckManifest checkManifest
+ ResourceMerged resourceMerged
+
+ String errorMessage = ""
+ String lineSeparator = System.getProperty("line.separator")
+
+ Map> microModulePackageNameMap
+
+ CodeChecker(Project project, MicroModuleInfo microModuleInfo, ProductFlavorInfo productFlavorInfo, String buildType, String productFlavor) {
+ this.project = project
+ this.microModuleInfo = microModuleInfo
+ this.productFlavorInfo = productFlavorInfo
+ this.buildType = buildType
+ this.productFlavor = productFlavor
+ this.checkManifest = getModuleCheckManifest()
+ }
+
+ void checkResources(String mergeResourcesTaskName, List sourceFolders) {
+ resourceMerged = new ResourceMerged()
+ if (!resourceMerged.load(project.projectDir, mergeResourcesTaskName)) {
+ return
+ }
+
+ List resourceNodeLists = resourceMerged.getResourcesNodeList(sourceFolders)
+ List modifiedResourcesList = getModifiedResourcesList(resourceNodeLists)
+ if (modifiedResourcesList.size() == 0) {
+ return
+ }
+ handleModifiedResources(modifiedResourcesList)
+ if (errorMessage != "") {
+ throw new GradleScriptException(errorMessage, null)
+ }
+
+ def manifest = new File(microModuleInfo.mainMicroModule.microModuleDir, "src/main/AndroidManifest.xml")
+ String packageName = Utils.getAndroidManifestPackageName(manifest)
+ checkManifest.packageName = packageName
+ saveModuleCheckManifest()
+ }
+
+ List getModifiedResourcesList(List resourcesNodeList) {
+ Map lastModifiedResourcesMap = checkManifest.getResourcesMap()
+ List modifiedResourcesList = new ArrayList<>()
+ if (resourcesNodeList == null || resourcesNodeList.length == 0) return modifiedResourcesList
+
+ resourcesNodeList.each {
+ for (int i = 0; i < it.getLength(); i++) {
+ Element resourcesElement = (Element) it.item(i)
+ NodeList fileNodeList = resourcesElement.getElementsByTagName("file")
+ for (int j = 0; j < fileNodeList.getLength(); j++) {
+ Element fileElement = (Element) fileNodeList.item(j)
+ String filePath = fileElement.getAttribute("path")
+ if (filePath != null && filePath.endsWith(".xml")) {
+ File file = project.file(filePath)
+ MicroModuleFile resourceFile = lastModifiedResourcesMap.get(filePath)
+ def currentModified = file.lastModified()
+ if (resourceFile == null || resourceFile.lastModified.longValue() < currentModified) {
+ modifiedResourcesList.add(file)
+
+ if (resourceFile == null) {
+ resourceFile = new MicroModuleFile()
+ resourceFile.name = file.name
+ resourceFile.path = filePath
+ resourceFile.microModuleName = getMicroModuleName(filePath)
+ lastModifiedResourcesMap.put(filePath, resourceFile)
+ }
+ resourceFile.lastModified = currentModified
+ }
+ }
+ }
+ }
+ }
+
+ return modifiedResourcesList
+ }
+
+ void handleModifiedResources(List modifiedResourcesList) {
+ Map resourcesMap = resourceMerged.getResourcesMap()
+ def resourcesPattern = /@(dimen|drawable|color|string|style|id|mipmap|layout)\/[A-Za-z0-9_]+/
+ modifiedResourcesList.each {
+ String text = it.text
+ List textLines = text.readLines()
+ def matcher = (text =~ resourcesPattern)
+ def absolutePath = it.absolutePath
+ def microModuleName = getMicroModuleName(absolutePath)
+ while (matcher.find()) {
+ def find = matcher.group()
+ def name = find.substring(find.indexOf("/") + 1)
+ def from = resourcesMap.get(name)
+ if (from != null && microModuleName != from && !microModuleInfo.hasDependency(microModuleName, from)) {
+ List lines = textLines.findIndexValues { it.contains(find) }
+ lines.each {
+ def lineIndex = it.intValue()
+ def lineContext = textLines.get(lineIndex).trim()
+ if (lineContext.startsWith("