diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09b993d --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/README.md b/README.md new file mode 100644 index 0000000..77ed738 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +相关文章链接:[MainDex 优化记](https://mp.weixin.qq.com/s?__biz=MzUyMDg2ODgwOQ==&mid=2247483679&idx=1&sn=4520ae38f703ee3b2303c02b47a80639&chksm=f9e287b9ce950eafa38deb9d8bf898a3abcaa0234adcc08ad017d8fd89bfc612a8d6e6a1cefe&token=1165484979&lang=zh_CN#rd) + +### 原理 + +此插件只作用于打包过程,编码过程无感知、无影响。 + +混淆之后将会产生一个包含应用中所有 class 的 jar,通过 ASM 扫描所有的类、类的方法、类的字段等一切可能会出现注解的地方,扫描到开发者配置的注解则将其删除;而如果当前类是注解类且是开发者所配置的类的话,该注解类将会被删除。 + +### 配置 + +thinAnnotation 默认删除**所有**的 SOURCE 时期注解,因为使用 SOURCE 时期注解的地方实际上会在 .java -> .class 过程中被擦除,所以所有的 SOURCE 注解类实际上都是无用类,例如 `android/support/design/widget/TabLayout$Mode` 类;thinAnnotation 默认删除了 `butterknife/` 包、 `android/support/annotation/` 包、`androidx/annotation/` 包的所有注解,原因是这些 CLASS 时期的类在运行时是无用的(除非特殊要求,CLASS 文件实际上也是可以全部删除的);开发者只需要配置想要删除的 CLASS 和 RUNTIME 时期的注解类,配置方式如下—— + +在 project/build.gradle 中: + +``` +buildscript { + repositories { + maven { url 'https://jitpack.io' } + } + dependencies { + classpath 'com.github.jokermonn:thinAnnotation:0.0.2' + } +} +``` + +在 app/build.gradle 中: + +``` +apply plugin: 'thinAnnotation' + +thinAnnotation { + // 是否开启插件 + enable true + // 目标注解类的路径 + shrinkClass = ['com/joker/maindexkeep/annotations/RuntimeAnn', 'com/joker/maindexkeep/annotations/Type'] + // 目标包的路径 + shrinkPackage = ['com/joker/maindexkeep/shrink'] +} +``` + +日志在:`app/build/outputs/thinAnnotation` 路径下。 + +## 示例 + +以 butterknife 为例: + +使用前:butterknife 包注解类存在,`@BindView` 等注解存在于 .class 文件中 + +![](http://imglf6.nosdn0.126.net/img/UnlRcDgySWkxbnZUbjBCSXdnUFoza3RCR0R1TGY0M0x6dkxwQksxaFdJS3FJUHhmT2FCK21RPT0.png?imageView&thumbnail=2490y1632&type=png&quality=96&stripmeta=0) + +使用后:butterknife 包注解类全部删除,所有使用该注解的地方也都会被清除注解 + +![](http://imglf5.nosdn0.126.net/img/UnlRcDgySWkxbnZUbjBCSXdnUFozanN2dzFqaU4xREZZalNtc2JrSGw0WXNQWEQ5NlpQNUlnPT0.png?imageView&thumbnail=2238y1484&type=png&quality=96&stripmeta=0) \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..668f057 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,79 @@ +apply plugin: 'com.android.application' +apply plugin: 'com.getkeepsafe.dexcount' +apply plugin: 'thinAnnotation' + +android { + compileSdkVersion 28 + + defaultConfig { + applicationId "com.joker.maindexkeep" + minSdkVersion 14 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + + multiDexEnabled true + multiDexKeepProguard file('./maindex-rules.pro') + multiDexKeepFile file('./maindex-keep.txt') + } + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + dexOptions { + // keepRuntimeAnnotatedClasses false + } +} + +thinAnnotation { + enable = true + shrinkClass = ['com/joker/maindexkeep/annotations/RuntimeAnn'] + shrinkPackage = ['com/joker/maindexkeep/shrink'] +} + +dependencies { + implementation fileTree(dir: 'libs') + implementation 'com.squareup.wire:wire-runtime:2.2.0' + implementation 'com.google.code.gson:gson:2.8.5' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'com.android.support:multidex:1.0.3' + implementation 'com.google.guava:guava:27.0.1-android' + implementation "io.reactivex.rxjava2:rxjava:2.2.6" + implementation 'com.squareup.retrofit2:retrofit:2.4.0' + implementation 'com.squareup.okhttp3:okhttp:3.12.1' + implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0' + implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.4.0' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' + implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' + implementation 'com.android.support:exifinterface:28.0.0' + implementation 'com.github.bumptech.glide:glide:4.8.0' + implementation 'com.miguelcatalan:materialsearchview:1.0.1' + implementation 'com.sdsmdg.harjot:vectormaster:1.0.5' + implementation 'com.amap.api:3dmap:5.7.0' + implementation 'com.amap.api:location:3.5.0' + implementation 'com.amap.api:search:5.7.0' + implementation 'com.xyz.step:step:1.0.4' + implementation 'com.rengwuxian.materialedittext:library:2.1.4' + implementation 'info.hoang8f:fbutton:1.0.5' + implementation 'me.drakeet.materialdialog:library:1.3.1' + implementation 'com.daimajia.swipelayout:library:1.2.0@aar' + implementation 'com.squareup.leakcanary:leakcanary-android:1.6.1' + implementation 'com.android.support:cardview-v7:28.0.0' + implementation 'com.android.support:support-v4:28.0.0' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:design:28.0.0' + implementation 'com.facebook.fresco:fresco:1.11.0' + implementation 'com.facebook.fresco:animated-gif:1.11.0' + implementation 'com.facebook.fresco:animated-webp:1.11.0' + implementation 'com.facebook.fresco:webpsupport:1.11.0' + implementation 'com.facebook.fresco:webpsupport:1.11.0' + implementation 'com.android.support:preference-v7:28.0.0' + implementation 'com.jakewharton:butterknife:8.4.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' +} diff --git a/app/maindex-keep.txt b/app/maindex-keep.txt new file mode 100644 index 0000000..e69de29 diff --git a/app/maindex-rules.pro b/app/maindex-rules.pro new file mode 100644 index 0000000..02f37f6 --- /dev/null +++ b/app/maindex-rules.pro @@ -0,0 +1,3 @@ +#-keep class com.joker.maindexkeep.model.AppReferencerence { +# public void test(); +#} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..ca268a6 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,85 @@ +# 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 +-dontshrink # 为了超过 66536 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-verbose +-dontpreverify +-dontoptimize +-dontwarn +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase +-dontwarn org.codehaus.mojo.animal_sniffer.* +-dontwarn okhttp3.internal.platform.ConscryptPlatform +-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation* +-keepclassmembers,allowshrinking,allowobfuscation interface * { + @retrofit2.http.* ; +} +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement +-dontwarn javax.annotation.** +-dontwarn kotlin.Unit +-dontwarn retrofit2.-KotlinExtensions +-keep class sun.misc.Unsafe { *; } +-dontwarn java.nio.file.* +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement +-dontwarn okio.** +-keep class com.google.common.io.Resources { + public static ; +} +-keep class com.google.common.collect.Lists { + public static ** reverse(**); +} +-keep class com.google.common.base.Charsets { + public static ; +} + +-keep class com.google.common.base.Joiner { + public static com.google.common.base.Joiner on(java.lang.String); + public ** join(...); +} + +-keep class com.google.common.collect.MapMakerInternalMap$ReferenceEntry +-keep class com.google.common.cache.LocalCache$ReferenceEntry +-dontwarn javax.annotation.** +-dontwarn javax.inject.** +-dontwarn sun.misc.Unsafe +-dontwarn java.lang.ClassValue +-dontwarn com.google.j2objc.annotations.Weak +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement +-dontwarn javax.lang.model.element.Modifier + +-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip + +-keep @com.facebook.common.internal.DoNotStrip class * +-keepclassmembers class * { + @com.facebook.common.internal.DoNotStrip *; +} +-keepclassmembers class * { + native ; +} +-dontwarn okio.** +-dontwarn javax.annotation.** +-dontwarn com.android.volley.toolbox.** + +# app +-keep class com.joker.maindexkeep.model.AppReference { + public void test(); +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..46d552d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/joker/maindexkeep/App.java b/app/src/main/java/com/joker/maindexkeep/App.java new file mode 100644 index 0000000..eb23111 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/App.java @@ -0,0 +1,19 @@ +package com.joker.maindexkeep; + +import android.content.Context; +import android.support.multidex.MultiDex; +import android.support.multidex.MultiDexApplication; +import android.util.Log; +import com.joker.maindexkeep.model.AppReference; + +public class App extends MultiDexApplication { + @Override protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + AppReference useless = new AppReference(); + } + + @Override public void onCreate() { + MultiDex.install(this); + super.onCreate(); + } +} diff --git a/app/src/main/java/com/joker/maindexkeep/MainActivity.java b/app/src/main/java/com/joker/maindexkeep/MainActivity.java new file mode 100644 index 0000000..ec1f8a4 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/MainActivity.java @@ -0,0 +1,23 @@ +package com.joker.maindexkeep; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import com.joker.maindexkeep.annotations.RuntimeAnn; + +/** + * 方法/类被运行时注解所修饰,所以当前类将会被打入 maindex + */ +@RuntimeAnn +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } + + @RuntimeAnn + void test() { + + } +} diff --git a/app/src/main/java/com/joker/maindexkeep/MultiProcessService.java b/app/src/main/java/com/joker/maindexkeep/MultiProcessService.java new file mode 100644 index 0000000..7b788cd --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/MultiProcessService.java @@ -0,0 +1,19 @@ +package com.joker.maindexkeep; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +/** + * manifest 中有注释 + */ +public class MultiProcessService extends Service { + public MultiProcessService() { + } + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return the communication channel to the service. + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/app/src/main/java/com/joker/maindexkeep/SecondActivity.java b/app/src/main/java/com/joker/maindexkeep/SecondActivity.java new file mode 100644 index 0000000..3749629 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/SecondActivity.java @@ -0,0 +1,21 @@ +package com.joker.maindexkeep; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.widget.TextView; +import butterknife.BindView; +import com.joker.maindexkeep.annotations.RuntimeAnn; + +/** + * {@link BindView} 是运行时注解,所以当前类将会被打入 maindex + */ +public class SecondActivity extends AppCompatActivity { + + @BindView(R.id.tv) TextView tv; + + @Override @RuntimeAnn + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_second); + } +} diff --git a/app/src/main/java/com/joker/maindexkeep/annotations/AnnotationWrapper.java b/app/src/main/java/com/joker/maindexkeep/annotations/AnnotationWrapper.java new file mode 100644 index 0000000..6e69015 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/annotations/AnnotationWrapper.java @@ -0,0 +1,27 @@ +package com.joker.maindexkeep.annotations; + +import android.support.annotation.StringDef; +import com.joker.maindexkeep.model.AnnotationWrapperReference; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import static com.joker.maindexkeep.annotations.AnnotationWrapper.Nothing.A; +import static com.joker.maindexkeep.annotations.AnnotationWrapper.Nothing.B; + +/** + * 由于包含注解内部类,所以本身及本身引用的 {@link AnnotationWrapperReference}及 {@link AnnotationWrapperReference} + * 的引用类都将会被打入 maindex + */ +public class AnnotationWrapper { + + public void test() { + AnnotationWrapperReference reference = new AnnotationWrapperReference(); + } + + @Retention(RetentionPolicy.SOURCE) + @StringDef({ A, B }) + public @interface Nothing { + String A = "a"; + String B = "b"; + } +} diff --git a/app/src/main/java/com/joker/maindexkeep/annotations/ClassAnn.java b/app/src/main/java/com/joker/maindexkeep/annotations/ClassAnn.java new file mode 100644 index 0000000..b94e770 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/annotations/ClassAnn.java @@ -0,0 +1,11 @@ +package com.joker.maindexkeep.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ ElementType.METHOD }) +public @interface ClassAnn { +} diff --git a/app/src/main/java/com/joker/maindexkeep/annotations/RuntimeAnn.java b/app/src/main/java/com/joker/maindexkeep/annotations/RuntimeAnn.java new file mode 100644 index 0000000..5ed2ab8 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/annotations/RuntimeAnn.java @@ -0,0 +1,11 @@ +package com.joker.maindexkeep.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +public @interface RuntimeAnn { +} diff --git a/app/src/main/java/com/joker/maindexkeep/annotations/Type.java b/app/src/main/java/com/joker/maindexkeep/annotations/Type.java new file mode 100644 index 0000000..cf2a8c6 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/annotations/Type.java @@ -0,0 +1,13 @@ +package com.joker.maindexkeep.annotations; + +import android.support.annotation.StringDef; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@RuntimeAnn +@Retention(RetentionPolicy.RUNTIME) +@StringDef({ Type.A, Type.B }) +public @interface Type { + String A = "a"; + String B = "b"; +} diff --git a/app/src/main/java/com/joker/maindexkeep/model/AnnotationWrapperReference.java b/app/src/main/java/com/joker/maindexkeep/model/AnnotationWrapperReference.java new file mode 100644 index 0000000..6bc7549 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/model/AnnotationWrapperReference.java @@ -0,0 +1,4 @@ +package com.joker.maindexkeep.model; + +public class AnnotationWrapperReference extends AnnotationWrapperReferenceBase { +} diff --git a/app/src/main/java/com/joker/maindexkeep/model/AnnotationWrapperReferenceBase.java b/app/src/main/java/com/joker/maindexkeep/model/AnnotationWrapperReferenceBase.java new file mode 100644 index 0000000..638a1be --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/model/AnnotationWrapperReferenceBase.java @@ -0,0 +1,4 @@ +package com.joker.maindexkeep.model; + +public class AnnotationWrapperReferenceBase { +} diff --git a/app/src/main/java/com/joker/maindexkeep/model/AppReference.java b/app/src/main/java/com/joker/maindexkeep/model/AppReference.java new file mode 100644 index 0000000..7737111 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/model/AppReference.java @@ -0,0 +1,7 @@ +package com.joker.maindexkeep.model; + +public class AppReference { + public void test() { + + } +} diff --git a/app/src/main/java/com/joker/maindexkeep/model/Person.java b/app/src/main/java/com/joker/maindexkeep/model/Person.java new file mode 100644 index 0000000..acaa3a8 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/model/Person.java @@ -0,0 +1,10 @@ +package com.joker.maindexkeep.model; + +import com.google.gson.annotations.SerializedName; + +/** + * 由于 {@link SerializedName} 是运行时注解,所以当前类将会被打入 maindex + */ +public class Person { + @SerializedName("i") private int i; +} diff --git a/app/src/main/java/com/joker/maindexkeep/shrink/First.java b/app/src/main/java/com/joker/maindexkeep/shrink/First.java new file mode 100644 index 0000000..e73cba8 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/shrink/First.java @@ -0,0 +1,11 @@ +package com.joker.maindexkeep.shrink; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface First { +} diff --git a/app/src/main/java/com/joker/maindexkeep/shrink/Second.java b/app/src/main/java/com/joker/maindexkeep/shrink/Second.java new file mode 100644 index 0000000..4cd91c9 --- /dev/null +++ b/app/src/main/java/com/joker/maindexkeep/shrink/Second.java @@ -0,0 +1,11 @@ +package com.joker.maindexkeep.shrink; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Second { +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..7daee77 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..1d4d9f8 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..f0c039d --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_second.xml b/app/src/main/res/layout/activity_second.xml new file mode 100644 index 0000000..b144b48 --- /dev/null +++ b/app/src/main/res/layout/activity_second.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6d5e5d0 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6d5e5d0 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..898f3ed Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..dffca36 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..64ba76f Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..dae5e08 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..e5ed465 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..14ed0af Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b0907ca Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..d8ae031 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..2c18de9 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..beed3cd Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..b2b4267 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008577 + #00574B + #D81B60 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..a9405ec --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + MainDexKeep + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..705be27 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..ea48afb --- /dev/null +++ b/build.gradle @@ -0,0 +1,24 @@ +buildscript { + repositories { + google() + jcenter() +// maven { url 'https://jitpack.io' } + } + dependencies { + classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.5' + classpath 'com.jakewharton:butterknife-gradle-plugin:8.4.0' +// classpath 'com.github.jokermonn:thinAnnotation:0.0.1' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/buildSrc/.gitignore b/buildSrc/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/buildSrc/.gitignore @@ -0,0 +1 @@ +/build diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 0000000..919b118 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'groovy' + +dependencies { + implementation gradleApi() + implementation localGroovy() + implementation 'com.android.tools.build:gradle:3.0.1' +} + +allprojects { + repositories { + google() + jcenter() + } +} + +buildscript { + repositories { + google() + jcenter() + } + dependencies { + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' + } +} + +apply plugin: 'com.github.dcendents.android-maven' +group = 'com.github.jokermonn' \ No newline at end of file diff --git a/buildSrc/local.properties b/buildSrc/local.properties new file mode 100644 index 0000000..44e11e3 --- /dev/null +++ b/buildSrc/local.properties @@ -0,0 +1,13 @@ +## This file is automatically generated by Android Studio. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file should *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=/Users/baolongzhang/Library/Android/sdk + +nexusUsername=eleme.mobile.android +nexusPassword=8c-3r1l*kI2Z3{ \ No newline at end of file diff --git a/buildSrc/src/main/groovy/com/joker/buildsrc/AbstractMappingProcessor.groovy b/buildSrc/src/main/groovy/com/joker/buildsrc/AbstractMappingProcessor.groovy new file mode 100644 index 0000000..aff1173 --- /dev/null +++ b/buildSrc/src/main/groovy/com/joker/buildsrc/AbstractMappingProcessor.groovy @@ -0,0 +1,20 @@ +package com.joker.buildsrc + +import proguard.obfuscate.MappingProcessor + +class AbstractMappingProcessor implements MappingProcessor { + @Override + boolean processClassMapping(String className, String newClassName) { + return false + } + + @Override + void processFieldMapping(String className, String fieldType, String fieldName, String newClassName, String newFieldName) { + + } + + @Override + void processMethodMapping(String className, int firstLineNumber, int lastLineNumber, String methodReturnType, String methodName, String methodArguments, String newClassName, int newFirstLineNumber, int newLastLineNumber, String newMethodName) { + + } +} diff --git a/buildSrc/src/main/groovy/com/joker/buildsrc/Log.java b/buildSrc/src/main/groovy/com/joker/buildsrc/Log.java new file mode 100644 index 0000000..ed7b30f --- /dev/null +++ b/buildSrc/src/main/groovy/com/joker/buildsrc/Log.java @@ -0,0 +1,12 @@ +package com.joker.buildsrc; + +import org.gradle.api.logging.Logger; +import org.gradle.api.logging.Logging; + +public class Log { + private static final Logger logger = Logging.getLogger(ThinAnnotationTask.class); + + public static void log(String s) { + logger.info(s); + } +} diff --git a/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationExtension.groovy b/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationExtension.groovy new file mode 100644 index 0000000..27db3d1 --- /dev/null +++ b/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationExtension.groovy @@ -0,0 +1,44 @@ +package com.joker.buildsrc; + +class ThinAnnotationExtension { + + static String NAME = "thinAnnotation" + + private boolean enable = true + private def shrinkClass = [] as HashSet + private def shrinkPackage = ["butterknife/", "android/support/annotation/", "androidx/annotation/"] + + def getShrinkClass() { + return shrinkClass + } + + def getShrinkPackage() { + return shrinkPackage + } + + boolean getEnable() { + return enable + } + + void setEnable(boolean enable) { + this.enable = enable + } + + void setShrinkClass(String... shrinkClassName) { + shrinkClass.addAll(shrinkClassName) + } + + void setShrinkPackage(String... shrinkPackageName) { + shrinkPackage.addAll(shrinkPackageName) + } + + @Override String toString() { + return "ThinAnnotationExtension{" + "enable=" + + enable + + ", shrinkClass=" + + shrinkClass + + ", shrinkPackage=" + + shrinkPackage + + '}'; + } +} diff --git a/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationPlugin.groovy b/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationPlugin.groovy new file mode 100644 index 0000000..006b9b2 --- /dev/null +++ b/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationPlugin.groovy @@ -0,0 +1,55 @@ +package com.joker.buildsrc + +import com.android.build.gradle.internal.api.ApplicationVariantImpl +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project + +class ThinAnnotationPlugin implements Plugin { + + def shrinkClass = [] as HashSet + def shrinkPackage = [] + + @Override + void apply(Project project) { + + final ThinAnnotationExtension extension = project.extensions.create( + ThinAnnotationExtension.NAME, ThinAnnotationExtension) + + project.afterEvaluate { + project.plugins.withId('com.android.application') { + project.android.applicationVariants.all { ApplicationVariantImpl variant -> + if (extension.enable) { + Log.log("ThinAnnotation: ThinAnnotationTask is enabled") + def varNameCap = variant.name.capitalize() + def proguardTask = project.tasks.findByName( + "transformClassesAndResourcesWithProguardFor${varNameCap}") + + if (!proguardTask) { + return + } + + proguardTask.doLast() { + proguardTask.outputs.files.each { File file -> + if (file.isDirectory()) { + Log.log("ThinAnnotation: extension: ${extension}") + + DefaultTask thinAnnTask = project.tasks.create( + name: "thinAnnotationFor${varNameCap}", + type: ThinAnnotationTask) { + jarDir = file + variantImpl = variant + shrinkClass = extension.shrinkClass + shrinkPackage = extension.shrinkPackage + } + thinAnnTask.execute() + } + } + } + } + } + } + } + } +} + diff --git a/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationTask.groovy b/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationTask.groovy new file mode 100644 index 0000000..ed2396c --- /dev/null +++ b/buildSrc/src/main/groovy/com/joker/buildsrc/ThinAnnotationTask.groovy @@ -0,0 +1,224 @@ +package com.joker.buildsrc + +import com.android.build.gradle.internal.api.ApplicationVariantImpl +import com.android.build.gradle.internal.scope.GlobalScope +import com.android.build.gradle.internal.scope.VariantScope +import com.google.common.base.Joiner +import groovy.io.FileType +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction +import org.objectweb.asm.* +import proguard.obfuscate.MappingReader + +import java.util.jar.JarEntry +import java.util.jar.JarFile +import java.util.jar.JarOutputStream +import java.util.zip.ZipEntry + +import static com.android.builder.model.AndroidProject.FD_OUTPUTS + +class ThinAnnotationTask extends DefaultTask { + + static final String RETENTION_POLICY = 'Ljava/lang/annotation/RetentionPolicy;' + static final String SOURCE_TIME = 'SOURCE' + + def mapping = [:] + def logFile + + @Input + File jarDir + + @Input + ApplicationVariantImpl variantImpl + + @Input + HashSet shrinkClass + + @Input + List shrinkPackage + + @TaskAction + void action() { + Log.log("ThinAnnotation: ThinAnnotationTask start executing...") + + VariantScope variantScope = variantImpl.variantData.scope + GlobalScope globalScope = variantScope.globalScope + + // get mapping file + MappingReader reader = new MappingReader(variantImpl.mappingFile) + reader.pump(new AbstractMappingProcessor() { + @Override + boolean processClassMapping(String className, String newClassName) { + mapping.put(newClassName.replace('.', '/'), className.replace('.', '/')) + return super.processClassMapping(className, newClassName) + } + }) + // create log file + File output = new File( + Joiner.on(File.separatorChar).join(String.valueOf(globalScope.getBuildDir()), + FD_OUTPUTS, + "thinAnnotation", + variantScope.getVariantConfiguration().getDirName())) + logFile = new File(output, "thinAnnotation.txt") + if (logFile.exists()) { + logFile.delete() + } + output.mkdirs() + logFile.createNewFile() + // remove annotations + jarDir.eachFileRecurse(FileType.FILES) { File file -> + if (!file.name.endsWith(".jar")) { + Log.log("ThinAnnotation: ignore file: ${file}") + return + } + Log.log("ThinAnnotation: target file: ${file}") + JarFile jf = new JarFile(file) + Enumeration je = jf.entries() + File tempJar = new File(file.parentFile, "temp.jar") + JarOutputStream jos = new JarOutputStream(new FileOutputStream(tempJar)) + + while (je.hasMoreElements()) { + JarEntry jarEntry = je.nextElement() + ZipEntry zipEntry = new ZipEntry(jarEntry.getName()) + InputStream originIns = jf.getInputStream(jarEntry) + byte[] bytes = Util.toByteArray(originIns) + originIns.close() + if (jarEntry.name.endsWith(".class")) { + bytes = removeAnnotation(bytes) + } + if (bytes != null) { + jos.putNextEntry(zipEntry) + jos.write(bytes) + jos.closeEntry() + } + } + jos.close() + jf.close() + file.delete() + Util.renameFile(tempJar, file) + Log.log("ThinAnnotation: ThinAnnotationTask is finishing...") + } + } + + byte[] removeAnnotation(byte[] bytes) { + ClassReader cr = new ClassReader(bytes) + ClassWriter cw = new ClassWriter(cr, 0) + String className = "" + boolean canRemoved = false + ClassVisitor cv = new ClassVisitor(Opcodes.ASM4, cw) { + private boolean isAnnotation + + @Override + void visit(int version, int access, String name, String signature, String superName, + String[] interfaces) { + className = name + isAnnotation = (access & Opcodes.ACC_ANNOTATION) != 0 + super.visit(version, access, name, signature, superName, interfaces) + } + + @Override + MethodVisitor visitMethod(int access, String name, String desc, String signature, + String[] exceptions) { + + return new MethodVisitor(Opcodes.ASM4, + super.visitMethod(access, name, desc, signature, exceptions)) { + // ElementType.METHOD / ElementType.CONSTRUCTOR + @Override + AnnotationVisitor visitAnnotation(String ann, boolean visible) { + if (needRemove(desc2ClassPath(ann))) { + return null + } + return super.visitAnnotation(ann, visible) + } + + // ElementType.PARAMETER + @Override + AnnotationVisitor visitParameterAnnotation(int parameter, String ann, + boolean visible) { + if (needRemove(desc2ClassPath(ann))) { + return null + } + return super.visitParameterAnnotation(parameter, ann, visible) + } + } + } + + // ElementType.FIELD / LOCAL_VARIABLE + @Override + FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + return new FieldVisitor(Opcodes.ASM4, + super.visitField(access, name, desc, signature, value)) { + @Override + AnnotationVisitor visitAnnotation(String ann, boolean visible) { + if (needRemove(desc2ClassPath(ann))) { + return null + } + return super.visitAnnotation(ann, visible) + } + } + } + + @Override + AnnotationVisitor visitAnnotation(String ann, boolean visible) { + // ElementType.TYPE / ElementType.ANNOTATION_TYPE + if (needRemove(desc2ClassPath(ann))) { + return null + } + // 是目标注解类则要删除类文件 + if (isAnnotation) { + return new AnnotationVisitor(Opcodes.ASM4, super.visitAnnotation(ann, visible)) { + @Override + void visitEnum(String name, String desc1, String value) { + // RetentionPolicy 为 SOURCE + if (desc1 == RETENTION_POLICY) { + if (value == SOURCE_TIME) { + canRemoved = true + logFile << "annotation(SOURCE) removed => ${decodeProguard(cr.className)}\n" + } else { + if (needRemove(cr.className)) { + canRemoved = true + logFile << "annotation(${value}) removed => ${decodeProguard(cr.className)}\n" + } else { + logFile << "annotation(${value}) not removed => ${decodeProguard(cr.className)}\n" + } + } + } + super.visitEnum(name, desc1, value) + } + } + } + return super.visitAnnotation(ann, visible) + } + } + cr.accept(cv, 0) + if (canRemoved) { + return null + } + return cw.toByteArray() + } + + private boolean needRemove(String className) { + def newName = decodeProguard(className) + return shrinkPackage.any { newName.startsWith(it) } || shrinkClass.contains(newName) + } + + private String desc2ClassPath(String desc) { + if (new String(desc.charAt(0)) == 'L' && new String(desc.charAt(desc.length() - 1)) == ';') { + return desc.substring(1, desc.length() - 1) + } else { + throw new IllegalStateException("desc ${desc} is not a descriptor") + } + } + + /** + * return class name before proguard + * @param the class name after proguard , like android.arch.core.a.a + * @return return origin class name according mapping.txt, return {@param name} if null + */ + private String decodeProguard(String name) { + def decodeName = mapping.get(name) + return decodeName != null ? decodeName : name + } +} diff --git a/buildSrc/src/main/groovy/com/joker/buildsrc/Util.groovy b/buildSrc/src/main/groovy/com/joker/buildsrc/Util.groovy new file mode 100644 index 0000000..7dcc920 --- /dev/null +++ b/buildSrc/src/main/groovy/com/joker/buildsrc/Util.groovy @@ -0,0 +1,27 @@ +package com.joker.buildsrc + +class Util { + + static byte[] toByteArray(final InputStream input) throws IOException { + final ByteArrayOutputStream output = new ByteArrayOutputStream() + final byte[] buffer = new byte[8024] + int n = 0 + long count = 0 + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n) + count += n + } + return output.toByteArray() + } + + static void renameFile(File originFile, File targetFile) { + if (targetFile.exists()) { + targetFile.delete() + } + targetFile.parentFile.mkdirs() + if (!originFile.renameTo(targetFile)) { + throw new RuntimeException("${originFile} rename to ${targetFile} failed ") + } + + } +} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/thinAnnotation.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/thinAnnotation.properties new file mode 100644 index 0000000..4beacec --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/thinAnnotation.properties @@ -0,0 +1 @@ +implementation-class=com.joker.buildsrc.ThinAnnotationPlugin \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1f3e3fe --- /dev/null +++ b/gradle.properties @@ -0,0 +1,15 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# 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 +# 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 +android.enableD8=false + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..fe04b61 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Jan 29 10:19:23 CST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# 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 () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +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 + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +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" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +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 + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e859912 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':app' +include ':buildSrc'