Browse Source

1:重新上传common、news、girls三个库。

guiying712 8 years ago
parent
commit
7301a8d5b2
66 changed files with 3323 additions and 4 deletions
  1. 2 0
      README.md
  2. 1 1
      app/build.gradle
  3. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  4. 41 0
      common/.gitignore
  5. 40 0
      common/build.gradle
  6. BIN
      common/libs/simple-xml-core.jar
  7. 17 0
      common/proguard-rules.pro
  8. 39 0
      common/src/main/AndroidManifest.xml
  9. 48 0
      common/src/main/java/com/guiying/common/base/BaseActivity.java
  10. 146 0
      common/src/main/java/com/guiying/common/base/BaseApplication.java
  11. 7 0
      common/src/main/java/com/guiying/common/base/BasePresenter.java
  12. 7 0
      common/src/main/java/com/guiying/common/base/BaseView.java
  13. 40 0
      common/src/main/java/com/guiying/common/glide/OkHttpGlideModule.java
  14. 78 0
      common/src/main/java/com/guiying/common/glide/OkHttpStreamFetcher.java
  15. 73 0
      common/src/main/java/com/guiying/common/glide/OkHttpUrlLoader.java
  16. 50 0
      common/src/main/java/com/guiying/common/http/ApiService.java
  17. 98 0
      common/src/main/java/com/guiying/common/http/DataParseUtil.java
  18. 337 0
      common/src/main/java/com/guiying/common/http/HttpClient.java
  19. 296 0
      common/src/main/java/com/guiying/common/http/HttpsUtil.java
  20. 141 0
      common/src/main/java/com/guiying/common/http/LoggerInterceptor.java
  21. 37 0
      common/src/main/java/com/guiying/common/http/OnResultListener.java
  22. 49 0
      common/src/main/java/com/guiying/common/utils/CloseUtils.java
  23. 342 0
      common/src/main/java/com/guiying/common/utils/NetworkUtils.java
  24. 156 0
      common/src/main/java/com/guiying/common/utils/ShellUtils.java
  25. 192 0
      common/src/main/java/com/guiying/common/utils/StringUtils.java
  26. 282 0
      common/src/main/java/com/guiying/common/utils/ToastUtils.java
  27. 71 0
      common/src/main/java/com/guiying/common/utils/Utils.java
  28. 14 0
      common/src/main/res/values-v21/styles.xml
  29. 70 0
      common/src/main/res/values/colors.xml
  30. 240 0
      common/src/main/res/values/dimens.xml
  31. 6 0
      common/src/main/res/values/strings.xml
  32. 51 0
      common/src/main/res/values/styles.xml
  33. 1 0
      girls/.gitignore
  34. 50 0
      girls/build.gradle
  35. 17 0
      girls/proguard-rules.pro
  36. 23 0
      girls/src/main/debug/AndroidManifest.xml
  37. 14 0
      girls/src/main/java/com/guiying/girls/Girls.java
  38. 16 0
      girls/src/main/java/com/guiying/girls/GirlsActivity.java
  39. 18 0
      girls/src/main/java/debug/GirlsApplication.java
  40. 10 0
      girls/src/main/release/AndroidManifest.xml
  41. 13 0
      girls/src/main/res/layout/activity_girls.xml
  42. BIN
      girls/src/main/res/mipmap-hdpi/ic_launcher.png
  43. BIN
      girls/src/main/res/mipmap-xhdpi/ic_launcher.png
  44. BIN
      girls/src/main/res/mipmap-xxhdpi/ic_launcher.png
  45. BIN
      girls/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  46. 4 0
      girls/src/main/res/values/colors.xml
  47. 4 0
      girls/src/main/res/values/dimens.xml
  48. 3 0
      girls/src/main/res/values/strings.xml
  49. 2 1
      gradle.properties
  50. 1 0
      news/.gitignore
  51. 50 0
      news/build.gradle
  52. 17 0
      news/proguard-rules.pro
  53. 22 0
      news/src/main/debug/AndroidManifest.xml
  54. 15 0
      news/src/main/java/com/guiying/news/News.java
  55. 16 0
      news/src/main/java/com/guiying/news/NewsActivity.java
  56. 18 0
      news/src/main/java/debug/NewsApplication.java
  57. 10 0
      news/src/main/release/AndroidManifest.xml
  58. 13 0
      news/src/main/res/layout/activity_news.xml
  59. BIN
      news/src/main/res/mipmap-hdpi/ic_launcher.png
  60. BIN
      news/src/main/res/mipmap-xhdpi/ic_launcher.png
  61. BIN
      news/src/main/res/mipmap-xxhdpi/ic_launcher.png
  62. BIN
      news/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  63. 4 0
      news/src/main/res/values/colors.xml
  64. 4 0
      news/src/main/res/values/dimens.xml
  65. 3 0
      news/src/main/res/values/strings.xml
  66. 4 2
      settings.gradle

+ 2 - 0
README.md

@@ -1,2 +1,4 @@
 # AndroidModulePattern
 Android项目组件化示例代码
+
+博客:http://blog.csdn.net/guiying712/article/details/55213884

+ 1 - 1
app/build.gradle

@@ -56,7 +56,7 @@ dependencies {
         compile project(':girls')
         compile project(':news')
     } else {
-        //compile project(':common')
+        compile project(':common')
     }
     //router
     apt "com.github.mzule.activityrouter:compiler:$rootProject.aptCompilerVersion"

BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png


+ 41 - 0
common/.gitignore

@@ -0,0 +1,41 @@
+# 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
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# Intellij
+*.iml
+.idea/workspace.xml
+.idea/vcs.xml
+
+# Keystore files
+*.jks

+ 40 - 0
common/build.gradle

@@ -0,0 +1,40 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode rootProject.ext.versionCode
+        versionName rootProject.ext.versionName
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    //Android Support
+    compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
+    compile "com.android.support:design:$rootProject.supportLibraryVersion"
+    compile "com.android.support:percent:$rootProject.supportLibraryVersion"
+    //网络请求相关
+    compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
+    compile "com.squareup.retrofit2:retrofit-mock:$rootProject.retrofitVersion"
+    compile "com.github.franmontiel:PersistentCookieJar:$rootProject.cookieVersion"
+    //稳定的
+    compile "com.github.bumptech.glide:glide:$rootProject.glideVersion"
+    compile "com.orhanobut:logger:$rootProject.loggerVersion"
+    compile "org.greenrobot:eventbus:$rootProject.eventbusVersion"
+    compile "com.google.code.gson:gson:$rootProject.gsonVersion"
+    //不稳定的
+    compile "com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"
+    compile "com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion"
+}

BIN
common/libs/simple-xml-core.jar


+ 17 - 0
common/proguard-rules.pro

@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive 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:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

+ 39 - 0
common/src/main/AndroidManifest.xml

@@ -0,0 +1,39 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.common">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.WRITE_OWNER_DATA" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.PHONE_STATE" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.FLASHLIGHT" />
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+    <uses-permission android:name="com.videogo.open.permission.C2D_MESSAGE" />
+    <uses-permission android:name="android.permission.GET_TASKS" />
+
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-feature android:name="android.hardware.camera.autofocus" />
+
+    <application>
+        <!--<meta-data-->
+        <!--android:name="com.common.loader.OkHttpGlideModule"-->
+        <!--android:value="GlideModule" />-->
+
+    </application>
+
+</manifest>

+ 48 - 0
common/src/main/java/com/guiying/common/base/BaseActivity.java

@@ -0,0 +1,48 @@
+package com.guiying.common.base;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.IdRes;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+
+/**
+ * <p>Activity基类 </p>
+ *
+ * @author 2016/12/2 17:33
+ * @version V1.0.0
+ * @name BaseActivity
+ */
+public abstract class BaseActivity extends AppCompatActivity {
+
+    /**
+     * 处理Intent,防止开发人员没做Intent判空
+     */
+    protected void handleIntent(Intent intent) {
+    }
+
+    /**
+     * 封装的findViewByID方法
+     */
+    @SuppressWarnings("unchecked")
+    protected <T extends View> T $(@IdRes int id) {
+        return (T) super.findViewById(id);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        BaseApplication.getIns().addActivity(this);
+        //强制在基类Intent判空
+        if (null != getIntent()) {
+            handleIntent(getIntent());
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        BaseApplication.getIns().finishActivity(this);
+    }
+
+}

+ 146 - 0
common/src/main/java/com/guiying/common/base/BaseApplication.java

@@ -0,0 +1,146 @@
+package com.guiying.common.base;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import com.guiying.common.utils.StringUtils;
+import com.guiying.common.utils.Utils;
+import com.orhanobut.logger.LogLevel;
+import com.orhanobut.logger.Logger;
+
+import java.util.Stack;
+
+/**
+ * 要想使用BaseApplication,必须在组件中实现自己的Application,并且继承BaseApplication;
+ * 组件中实现的Application必须在AndroidManifest.xml中注册,否则无法使用;
+ * 组件的Application需置于java/debug文件夹中,不得放于主代码;
+ * 组件中获取Context的方法必须为:Utils.getContext(),不允许其他写法;
+ * BaseApplication主要有如下功能:
+ * 1、全局获取Context;
+ * 2、用来管理全局Activity;
+ *
+ * @author 2016/12/2 17:02
+ * @version V1.0.0
+ * @name BaseApplication
+ */
+public class BaseApplication extends Application {
+
+    private static BaseApplication sInstance;
+
+    private static Stack<Activity> activityStack;
+
+    public static BaseApplication getIns() {
+        return sInstance;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        sInstance = this;
+        Context context = this.getApplicationContext();
+        Utils.init(context);
+        if (isAppDebug(context)) {
+            //只有debug模式才会打印日志
+            Logger.init("Petrel").logLevel(LogLevel.FULL);
+        } else {
+            Logger.init("Petrel").logLevel(LogLevel.NONE);
+        }
+    }
+
+    /**
+     * 添加指定Activity到堆栈
+     */
+    public void addActivity(Activity activity) {
+        if (activityStack == null) {
+            activityStack = new Stack<>();
+        }
+        activityStack.add(activity);
+    }
+
+    /**
+     * 获取当前Activity
+     */
+    public Activity currentActivity() {
+        return activityStack.lastElement();
+    }
+
+    /**
+     * 结束当前Activity
+     */
+    public void finishActivity() {
+        Activity activity = activityStack.lastElement();
+        finishActivity(activity);
+    }
+
+    /**
+     * 结束指定的Activity
+     */
+    public void finishActivity(Activity activity) {
+        if (activity != null) {
+            activityStack.remove(activity);
+            activity.finish();
+            activity = null;
+        }
+    }
+
+    /**
+     * 结束指定Class的Activity
+     */
+    public void finishActivity(Class<?> cls) {
+        for (Activity activity : activityStack) {
+            if (activity.getClass().equals(cls)) {
+                finishActivity(activity);
+                return;
+            }
+        }
+    }
+
+    /**
+     * 结束全部的Activity
+     */
+    public void finishAllActivity() {
+        for (int i = 0, size = activityStack.size(); i < size; i++) {
+            if (null != activityStack.get(i)) {
+                activityStack.get(i).finish();
+            }
+        }
+        activityStack.clear();
+    }
+
+    /**
+     * 退出应用程序
+     */
+    public void exitApp(Context context) {
+        try {
+            finishAllActivity();
+            android.app.ActivityManager activityMgr = (android.app.ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+            activityMgr.restartPackage(context.getPackageName());
+            System.exit(0);
+        } catch (Exception e) {
+            Log.e("ActivityManager", "app exit" + e.getMessage());
+        }
+    }
+
+    /**
+     * 判断App是否是Debug版本
+     *
+     * @param context 上下文
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean isAppDebug(Context context) {
+        if (StringUtils.isSpace(context.getPackageName())) return false;
+        try {
+            PackageManager pm = context.getPackageManager();
+            ApplicationInfo ai = pm.getApplicationInfo(context.getPackageName(), 0);
+            return ai != null && (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+}

+ 7 - 0
common/src/main/java/com/guiying/common/base/BasePresenter.java

@@ -0,0 +1,7 @@
+package com.guiying.common.base;
+
+public interface BasePresenter {
+
+    void start();
+
+}

+ 7 - 0
common/src/main/java/com/guiying/common/base/BaseView.java

@@ -0,0 +1,7 @@
+package com.guiying.common.base;
+
+public interface BaseView<T> {
+
+    void setPresenter(T presenter);
+
+}

+ 40 - 0
common/src/main/java/com/guiying/common/glide/OkHttpGlideModule.java

@@ -0,0 +1,40 @@
+package com.guiying.common.glide;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.module.GlideModule;
+
+/**
+ * A {@link GlideModule} implementation to replace Glide's default
+ * {@link java.net.HttpURLConnection} based {@link com.bumptech.glide.load.model.ModelLoader} with an OkHttp based
+ * {@link com.bumptech.glide.load.model.ModelLoader}.
+ * <p>
+ * <p>
+ * If you're using gradle, you can include this module simply by depending on the aar, the module will be merged
+ * in by manifest merger. For other build systems or for more more information, see
+ * {@link GlideModule}.
+ * </p>
+ */
+public class OkHttpGlideModule implements GlideModule {
+    @Override
+    public void applyOptions(Context context, GlideBuilder builder) {
+        // Do nothing.
+    }
+
+    @Override
+    public void registerComponents(Context context, Glide glide) {
+//        HttpsUtil.SSLParams sslParams = HttpsUtil.getSslSocketFactory(BaseApplication.context, null, , "");
+//        OkHttpClient okHttpClient = new OkHttpClient.Builder()
+//                .sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
+//                .hostnameVerifier(new HostnameVerifier() {
+//                    @Override
+//                    public boolean verify(String hostname, SSLSession session) {
+//                        return true;
+//                    }
+//                })
+//                .build();
+//        glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
+    }
+}

+ 78 - 0
common/src/main/java/com/guiying/common/glide/OkHttpStreamFetcher.java

@@ -0,0 +1,78 @@
+package com.guiying.common.glide;
+
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.util.ContentLengthInputStream;
+import com.guiying.common.utils.CloseUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+
+/**
+ * Fetches an {@link InputStream} using the okhttp library.
+ */
+public class OkHttpStreamFetcher implements DataFetcher<InputStream> {
+    private final OkHttpClient client;
+    private final GlideUrl url;
+    private InputStream stream;
+    private ResponseBody responseBody;
+
+    public OkHttpStreamFetcher(OkHttpClient client, GlideUrl url) {
+        this.client = client;
+        this.url = url;
+    }
+
+    @Override
+    public InputStream loadData(Priority priority) throws Exception {
+        Request.Builder requestBuilder = new Request.Builder()
+                .url(url.toStringUrl());
+
+        for (Map.Entry<String, String> headerEntry : url.getHeaders().entrySet()) {
+            String key = headerEntry.getKey();
+            requestBuilder.addHeader(key, headerEntry.getValue());
+        }
+
+        Request request = requestBuilder.build();
+
+        Response response = client.newCall(request).execute();
+        responseBody = response.body();
+        if (!response.isSuccessful()) {
+            throw new IOException("Request failed with code: " + response.code());
+        }
+
+        long contentLength = responseBody.contentLength();
+        stream = ContentLengthInputStream.obtain(responseBody.byteStream(), contentLength);
+        return stream;
+    }
+
+    @Override
+    public void cleanup() {
+        if (stream != null) {
+            try {
+                stream.close();
+            } catch (IOException e) {
+                // Ignored
+            }
+        }
+        if (responseBody != null) {
+            CloseUtils.closeIO(responseBody);
+        }
+    }
+
+    @Override
+    public String getId() {
+        return url.getCacheKey();
+    }
+
+    @Override
+    public void cancel() {
+
+    }
+}

+ 73 - 0
common/src/main/java/com/guiying/common/glide/OkHttpUrlLoader.java

@@ -0,0 +1,73 @@
+package com.guiying.common.glide;
+
+import android.content.Context;
+
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.GenericLoaderFactory;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+
+import java.io.InputStream;
+
+import okhttp3.OkHttpClient;
+
+/**
+ * A simple model loader for fetching media over http/https using OkHttp.
+ */
+public class OkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
+
+    /**
+     * The default factory for {@link OkHttpUrlLoader}s.
+     */
+    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
+        private static volatile OkHttpClient internalClient;
+        private OkHttpClient client;
+
+        private static OkHttpClient getInternalClient() {
+            if (internalClient == null) {
+                synchronized (Factory.class) {
+                    if (internalClient == null) {
+                        internalClient = new OkHttpClient();
+                    }
+                }
+            }
+            return internalClient;
+        }
+
+        /**
+         * Constructor for a new Factory that runs requests using a static singleton client.
+         */
+        public Factory() {
+            this(getInternalClient());
+        }
+
+        /**
+         * Constructor for a new Factory that runs requests using given client.
+         */
+        public Factory(OkHttpClient client) {
+            this.client = client;
+        }
+
+        @Override
+        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
+            return new OkHttpUrlLoader(client);
+        }
+
+        @Override
+        public void teardown() {
+            // Do nothing, this instance doesn't own the client.
+        }
+    }
+
+    private final OkHttpClient client;
+
+    public OkHttpUrlLoader(OkHttpClient client) {
+        this.client = client;
+    }
+
+    @Override
+    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
+        return new OkHttpStreamFetcher(client, model);
+    }
+}

+ 50 - 0
common/src/main/java/com/guiying/common/http/ApiService.java

@@ -0,0 +1,50 @@
+package com.guiying.common.http;
+
+import java.util.Map;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.FieldMap;
+import retrofit2.http.FormUrlEncoded;
+import retrofit2.http.GET;
+import retrofit2.http.HeaderMap;
+import retrofit2.http.POST;
+import retrofit2.http.Streaming;
+import retrofit2.http.Url;
+
+/**
+ * <p>
+ * 注意:如果方法的泛型指定的类不是ResponseBody,retrofit会将返回的string用json转换器自动转换该类的一个对象,转换不成功就报错
+ * 如果不需要Gson转换,那么就指定泛型为ResponseBody,只能是ResponseBody,子类都不行.
+ * </p>
+ *
+ * @author 张华洋 2016/12/5 15:22
+ * @version V1.0.0
+ * @name HttpParams
+ */
+public interface ApiService {
+
+
+    @GET
+    Call<ResponseBody> executeGet(@Url String url);
+
+    /**
+     * POST方式将以表单的方式传递键值对作为请求体发送到服务器
+     * 其中@FormUrlEncoded 以表单的方式传递键值对
+     * 其中 @Path:所有在网址中的参数(URL的问号前面)
+     * 另外@FieldMap 用于POST请求,提交多个表单数据,@Field:用于POST请求,提交单个数据
+     * 使用@url是为了防止URL被转义为https://10.33.31.200:8890/msp%2Fmobile%2Flogin%3
+     */
+    @FormUrlEncoded
+    @POST
+    Call<ResponseBody> executePost(@Url String url, @FieldMap Map<String, String> map);
+
+
+    /**
+     * 流式下载,不加这个注解的话,会整个文件字节数组全部加载进内存,可能导致oom
+     */
+    @Streaming
+    @GET
+    Call<ResponseBody> download(@Url String fileUrl, @HeaderMap Map<String, String> headers);
+
+}

+ 98 - 0
common/src/main/java/com/guiying/common/http/DataParseUtil.java

@@ -0,0 +1,98 @@
+package com.guiying.common.http;
+
+import android.text.TextUtils;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
+
+import org.simpleframework.xml.Serializer;
+import org.simpleframework.xml.core.Persister;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <p>
+ * 服务器返回数据的解析工具;
+ * 支持XML,json对象,json数组
+ * </p>
+ *
+ * @author 张华洋 2017/1/9 16:00
+ * @version V1.2.0
+ * @name DataParseUtil
+ */
+
+
+public class DataParseUtil {
+
+    private DataParseUtil() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    /**
+     * 解析json对象
+     *
+     * @param string 要解析的json
+     * @param clazz  解析类
+     */
+    public static <T> T parseObject(String string, Class<T> clazz) {
+        return new Gson().fromJson(string, clazz);
+    }
+
+    /**
+     * 解析json数组为ArrayList
+     *
+     * @param json  要解析的json
+     * @param clazz 解析类
+     * @return ArrayList
+     */
+    public static <T> ArrayList<T> parseToArrayList(String json, Class<T> clazz) {
+        Type type = new TypeToken<ArrayList<JsonObject>>() {
+        }.getType();
+        ArrayList<JsonObject> jsonObjects = new Gson().fromJson(json, type);
+        ArrayList<T> arrayList = new ArrayList<>();
+        for (JsonObject jsonObject : jsonObjects) {
+            arrayList.add(new Gson().fromJson(jsonObject, clazz));
+        }
+        return arrayList;
+    }
+
+    /**
+     * 解析json数组为List
+     *
+     * @param json  要解析的json
+     * @param clazz 解析类
+     * @return List
+     */
+    public static <T> List<T> parseToList(String json, Class<T[]> clazz) {
+        Gson gson = new Gson();
+        T[] array = gson.fromJson(json, clazz);
+        return Arrays.asList(array);
+    }
+
+
+    /**
+     * 解析Xml格式数据
+     *
+     * @param json  要解析的json
+     * @param clazz 解析类
+     */
+    public static Object parseXml(String json, Class<?> clazz) {
+        try {
+            if (!TextUtils.isEmpty(json) && clazz != null) {
+                Serializer serializer = new Persister();
+                InputStreamReader is = new InputStreamReader(new ByteArrayInputStream(json.getBytes("UTF-8")), "utf-8");
+                return serializer.read(clazz, is);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+}

+ 337 - 0
common/src/main/java/com/guiying/common/http/HttpClient.java

@@ -0,0 +1,337 @@
+package com.guiying.common.http;
+
+import android.text.TextUtils;
+
+import com.franmontiel.persistentcookiejar.ClearableCookieJar;
+import com.franmontiel.persistentcookiejar.PersistentCookieJar;
+import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
+import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
+import com.guiying.common.R;
+import com.guiying.common.utils.NetworkUtils;
+import com.guiying.common.utils.StringUtils;
+import com.guiying.common.utils.ToastUtils;
+import com.guiying.common.utils.Utils;
+import com.orhanobut.logger.Logger;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.OkHttpClient;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2016/12/5 14:09
+ * @version V1.0.0
+ * @name HttpClient
+ */
+public class HttpClient {
+
+    /*The certificate's password*/
+    private static final String STORE_PASS = "4444444";
+    /*返回数据为String*/
+    public static final int STRING = 0;
+    /*返回数据为json对象*/
+    public static final int OBJECT = 1;
+    /*返回数据为json数组*/
+    public static final int ARRAY = 2;
+    /*返回数据为xml类型*/
+    public static final int XML = 3;
+    /*用户设置的BASE_URL*/
+    public static String BASE_URL = "";
+    /*本地使用的baseUrl*/
+    private String baseUrl = "";
+    private static OkHttpClient okHttpClient;
+    private Builder mBuilder;
+    private Retrofit retrofit;
+    private Call<ResponseBody> mCall;
+    private static final Map<String, Call> CALL_MAP = new HashMap<>();
+
+    /**
+     * 获取HttpClient的单例
+     *
+     * @return HttpClient的唯一对象
+     */
+    private static HttpClient getIns() {
+        return HttpClientHolder.sInstance;
+    }
+
+    /**
+     * 单例模式中的静态内部类写法
+     */
+    private static class HttpClientHolder {
+        private static final HttpClient sInstance = new HttpClient();
+    }
+
+    private HttpClient() {
+        ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(Utils.getContext()));
+        //HttpsUtil.SSLParams sslParams = HttpsUtil.getSslSocketFactory(Utils.getContext(), new int[0], , STORE_PASS);
+        okHttpClient = new OkHttpClient.Builder()
+                .connectTimeout(10000L, TimeUnit.MILLISECONDS)
+                //.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)
+                .hostnameVerifier(HttpsUtil.getHostnameVerifier())
+                .addInterceptor(new LoggerInterceptor(null, true))
+                .cookieJar(cookieJar)
+                .build();
+    }
+
+    public Builder getBuilder() {
+        return mBuilder;
+    }
+
+    private void setBuilder(Builder builder) {
+        this.mBuilder = builder;
+    }
+
+    /**
+     * 获取的Retrofit的实例,
+     * 引起Retrofit变化的因素只有静态变量BASE_URL的改变。
+     */
+    private void getRetrofit() {
+        if (!BASE_URL.equals(baseUrl) || retrofit == null) {
+            baseUrl = BASE_URL;
+            retrofit = new Retrofit.Builder()
+                    .baseUrl(baseUrl)
+                    .client(okHttpClient)
+                    .build();
+        }
+    }
+
+
+    public void post(final OnResultListener onResultListener) {
+        Builder builder = mBuilder;
+        mCall = retrofit.create(ApiService.class)
+                .executePost(builder.url, builder.params);
+        putCall(builder, mCall);
+        request(builder, onResultListener);
+    }
+
+
+    public void get(final OnResultListener onResultListener) {
+        Builder builder = mBuilder;
+        if (!builder.params.isEmpty()) {
+            String value = "";
+            for (Map.Entry<String, String> entry : builder.params.entrySet()) {
+                String mapKey = entry.getKey();
+                String mapValue = entry.getValue();
+                String span = value.equals("") ? "" : "&";
+                String part = StringUtils.buffer(span, mapKey, "=", mapValue);
+                value = StringUtils.buffer(value, part);
+            }
+            builder.url(StringUtils.buffer(builder.url, "?", value));
+        }
+        mCall = retrofit.create(ApiService.class).executeGet(builder.url);
+        putCall(builder, mCall);
+        request(builder, onResultListener);
+    }
+
+
+    private void request(final Builder builder, final OnResultListener onResultListener) {
+        if (!NetworkUtils.isConnected()) {
+            ToastUtils.showLongToastSafe(R.string.current_internet_invalid);
+            onResultListener.onFailure("");
+            return;
+        }
+        mCall.enqueue(new Callback<ResponseBody>() {
+            @Override
+            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
+                if (200 == response.code()) {
+                    try {
+                        String result = response.body().string();
+                        parseJson(result, builder.clazz, builder.bodyType, onResultListener);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+                if (!response.isSuccessful() || 200 != response.code()) {
+                    onResultListener.onError(response.code(), response.message());
+                }
+                if (null != builder.tag) {
+                    removeCall(builder.url);
+                }
+            }
+
+            @Override
+            public void onFailure(Call<ResponseBody> call, Throwable t) {
+                t.printStackTrace();
+                onResultListener.onFailure(t.getMessage());
+                if (null != builder.tag) {
+                    removeCall(builder.url);
+                }
+            }
+
+        });
+    }
+
+
+    /**
+     * 添加某个请求
+     */
+    private synchronized void putCall(Builder builder, Call call) {
+        if (builder.tag == null)
+            return;
+        synchronized (CALL_MAP) {
+            CALL_MAP.put(builder.tag.toString() + builder.url, call);
+        }
+    }
+
+
+    /**
+     * 取消某个界面都所有请求,或者是取消某个tag的所有请求;
+     * 如果要取消某个tag单独请求,tag需要传入tag+url
+     *
+     * @param tag 请求标签
+     */
+    public synchronized void cancel(Object tag) {
+        if (tag == null)
+            return;
+        List<String> list = new ArrayList<>();
+        synchronized (CALL_MAP) {
+            for (String key : CALL_MAP.keySet()) {
+                if (key.startsWith(tag.toString())) {
+                    CALL_MAP.get(key).cancel();
+                    list.add(key);
+                }
+            }
+        }
+        for (String s : list) {
+            removeCall(s);
+        }
+
+    }
+
+    /**
+     * 移除某个请求
+     *
+     * @param url 添加的url
+     */
+    private synchronized void removeCall(String url) {
+        synchronized (CALL_MAP) {
+            for (String key : CALL_MAP.keySet()) {
+                if (key.contains(url)) {
+                    url = key;
+                    break;
+                }
+            }
+            CALL_MAP.remove(url);
+        }
+    }
+
+    /**
+     * Build a new HttpClient.
+     * url is required before calling. All other methods are optional.
+     */
+    public static final class Builder {
+        private String builderBaseUrl = BASE_URL;
+        private String url;
+        private Object tag;
+        private Map<String, String> params = new HashMap<>();
+        /*返回数据的类型*/
+        private int bodyType = STRING;
+        /*解析类*/
+        private Class clazz;
+
+        public Builder() {
+        }
+
+        /**
+         * 如需设置baseUrl请使用这种方法:HttpClient.BASE_URL = "https://10.33.31.200:8890/";
+         * 不推荐使用下面的方法改变baseUrl的值。
+         * 请求地址的baseUrl,最后会被赋值给HttpClient的静态变量BASE_URL;
+         * 例如:"https://10.33.31.200:8890/"
+         *
+         * @param baseUrl 请求地址的baseUrl
+         */
+        @Deprecated
+        public Builder baseUrl(String baseUrl) {
+            this.builderBaseUrl = baseUrl;
+            return this;
+        }
+
+        /**
+         * 除baseUrl以外的部分,
+         * 例如:"msp/mobile/login"
+         *
+         * @param url path路径
+         */
+        public Builder url(String url) {
+            this.url = url;
+            return this;
+        }
+
+        /**
+         * 给当前网络请求添加标签,用于取消这个网络请求
+         *
+         * @param tag 标签
+         */
+        public Builder tag(Object tag) {
+            this.tag = tag;
+            return this;
+        }
+
+        /**
+         * 添加请求参数
+         *
+         * @param key   键
+         * @param value 值
+         */
+        public Builder params(String key, String value) {
+            this.params.put(key, value);
+            return this;
+        }
+
+        /**
+         * 响应体类型设置
+         *
+         * @param bodyType 响应体类型,分别为STRING,OBJECT,ARRAY,XML
+         * @param clazz    指定的解析类
+         * @param <T>      解析类
+         */
+        public <T> Builder bodyType(int bodyType, Class<T> clazz) {
+            this.bodyType = bodyType;
+            this.clazz = clazz;
+            return this;
+        }
+
+        public HttpClient build() {
+            if (!TextUtils.isEmpty(builderBaseUrl)) {
+                BASE_URL = builderBaseUrl;
+            }
+            HttpClient client = HttpClient.getIns();
+            client.getRetrofit();
+            client.setBuilder(this);
+            return client;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void parseJson(String data, Class clazz, int bodyType, OnResultListener onResultListener) {
+        switch (bodyType) {
+            case STRING:
+                onResultListener.onSuccess(data);
+                break;
+            case OBJECT:
+                onResultListener.onSuccess(DataParseUtil.parseObject(data, clazz));
+                break;
+            case ARRAY:
+                onResultListener.onSuccess(DataParseUtil.parseToArrayList(data, clazz));
+                break;
+            case XML:
+                onResultListener.onSuccess(DataParseUtil.parseXml(data, clazz));
+                break;
+            default:
+                Logger.e("http parse tip:", "if you want return object, please use bodyType() set data type");
+                break;
+        }
+    }
+
+}

+ 296 - 0
common/src/main/java/com/guiying/common/http/HttpsUtil.java

@@ -0,0 +1,296 @@
+package com.guiying.common.http;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RawRes;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * HttpsUtils来自于鸿洋的: https://github.com/hongyangAndroid/okhttputils;
+ * 增加了主机名校验方法getHostnameVerifier();
+ * 其他参考的文章有:http://android.jobbole.com/83787/;
+ * <p>
+ * Android 4.X 对TLS1.1、TLS1.2的支持参考了http://blog.csdn.net/joye123/article/details/53888252
+ */
+public class HttpsUtil {
+
+    /**
+     * 包装的 SSL(Secure Socket Layer)参数类
+     */
+    public static class SSLParams {
+        public SSLSocketFactory sSLSocketFactory;
+        public X509TrustManager trustManager;
+    }
+
+    /**
+     * @param context        上下文
+     * @param certificatesId "XXX.cer" 文件 (文件位置res/raw/XXX.cer)
+     * @param bksFileId      "XXX.bks"文件(文件位置res/raw/XXX.bks)
+     * @param password       The certificate's password.
+     * @return SSLParams
+     */
+    public static SSLParams getSslSocketFactory(Context context, @RawRes int[] certificatesId, @RawRes int bksFileId, String password) {
+        if (context == null) {
+            throw new NullPointerException("context == null");
+        }
+        SSLParams sslParams = new SSLParams();
+        try {
+            TrustManager[] trustManagers = prepareTrustManager(context, certificatesId);
+            KeyManager[] keyManagers = prepareKeyManager(context, bksFileId, password);
+
+            //创建TLS类型的SSLContext对象,that uses our TrustManager
+            SSLContext sslContext = SSLContext.getInstance("TLS");
+
+            X509TrustManager x509TrustManager;
+            if (trustManagers != null) {
+                x509TrustManager = new MyTrustManager(chooseTrustManager(trustManagers));
+            } else {
+                x509TrustManager = new UnSafeTrustManager();
+            }
+            //用上面得到的trustManagers初始化SSLContext,这样sslContext就会信任keyStore中的证书
+            sslContext.init(keyManagers, new TrustManager[]{x509TrustManager}, null);
+
+            //通过sslContext获取SSLSocketFactory对象
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+                /*Android 4.X 对TLS1.1、TLS1.2的支持*/
+                sslParams.sSLSocketFactory = new Tls12SocketFactory(sslContext.getSocketFactory());
+                sslParams.trustManager = x509TrustManager;
+                return sslParams;
+            }
+
+            sslParams.sSLSocketFactory = sslContext.getSocketFactory();
+            sslParams.trustManager = x509TrustManager;
+            return sslParams;
+        } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
+            throw new AssertionError(e);
+        }
+    }
+
+
+    /**
+     * 主机名校验方法
+     */
+    public static HostnameVerifier getHostnameVerifier() {
+        return new HostnameVerifier() {
+            @Override
+            public boolean verify(String hostname, SSLSession session) {
+                return hostname.equalsIgnoreCase(session.getPeerHost());
+            }
+        };
+    }
+
+
+    private static TrustManager[] prepareTrustManager(Context context, int[] certificatesId) {
+        if (certificatesId == null || certificatesId.length <= 0) {
+            return null;
+        }
+
+        try {
+            //创建X.509格式的CertificateFactory
+            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+            // 创建一个默认类型的KeyStore,存储我们信任的证书
+            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            keyStore.load(null);
+            int index = 0;
+            for (int certificateId : certificatesId) {
+                //从本地资源中获取证书的流
+                InputStream cerInputStream = context.getResources().openRawResource(certificateId);
+                String certificateAlias = Integer.toString(index++);
+
+                //certificate是java.security.cert.Certificate,而不是其他Certificate
+                //证书工厂根据证书文件的流生成证书Certificate
+                Certificate certificate = certificateFactory.generateCertificate(cerInputStream);
+                //将证书certificate作为信任的证书放入到keyStore中
+                keyStore.setCertificateEntry(certificateAlias, certificate);
+                try {
+                    if (cerInputStream != null)
+                        cerInputStream.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+
+            //TrustManagerFactory是用于生成TrustManager的,这里创建一个默认类型的TrustManagerFactory
+            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            //用我们之前的keyStore实例初始化TrustManagerFactory,这样trustManagerFactory就会信任keyStore中的证书
+            trustManagerFactory.init(keyStore);
+            return trustManagerFactory.getTrustManagers();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    private static KeyManager[] prepareKeyManager(Context context, @RawRes int bksFileId, String password) {
+
+        try {
+            KeyStore clientKeyStore = KeyStore.getInstance("BKS");
+            clientKeyStore.load(context.getResources().openRawResource(bksFileId), password.toCharArray());
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+            keyManagerFactory.init(clientKeyStore, password.toCharArray());
+            return keyManagerFactory.getKeyManagers();
+
+        } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException | CertificateException | IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) {
+        for (TrustManager trustManager : trustManagers) {
+            if (trustManager instanceof X509TrustManager) {
+                return (X509TrustManager) trustManager;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * 客户端不对证书做任何检查;
+     * 客户端不对证书做任何验证的做法有很大的安全漏洞。
+     */
+    private static class UnSafeTrustManager implements X509TrustManager {
+
+        @SuppressLint("TrustAllX509TrustManager")
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+
+        @SuppressLint("TrustAllX509TrustManager")
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType)
+                throws CertificateException {
+        }
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[]{};
+        }
+    }
+
+
+    private static class MyTrustManager implements X509TrustManager {
+        private X509TrustManager defaultTrustManager;
+        private X509TrustManager localTrustManager;
+
+        private MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
+            //TrustManagerFactory是用于生成TrustManager的,创建一个默认类型的TrustManagerFactory
+            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+            trustManagerFactory.init((KeyStore) null);
+            defaultTrustManager = chooseTrustManager(trustManagerFactory.getTrustManagers());
+            this.localTrustManager = localTrustManager;
+        }
+
+
+        @SuppressLint("TrustAllX509TrustManager")
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+            try {
+                defaultTrustManager.checkServerTrusted(chain, authType);
+            } catch (CertificateException ce) {
+                localTrustManager.checkServerTrusted(chain, authType);
+            }
+        }
+
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[0];
+        }
+    }
+
+
+    /**
+     * 自行实现SSLSocketFactory ,实现Android 4.X 对TLSv1.1、TLSv1.2的支持
+     */
+    private static class Tls12SocketFactory extends SSLSocketFactory {
+
+        private static final String[] TLS_SUPPORT_VERSION = {"TLSv1.1", "TLSv1.2"};
+
+        final SSLSocketFactory delegate;
+
+        private Tls12SocketFactory(SSLSocketFactory base) {
+            this.delegate = base;
+        }
+
+        @Override
+        public String[] getDefaultCipherSuites() {
+            return delegate.getDefaultCipherSuites();
+        }
+
+        @Override
+        public String[] getSupportedCipherSuites() {
+            return delegate.getSupportedCipherSuites();
+        }
+
+        @Override
+        public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
+            return patch(delegate.createSocket(s, host, port, autoClose));
+        }
+
+        @Override
+        public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
+            return patch(delegate.createSocket(host, port));
+        }
+
+        @Override
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
+            return patch(delegate.createSocket(host, port, localHost, localPort));
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port) throws IOException {
+            return patch(delegate.createSocket(host, port));
+        }
+
+        @Override
+        public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
+            return patch(delegate.createSocket(address, port, localAddress, localPort));
+        }
+
+        private Socket patch(Socket s) {
+            //代理SSLSocketFactory在创建一个Socket连接的时候,会设置Socket的可用的TLS版本。
+            if (s instanceof SSLSocket) {
+                ((SSLSocket) s).setEnabledProtocols(TLS_SUPPORT_VERSION);
+            }
+            return s;
+        }
+    }
+
+
+}

+ 141 - 0
common/src/main/java/com/guiying/common/http/LoggerInterceptor.java

@@ -0,0 +1,141 @@
+package com.guiying.common.http;
+
+import android.text.TextUtils;
+
+import com.orhanobut.logger.Logger;
+
+import java.io.IOException;
+
+import okhttp3.Headers;
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okhttp3.ResponseBody;
+import okio.Buffer;
+
+public class LoggerInterceptor implements Interceptor {
+
+    public static final String TAG = "HttpClient";
+    private String tag;
+    private boolean showResponse;
+
+    public LoggerInterceptor(String tag, boolean showResponse) {
+        if (TextUtils.isEmpty(tag)) {
+            tag = TAG;
+        }
+        this.showResponse = showResponse;
+        this.tag = tag;
+    }
+
+    public LoggerInterceptor(String tag) {
+        this(tag, false);
+    }
+
+    @Override
+    public Response intercept(Chain chain) throws IOException {
+
+        Request request = chain.request();
+        logForRequest(request);
+        Response response = chain.proceed(request);
+        return logForResponse(response);
+    }
+
+    private void logForRequest(Request request) {
+        try {
+            String url = request.url().toString();
+            Headers headers = request.headers();
+
+            Logger.d("method : " + request.method() + "  ║  url : " + url);
+            if (headers != null && headers.size() > 0) {
+                //Logger.d("headers : " + headers.toString());
+            }
+
+            RequestBody requestBody = request.body();
+            if (requestBody != null) {
+                MediaType mediaType = requestBody.contentType();
+                if (mediaType != null) {
+                    //Logger.d("requestBody's contentType : " + mediaType.toString());
+                    if (isText(mediaType)) {
+                        Logger.d("requestBody's content : " + bodyToString(request));
+                    } else {
+                        //Logger.e("requestBody's content : " + " maybe [file part] , too large too print , ignored!");
+                    }
+                }
+            }
+        } catch (Exception e) {
+//            e.printStackTrace();
+        }
+    }
+
+    private Response logForResponse(Response response) {
+        try {
+            Response.Builder builder = response.newBuilder();
+            Response clone = builder.build();
+            Logger.d("url : " + clone.request().url() + "  ║  code : " + clone.code() + "  ║  protocol : " + clone.protocol());
+            if (!TextUtils.isEmpty(clone.message()))
+                //Logger.d("message : " + clone.message());
+
+                if (showResponse) {
+                    ResponseBody body = clone.body();
+                    if (body != null) {
+                        MediaType mediaType = body.contentType();
+                        if (mediaType != null) {
+                            //Logger.d("responseBody's contentType : " + mediaType.toString());
+                            if (isText(mediaType)) {
+                                String resp = body.string();
+                                //打印json格式或者xml格式日志
+                                switch (mediaType.subtype()) {
+                                    case "xml":
+                                        Logger.xml(resp);
+                                        break;
+                                    case "json":
+                                        Logger.json(resp);
+                                        break;
+                                    default:
+                                        Logger.d(resp);
+                                        break;
+                                }
+                                body = ResponseBody.create(mediaType, resp);
+                                return response.newBuilder().body(body).build();
+                            } else {
+                                Logger.e("responseBody's content : " + " maybe [file part] , too large too print , ignored!");
+                            }
+                        }
+                    }
+                }
+        } catch (Exception e) {
+//            e.printStackTrace();
+        }
+
+        return response;
+    }
+
+
+    private boolean isText(MediaType mediaType) {
+        if (mediaType.type() != null && mediaType.type().equals("text")) {
+            return true;
+        }
+        if (mediaType.subtype() != null) {
+            if (mediaType.subtype().equals("json") ||
+                    mediaType.subtype().equals("xml") ||
+                    mediaType.subtype().equals("html") ||
+                    mediaType.subtype().equals("webviewhtml")
+                    )
+                return true;
+        }
+        return false;
+    }
+
+    private String bodyToString(final Request request) {
+        try {
+            final Request copy = request.newBuilder().build();
+            final Buffer buffer = new Buffer();
+            copy.body().writeTo(buffer);
+            return buffer.readUtf8();
+        } catch (final IOException e) {
+            return "something error when show requestBody.";
+        }
+    }
+}

+ 37 - 0
common/src/main/java/com/guiying/common/http/OnResultListener.java

@@ -0,0 +1,37 @@
+package com.guiying.common.http;
+
+/**
+ * <p>在Retrofit中接口会导致泛型擦除,所以这里回调使用Class</p>
+ *
+ * @author 张华洋 2016/12/15 10:27
+ * @version V1.0.0
+ * @name OnResultListener
+ */
+public class OnResultListener<T> {
+
+    /**
+     * 请求成功的情况
+     *
+     * @param result 需要解析的解析类
+     */
+    public void onSuccess(T result) {
+    }
+
+    /**
+     * 响应成功,但是出错的情况
+     *
+     * @param code    错误码
+     * @param message 错误信息
+     */
+    public void onError(int code, String message) {
+    }
+
+    /**
+     * 请求失败的情况
+     *
+     * @param message 失败信息
+     */
+    public void onFailure(String message) {
+    }
+
+}

+ 49 - 0
common/src/main/java/com/guiying/common/utils/CloseUtils.java

@@ -0,0 +1,49 @@
+package com.guiying.common.utils;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * 关闭相关工具类
+ */
+public class CloseUtils {
+
+    private CloseUtils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    /**
+     * 关闭IO
+     *
+     * @param closeables closeable
+     */
+    public static void closeIO(Closeable... closeables) {
+        if (closeables == null) return;
+        for (Closeable closeable : closeables) {
+            if (closeable != null) {
+                try {
+                    closeable.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * 安静关闭IO
+     *
+     * @param closeables closeable
+     */
+    public static void closeIOQuietly(Closeable... closeables) {
+        if (closeables == null) return;
+        for (Closeable closeable : closeables) {
+            if (closeable != null) {
+                try {
+                    closeable.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+}

+ 342 - 0
common/src/main/java/com/guiying/common/utils/NetworkUtils.java

@@ -0,0 +1,342 @@
+package com.guiying.common.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
+import android.telephony.TelephonyManager;
+
+import com.orhanobut.logger.Logger;
+
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * 网络相关工具类
+ */
+public class NetworkUtils {
+
+    private NetworkUtils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    public enum NetworkType {
+        NETWORK_WIFI,
+        NETWORK_4G,
+        NETWORK_3G,
+        NETWORK_2G,
+        NETWORK_UNKNOWN,
+        NETWORK_NO
+    }
+
+    /**
+     * 打开网络设置界面
+     * <p>3.0以下打开设置界面</p>
+     */
+    public static void openWirelessSettings() {
+        if (android.os.Build.VERSION.SDK_INT > 10) {
+            Utils.getContext().startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        } else {
+            Utils.getContext().startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+        }
+    }
+
+    /**
+     * 获取活动网络信息
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>}</p>
+     *
+     * @return NetworkInfo
+     */
+    private static NetworkInfo getActiveNetworkInfo() {
+        return ((ConnectivityManager) Utils.getContext().getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
+    }
+
+    /**
+     * 判断网络是否连接
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>}</p>
+     *
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean isConnected() {
+        NetworkInfo info = getActiveNetworkInfo();
+        return info != null && info.isConnected();
+    }
+
+    /**
+     * 判断网络是否可用
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.INTERNET"/>}</p>
+     *
+     * @return {@code true}: 可用<br>{@code false}: 不可用
+     */
+    public static boolean isAvailableByPing() {
+        ShellUtils.CommandResult result = ShellUtils.execCmd("ping -c 1 -w 1 223.5.5.5", false);
+        boolean ret = result.result == 0;
+        if (result.errorMsg != null) {
+            Logger.d("isAvailableByPing errorMsg", result.errorMsg);
+        }
+        if (result.successMsg != null) {
+            Logger.d("isAvailableByPing successMsg", result.successMsg);
+        }
+        return ret;
+    }
+
+    /**
+     * 判断移动数据是否打开
+     *
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean getDataEnabled() {
+        try {
+            TelephonyManager tm = (TelephonyManager) Utils.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+            Method getMobileDataEnabledMethod = tm.getClass().getDeclaredMethod("getDataEnabled");
+            if (null != getMobileDataEnabledMethod) {
+                return (boolean) getMobileDataEnabledMethod.invoke(tm);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    /**
+     * 打开或关闭移动数据
+     * <p>需系统应用 需添加权限{@code <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>}</p>
+     *
+     * @param enabled {@code true}: 打开<br>{@code false}: 关闭
+     */
+    public static void setDataEnabled(boolean enabled) {
+        try {
+            TelephonyManager tm = (TelephonyManager) Utils.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+            Method setMobileDataEnabledMethod = tm.getClass().getDeclaredMethod("setDataEnabled", boolean.class);
+            if (null != setMobileDataEnabledMethod) {
+                setMobileDataEnabledMethod.invoke(tm, enabled);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 判断网络是否是4G
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>}</p>
+     *
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean is4G() {
+        NetworkInfo info = getActiveNetworkInfo();
+        return info != null && info.isAvailable() && info.getSubtype() == TelephonyManager.NETWORK_TYPE_LTE;
+    }
+
+    /**
+     * 判断wifi是否打开
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>}</p>
+     *
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean getWifiEnabled() {
+        WifiManager wifiManager = (WifiManager) Utils.getContext().getSystemService(Context.WIFI_SERVICE);
+        return wifiManager.isWifiEnabled();
+    }
+
+    /**
+     * 打开或关闭wifi
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>}</p>
+     *
+     * @param enabled {@code true}: 打开<br>{@code false}: 关闭
+     */
+    public static void setWifiEnabled(boolean enabled) {
+        WifiManager wifiManager = (WifiManager) Utils.getContext().getSystemService(Context.WIFI_SERVICE);
+        if (enabled) {
+            if (!wifiManager.isWifiEnabled()) {
+                wifiManager.setWifiEnabled(true);
+            }
+        } else {
+            if (wifiManager.isWifiEnabled()) {
+                wifiManager.setWifiEnabled(false);
+            }
+        }
+    }
+
+    /**
+     * 判断wifi是否连接状态
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>}</p>
+     *
+     * @return {@code true}: 连接<br>{@code false}: 未连接
+     */
+    public static boolean isWifiConnected() {
+        ConnectivityManager cm = (ConnectivityManager) Utils.getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        return cm != null && cm.getActiveNetworkInfo() != null
+                && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI;
+    }
+
+    /**
+     * 判断wifi数据是否可用
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>}</p>
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.INTERNET"/>}</p>
+     *
+     * @return {@code true}: 是<br>{@code false}: 否
+     */
+    public static boolean isWifiAvailable() {
+        return getWifiEnabled() && isAvailableByPing();
+    }
+
+    /**
+     * 获取网络运营商名称
+     * <p>中国移动、如中国联通、中国电信</p>
+     *
+     * @return 运营商名称
+     */
+    public static String getNetworkOperatorName() {
+        TelephonyManager tm = (TelephonyManager) Utils.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        return tm != null ? tm.getNetworkOperatorName() : null;
+    }
+
+    private static final int NETWORK_TYPE_GSM = 16;
+    private static final int NETWORK_TYPE_TD_SCDMA = 17;
+    private static final int NETWORK_TYPE_IWLAN = 18;
+
+    /**
+     * 获取当前网络类型
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>}</p>
+     *
+     * @return 网络类型
+     * <ul>
+     * <li>{@link NetworkUtils.NetworkType#NETWORK_WIFI   } </li>
+     * <li>{@link NetworkUtils.NetworkType#NETWORK_4G     } </li>
+     * <li>{@link NetworkUtils.NetworkType#NETWORK_3G     } </li>
+     * <li>{@link NetworkUtils.NetworkType#NETWORK_2G     } </li>
+     * <li>{@link NetworkUtils.NetworkType#NETWORK_UNKNOWN} </li>
+     * <li>{@link NetworkUtils.NetworkType#NETWORK_NO     } </li>
+     * </ul>
+     */
+    public static NetworkType getNetworkType() {
+        NetworkType netType = NetworkType.NETWORK_NO;
+        NetworkInfo info = getActiveNetworkInfo();
+        if (info != null && info.isAvailable()) {
+
+            if (info.getType() == ConnectivityManager.TYPE_WIFI) {
+                netType = NetworkType.NETWORK_WIFI;
+            } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
+                switch (info.getSubtype()) {
+
+                    case NETWORK_TYPE_GSM:
+                    case TelephonyManager.NETWORK_TYPE_GPRS:
+                    case TelephonyManager.NETWORK_TYPE_CDMA:
+                    case TelephonyManager.NETWORK_TYPE_EDGE:
+                    case TelephonyManager.NETWORK_TYPE_1xRTT:
+                    case TelephonyManager.NETWORK_TYPE_IDEN:
+                        netType = NetworkType.NETWORK_2G;
+                        break;
+
+                    case NETWORK_TYPE_TD_SCDMA:
+                    case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                    case TelephonyManager.NETWORK_TYPE_UMTS:
+                    case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                    case TelephonyManager.NETWORK_TYPE_HSDPA:
+                    case TelephonyManager.NETWORK_TYPE_HSUPA:
+                    case TelephonyManager.NETWORK_TYPE_HSPA:
+                    case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                    case TelephonyManager.NETWORK_TYPE_EHRPD:
+                    case TelephonyManager.NETWORK_TYPE_HSPAP:
+                        netType = NetworkType.NETWORK_3G;
+                        break;
+
+                    case NETWORK_TYPE_IWLAN:
+                    case TelephonyManager.NETWORK_TYPE_LTE:
+                        netType = NetworkType.NETWORK_4G;
+                        break;
+                    default:
+
+                        String subtypeName = info.getSubtypeName();
+                        if (subtypeName.equalsIgnoreCase("TD-SCDMA")
+                                || subtypeName.equalsIgnoreCase("WCDMA")
+                                || subtypeName.equalsIgnoreCase("CDMA2000")) {
+                            netType = NetworkType.NETWORK_3G;
+                        } else {
+                            netType = NetworkType.NETWORK_UNKNOWN;
+                        }
+                        break;
+                }
+            } else {
+                netType = NetworkType.NETWORK_UNKNOWN;
+            }
+        }
+        return netType;
+    }
+
+    /**
+     * 获取IP地址
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.INTERNET"/>}</p>
+     *
+     * @param useIPv4 是否用IPv4
+     * @return IP地址
+     */
+    public static String getIPAddress(boolean useIPv4) {
+        try {
+            for (Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces(); nis.hasMoreElements(); ) {
+                NetworkInterface ni = nis.nextElement();
+                // 防止小米手机返回10.0.2.15
+                if (!ni.isUp()) continue;
+                for (Enumeration<InetAddress> addresses = ni.getInetAddresses(); addresses.hasMoreElements(); ) {
+                    InetAddress inetAddress = addresses.nextElement();
+                    if (!inetAddress.isLoopbackAddress()) {
+                        String hostAddress = inetAddress.getHostAddress();
+                        boolean isIPv4 = hostAddress.indexOf(':') < 0;
+                        if (useIPv4) {
+                            if (isIPv4) return hostAddress;
+                        } else {
+                            if (!isIPv4) {
+                                int index = hostAddress.indexOf('%');
+                                return index < 0 ? hostAddress.toUpperCase() : hostAddress.substring(0, index).toUpperCase();
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 获取域名ip地址
+     * <p>需添加权限 {@code <uses-permission android:name="android.permission.INTERNET"/>}</p>
+     *
+     * @param domain 域名
+     * @return ip地址
+     */
+    public static String getDomainAddress(final String domain) {
+        try {
+            ExecutorService exec = Executors.newCachedThreadPool();
+            Future<String> fs = exec.submit(new Callable<String>() {
+                @Override
+                public String call() throws Exception {
+                    InetAddress inetAddress;
+                    try {
+                        inetAddress = InetAddress.getByName(domain);
+                        return inetAddress.getHostAddress();
+                    } catch (UnknownHostException e) {
+                        e.printStackTrace();
+                    }
+                    return null;
+                }
+            });
+            return fs.get();
+        } catch (InterruptedException | ExecutionException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 156 - 0
common/src/main/java/com/guiying/common/utils/ShellUtils.java

@@ -0,0 +1,156 @@
+package com.guiying.common.utils;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+
+/**
+ * Shell相关工具类
+ */
+public class ShellUtils {
+
+    private ShellUtils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    /**
+     * 是否是在root下执行命令
+     *
+     * @param command 命令
+     * @param isRoot  是否需要root权限执行
+     * @return CommandResult
+     */
+    public static CommandResult execCmd(String command, boolean isRoot) {
+        return execCmd(new String[]{command}, isRoot, true);
+    }
+
+    /**
+     * 是否是在root下执行命令
+     *
+     * @param commands 多条命令链表
+     * @param isRoot   是否需要root权限执行
+     * @return CommandResult
+     */
+    public static CommandResult execCmd(List<String> commands, boolean isRoot) {
+        return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRoot, true);
+    }
+
+    /**
+     * 是否是在root下执行命令
+     *
+     * @param commands 多条命令数组
+     * @param isRoot   是否需要root权限执行
+     * @return CommandResult
+     */
+    public static CommandResult execCmd(String[] commands, boolean isRoot) {
+        return execCmd(commands, isRoot, true);
+    }
+
+    /**
+     * 是否是在root下执行命令
+     *
+     * @param command         命令
+     * @param isRoot          是否需要root权限执行
+     * @param isNeedResultMsg 是否需要结果消息
+     * @return CommandResult
+     */
+    public static CommandResult execCmd(String command, boolean isRoot, boolean isNeedResultMsg) {
+        return execCmd(new String[]{command}, isRoot, isNeedResultMsg);
+    }
+
+    /**
+     * 是否是在root下执行命令
+     *
+     * @param commands        命令链表
+     * @param isRoot          是否需要root权限执行
+     * @param isNeedResultMsg 是否需要结果消息
+     * @return CommandResult
+     */
+    public static CommandResult execCmd(List<String> commands, boolean isRoot, boolean isNeedResultMsg) {
+        return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRoot, isNeedResultMsg);
+    }
+
+    /**
+     * 是否是在root下执行命令
+     *
+     * @param commands        命令数组
+     * @param isRoot          是否需要root权限执行
+     * @param isNeedResultMsg 是否需要结果消息
+     * @return CommandResult
+     */
+    public static CommandResult execCmd(String[] commands, boolean isRoot, boolean isNeedResultMsg) {
+        int result = -1;
+        if (commands == null || commands.length == 0) {
+            return new CommandResult(result, null, null);
+        }
+        Process process = null;
+        BufferedReader successResult = null;
+        BufferedReader errorResult = null;
+        StringBuilder successMsg = null;
+        StringBuilder errorMsg = null;
+        DataOutputStream os = null;
+        try {
+            process = Runtime.getRuntime().exec(isRoot ? "su" : "sh");
+            os = new DataOutputStream(process.getOutputStream());
+            for (String command : commands) {
+                if (command == null) continue;
+                os.write(command.getBytes());
+                os.writeBytes("\n");
+                os.flush();
+            }
+            os.writeBytes("exit\n");
+            os.flush();
+            result = process.waitFor();
+            if (isNeedResultMsg) {
+                successMsg = new StringBuilder();
+                errorMsg = new StringBuilder();
+                successResult = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
+                errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
+                String s;
+                while ((s = successResult.readLine()) != null) {
+                    successMsg.append(s);
+                }
+                while ((s = errorResult.readLine()) != null) {
+                    errorMsg.append(s);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            CloseUtils.closeIO(os, successResult, errorResult);
+            if (process != null) {
+                process.destroy();
+            }
+        }
+        return new CommandResult(
+                result,
+                successMsg == null ? null : successMsg.toString(),
+                errorMsg == null ? null : errorMsg.toString()
+        );
+    }
+
+    /**
+     * 返回的命令结果
+     */
+    public static class CommandResult {
+        /**
+         * 结果码
+         **/
+        public int result;
+        /**
+         * 成功信息
+         **/
+        public String successMsg;
+        /**
+         * 错误信息
+         **/
+        public String errorMsg;
+
+        public CommandResult(int result, String successMsg, String errorMsg) {
+            this.result = result;
+            this.successMsg = successMsg;
+            this.errorMsg = errorMsg;
+        }
+    }
+}

+ 192 - 0
common/src/main/java/com/guiying/common/utils/StringUtils.java

@@ -0,0 +1,192 @@
+package com.guiying.common.utils;
+
+/**
+ * 字符串相关工具类
+ */
+public class StringUtils {
+
+    private StringUtils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    /**
+     * 字符串拼接,线程安全
+     */
+    public static String buffer(String... array) {
+        StringBuffer s = new StringBuffer();
+        for (String str : array) {
+            s.append(str);
+        }
+        return s.toString();
+    }
+
+    /**
+     * 字符串拼接,线程不安全,效率高
+     */
+    public static String builder(String... array) {
+        StringBuilder s = new StringBuilder();
+        for (String str : array) {
+            s.append(str);
+        }
+        return s.toString();
+    }
+
+
+    /**
+     * 判断字符串是否为null或长度为0
+     *
+     * @param s 待校验字符串
+     * @return {@code true}: 空<br> {@code false}: 不为空
+     */
+    public static boolean isEmpty(CharSequence s) {
+        return s == null || s.length() == 0;
+    }
+
+    /**
+     * 判断字符串是否为null或全为空格
+     *
+     * @param s 待校验字符串
+     * @return {@code true}: null或全空格<br> {@code false}: 不为null且不全空格
+     */
+    public static boolean isSpace(String s) {
+        return (s == null || s.trim().length() == 0);
+    }
+
+    /**
+     * 判断两字符串是否相等
+     *
+     * @param a 待校验字符串a
+     * @param b 待校验字符串b
+     * @return {@code true}: 相等<br>{@code false}: 不相等
+     */
+    public static boolean equals(CharSequence a, CharSequence b) {
+        if (a == b) return true;
+        int length;
+        if (a != null && b != null && (length = a.length()) == b.length()) {
+            if (a instanceof String && b instanceof String) {
+                return a.equals(b);
+            } else {
+                for (int i = 0; i < length; i++) {
+                    if (a.charAt(i) != b.charAt(i)) return false;
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 判断两字符串忽略大小写是否相等
+     *
+     * @param a 待校验字符串a
+     * @param b 待校验字符串b
+     * @return {@code true}: 相等<br>{@code false}: 不相等
+     */
+    public static boolean equalsIgnoreCase(String a, String b) {
+        return (a == b) || (b != null) && (a.length() == b.length()) && a.regionMatches(true, 0, b, 0, b.length());
+    }
+
+    /**
+     * null转为长度为0的字符串
+     *
+     * @param s 待转字符串
+     * @return s为null转为长度为0字符串,否则不改变
+     */
+    public static String null2Length0(String s) {
+        return s == null ? "" : s;
+    }
+
+    /**
+     * 返回字符串长度
+     *
+     * @param s 字符串
+     * @return null返回0,其他返回自身长度
+     */
+    public static int length(CharSequence s) {
+        return s == null ? 0 : s.length();
+    }
+
+    /**
+     * 首字母大写
+     *
+     * @param s 待转字符串
+     * @return 首字母大写字符串
+     */
+    public static String upperFirstLetter(String s) {
+        if (isEmpty(s) || !Character.isLowerCase(s.charAt(0))) return s;
+        return String.valueOf((char) (s.charAt(0) - 32)) + s.substring(1);
+    }
+
+    /**
+     * 首字母小写
+     *
+     * @param s 待转字符串
+     * @return 首字母小写字符串
+     */
+    public static String lowerFirstLetter(String s) {
+        if (isEmpty(s) || !Character.isUpperCase(s.charAt(0))) return s;
+        return String.valueOf((char) (s.charAt(0) + 32)) + s.substring(1);
+    }
+
+    /**
+     * 反转字符串
+     *
+     * @param s 待反转字符串
+     * @return 反转字符串
+     */
+    public static String reverse(String s) {
+        int len = length(s);
+        if (len <= 1) return s;
+        int mid = len >> 1;
+        char[] chars = s.toCharArray();
+        char c;
+        for (int i = 0; i < mid; ++i) {
+            c = chars[i];
+            chars[i] = chars[len - i - 1];
+            chars[len - i - 1] = c;
+        }
+        return new String(chars);
+    }
+
+    /**
+     * 转化为半角字符
+     *
+     * @param s 待转字符串
+     * @return 半角字符串
+     */
+    public static String toDBC(String s) {
+        if (isEmpty(s)) return s;
+        char[] chars = s.toCharArray();
+        for (int i = 0, len = chars.length; i < len; i++) {
+            if (chars[i] == 12288) {
+                chars[i] = ' ';
+            } else if (65281 <= chars[i] && chars[i] <= 65374) {
+                chars[i] = (char) (chars[i] - 65248);
+            } else {
+                chars[i] = chars[i];
+            }
+        }
+        return new String(chars);
+    }
+
+    /**
+     * 转化为全角字符
+     *
+     * @param s 待转字符串
+     * @return 全角字符串
+     */
+    public static String toSBC(String s) {
+        if (isEmpty(s)) return s;
+        char[] chars = s.toCharArray();
+        for (int i = 0, len = chars.length; i < len; i++) {
+            if (chars[i] == ' ') {
+                chars[i] = (char) 12288;
+            } else if (33 <= chars[i] && chars[i] <= 126) {
+                chars[i] = (char) (chars[i] + 65248);
+            } else {
+                chars[i] = chars[i];
+            }
+        }
+        return new String(chars);
+    }
+}

+ 282 - 0
common/src/main/java/com/guiying/common/utils/ToastUtils.java

@@ -0,0 +1,282 @@
+package com.guiying.common.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.StringRes;
+import android.widget.Toast;
+
+/**
+ * Toast相关工具类
+ */
+public class ToastUtils {
+
+    private ToastUtils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    private static Toast sToast;
+    private static Handler sHandler = new Handler(Looper.getMainLooper());
+    private static boolean isJumpWhenMore;
+
+    /**
+     * 吐司初始化
+     *
+     * @param isJumpWhenMore 当连续弹出吐司时,是要弹出新吐司还是只修改文本内容
+     *                       <p>{@code true}: 弹出新吐司<br>{@code false}: 只修改文本内容</p>
+     *                       <p>如果为{@code false}的话可用来做显示任意时长的吐司</p>
+     */
+    public static void init(boolean isJumpWhenMore) {
+        ToastUtils.isJumpWhenMore = isJumpWhenMore;
+    }
+
+    /**
+     * 安全地显示短时吐司
+     *
+     * @param text 文本
+     */
+    public static void showShortToastSafe(final CharSequence text) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(text, Toast.LENGTH_SHORT);
+            }
+        });
+    }
+
+    /**
+     * 安全地显示短时吐司
+     *
+     * @param resId 资源Id
+     */
+    public static void showShortToastSafe(final @StringRes int resId) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(resId, Toast.LENGTH_SHORT);
+            }
+        });
+    }
+
+    /**
+     * 安全地显示短时吐司
+     *
+     * @param resId 资源Id
+     * @param args  参数
+     */
+    public static void showShortToastSafe(final @StringRes int resId, final Object... args) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(resId, Toast.LENGTH_SHORT, args);
+            }
+        });
+    }
+
+    /**
+     * 安全地显示短时吐司
+     *
+     * @param format 格式
+     * @param args   参数
+     */
+    public static void showShortToastSafe(final String format, final Object... args) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(format, Toast.LENGTH_SHORT, args);
+            }
+        });
+    }
+
+    /**
+     * 安全地显示长时吐司
+     *
+     * @param text 文本
+     */
+    public static void showLongToastSafe(final CharSequence text) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(text, Toast.LENGTH_LONG);
+            }
+        });
+    }
+
+    /**
+     * 安全地显示长时吐司
+     *
+     * @param resId 资源Id
+     */
+    public static void showLongToastSafe(final @StringRes int resId) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(resId, Toast.LENGTH_LONG);
+            }
+        });
+    }
+
+    /**
+     * 安全地显示长时吐司
+     *
+     * @param resId 资源Id
+     * @param args  参数
+     */
+    public static void showLongToastSafe(final @StringRes int resId, final Object... args) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(resId, Toast.LENGTH_LONG, args);
+            }
+        });
+    }
+
+    /**
+     * 安全地显示长时吐司
+     *
+     * @param format 格式
+     * @param args   参数
+     */
+    public static void showLongToastSafe(final String format, final Object... args) {
+        sHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                showToast(format, Toast.LENGTH_LONG, args);
+            }
+        });
+    }
+
+    /**
+     * 显示短时吐司
+     *
+     * @param text 文本
+     */
+    public static void showShortToast(CharSequence text) {
+        showToast(text, Toast.LENGTH_SHORT);
+    }
+
+    /**
+     * 显示短时吐司
+     *
+     * @param resId 资源Id
+     */
+    public static void showShortToast(@StringRes int resId) {
+        showToast(resId, Toast.LENGTH_SHORT);
+    }
+
+    /**
+     * 显示短时吐司
+     *
+     * @param resId 资源Id
+     * @param args  参数
+     */
+    public static void showShortToast(@StringRes int resId, Object... args) {
+        showToast(resId, Toast.LENGTH_SHORT, args);
+    }
+
+    /**
+     * 显示短时吐司
+     *
+     * @param format 格式
+     * @param args   参数
+     */
+    public static void showShortToast(String format, Object... args) {
+        showToast(format, Toast.LENGTH_SHORT, args);
+    }
+
+    /**
+     * 显示长时吐司
+     *
+     * @param text 文本
+     */
+    public static void showLongToast(CharSequence text) {
+        showToast(text, Toast.LENGTH_LONG);
+    }
+
+    /**
+     * 显示长时吐司
+     *
+     * @param resId 资源Id
+     */
+    public static void showLongToast(@StringRes int resId) {
+        showToast(resId, Toast.LENGTH_LONG);
+    }
+
+    /**
+     * 显示长时吐司
+     *
+     * @param resId 资源Id
+     * @param args  参数
+     */
+    public static void showLongToast(@StringRes int resId, Object... args) {
+        showToast(resId, Toast.LENGTH_LONG, args);
+    }
+
+    /**
+     * 显示长时吐司
+     *
+     * @param format 格式
+     * @param args   参数
+     */
+    public static void showLongToast(String format, Object... args) {
+        showToast(format, Toast.LENGTH_LONG, args);
+    }
+
+    /**
+     * 显示吐司
+     *
+     * @param resId    资源Id
+     * @param duration 显示时长
+     */
+    private static void showToast(@StringRes int resId, int duration) {
+        showToast(Utils.getContext().getResources().getText(resId).toString(), duration);
+    }
+
+    /**
+     * 显示吐司
+     *
+     * @param resId    资源Id
+     * @param duration 显示时长
+     * @param args     参数
+     */
+    private static void showToast(@StringRes int resId, int duration, Object... args) {
+        showToast(String.format(Utils.getContext().getResources().getString(resId), args), duration);
+    }
+
+    /**
+     * 显示吐司
+     *
+     * @param format   格式
+     * @param duration 显示时长
+     * @param args     参数
+     */
+    private static void showToast(String format, int duration, Object... args) {
+        showToast(String.format(format, args), duration);
+    }
+
+    /**
+     * 显示吐司
+     *
+     * @param text     文本
+     * @param duration 显示时长
+     */
+    private static void showToast(CharSequence text, int duration) {
+        if (isJumpWhenMore) cancelToast();
+        if (sToast == null) {
+            sToast = Toast.makeText(Utils.getContext(), text, duration);
+        } else {
+            sToast.setText(text);
+            sToast.setDuration(duration);
+        }
+        sToast.show();
+    }
+
+    /**
+     * 取消吐司显示
+     */
+    public static void cancelToast() {
+        if (sToast != null) {
+            sToast.cancel();
+            sToast = null;
+        }
+    }
+}

+ 71 - 0
common/src/main/java/com/guiying/common/utils/Utils.java

@@ -0,0 +1,71 @@
+package com.guiying.common.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.view.View;
+
+/**
+ * <p>Utils初始化相关 </p>
+ */
+public class Utils {
+
+    private static Context context;
+
+    private Utils() {
+        throw new UnsupportedOperationException("u can't instantiate me...");
+    }
+
+    /**
+     * 初始化工具类
+     *
+     * @param context 上下文
+     */
+    public static void init(Context context) {
+        Utils.context = context.getApplicationContext();
+    }
+
+    /**
+     * 获取ApplicationContext
+     *
+     * @return ApplicationContext
+     */
+    public static Context getContext() {
+        if (context != null) return context;
+        throw new NullPointerException("u should init first");
+    }
+
+    /**
+     * View获取Activity的工具
+     *
+     * @param view view
+     * @return Activity
+     */
+    public static
+    @NonNull
+    Activity getActivity(View view) {
+        Context context = view.getContext();
+
+        while (context instanceof ContextWrapper) {
+            if (context instanceof Activity) {
+                return (Activity) context;
+            }
+            context = ((ContextWrapper) context).getBaseContext();
+        }
+
+        throw new IllegalStateException("View " + view + " is not attached to an Activity");
+    }
+
+    /**
+     * 全局获取String的方法
+     *
+     * @param id 资源Id
+     * @return String
+     */
+    public static String getString(@StringRes int id) {
+        return context.getResources().getString(id);
+    }
+
+}

+ 14 - 0
common/src/main/res/values-v21/styles.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="AppTheme" parent="Base.AppTheme">
+        <item name="android:navigationBarColor">@color/gray_E6</item><!--虚拟导航栏颜色-->
+        <!--<item name="android:colorControlHighlight">@color/gray_AD</item>&lt;!&ndash;波纹颜色&ndash;&gt;-->
+    </style>
+
+    <style name="TransparentStatusBarTheme" parent="AppTheme.NoActionBar">
+        <item name="android:statusBarColor">@color/gray_D5</item>
+    </style>
+
+
+</resources>

+ 70 - 0
common/src/main/res/values/colors.xml

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!--APP的主题色-->
+    <color name="colorPrimary">#3f85e4</color>
+    <color name="colorPrimaryDark">#3f85e4</color>
+    <color name="colorAccent">#3f85e4</color>
+
+    <!--APP公用的一些颜色-->
+    <color name="common_bg">#fff7f7f7</color>
+    <color name="common_text">@android:color/primary_text_light</color>
+    <color name="common_hint_text">@color/gray_AD</color>
+    <color name="divider_color">@color/gray_cc</color>
+    <color name="black_overlay">@color/black_alpha40</color>
+    <color name="common_title_bg">#CA2C32</color>
+    <color name="transparent">@android:color/transparent</color>
+    <color name="transparent_color">#00ffffff</color>
+    <color name="text_black_4d">#4d4d4d</color>
+    <color name="text_908f94">#908f94</color>
+    <color name="bg_999999">#999999</color>
+    <color name="bg_gray">#dedede</color>
+    <color name="text_color_404040">#404040</color>
+    <color name="color_map_grid_selected">#F9686D</color>
+
+    <color name="white">#ffffffff</color>
+    <color name="white_10">#1affffff</color>
+    <color name="white_20">#33ffffff</color>
+    <color name="white_30">#4dffffff</color>
+
+    <!--黑色-各种等级透明度-->
+    <color name="black">#ff000000</color>
+    <color name="black_alpha10">#1a000000</color>
+    <color name="black_alpha15">#26000000</color>
+    <color name="black_alpha20">#33000000</color>
+    <color name="black_alpha30">#4d000000</color>
+    <color name="black_alpha32">#52000000</color>
+    <color name="black_alpha40">#66000000</color>
+    <color name="black_alpha5">#0d000000</color>
+    <color name="black_alpha50">#80000000</color>
+    <color name="black_alpha54">#89000000</color>
+    <color name="black_alpha60">#99000000</color>
+    <color name="black_alpha80">#cc000000</color>
+
+    <!--灰色-各种等级透明度-->
+    <color name="gray_32">#ff323232</color>
+    <color name="gray_33">#ff333333</color>
+    <color name="gray_35">#ff353535</color>
+    <color name="gray_66">#ff666666</color>
+    <color name="gray_7f">#ff7f7f7f</color>
+    <color name="gray_80">#ff808080</color>
+    <color name="gray_88">#ff888888</color>
+    <color name="gray_8832">#88323232</color>
+    <color name="gray_AD">#ffadadad</color>
+    <color name="gray_C8">#ffc8c8c8</color>
+    <color name="gray_D5">#ffd5d5d5</color>
+    <color name="gray_E6">#ffe6e6e6</color>
+    <color name="gray_cc">#ffcccccc</color>
+    <color name="gray_d9">#ffd9d9d9</color>
+    <color name="gray_f0">#fff0f0f0</color>
+    <color name="gray_f2">#fff2f2f2</color>
+    <color name="gray_f3">#fff3f3f3</color>
+    <color name="gray_f4">#fff4f4f4</color>
+    <color name="gray_f6">#fff6f6f6</color>
+    <color name="gray_fc">#fffcfcfc</color>
+    <color name="gray_e0">#e0e0e0</color>
+
+    <!--消息中心-->
+    <color name="red_common">#dfca2c32</color>
+    <color name="message_detail_rb_red">#f74248</color>
+    <color name="message_detail_divider">#e1e3e6</color>
+</resources>

+ 240 - 0
common/src/main/res/values/dimens.xml

@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <!--字体大小-->
+    <dimen name="text_size_9sp">9.0sp</dimen>
+    <dimen name="text_size_10sp">10.0sp</dimen>
+    <dimen name="text_size_11sp">11.0sp</dimen>
+    <dimen name="text_size_12sp">12.0sp</dimen>
+    <dimen name="text_size_13sp">13.0sp</dimen>
+    <dimen name="text_size_14sp">14.0sp</dimen>
+    <dimen name="text_size_15sp">15.0sp</dimen>
+    <dimen name="text_size_16sp">16.0sp</dimen>
+    <dimen name="text_size_17sp">17.0sp</dimen>
+    <dimen name="text_size_18sp">18.0sp</dimen>
+    <dimen name="text_size_19sp">19.0sp</dimen>
+    <dimen name="text_size_20sp">20.0sp</dimen>
+    <dimen name="text_size_21sp">21.0sp</dimen>
+    <dimen name="text_size_22sp">22.0sp</dimen>
+    <dimen name="text_size_23sp">23.0sp</dimen>
+    <dimen name="text_size_24sp">24.0sp</dimen>
+    <dimen name="text_size_28sp">28.0sp</dimen>
+    <dimen name="text_size_31sp">31.0sp</dimen>
+    <dimen name="text_size_37sp">37.0sp</dimen>
+
+    <!--margin大小 0-100之间-->
+    <dimen name="size_0.5dp">0.5dip</dimen>
+    <dimen name="size_1dp">1.0dip</dimen>
+    <dimen name="size_1.5dp">1.5dip</dimen>
+    <dimen name="size_2dp">2.0dip</dimen>
+    <dimen name="size_2.5dp">2.5dip</dimen>
+    <dimen name="size_3dp">3.0dip</dimen>
+    <dimen name="size_3.5dp">3.5dip</dimen>
+    <dimen name="size_4dp">4.0dip</dimen>
+    <dimen name="size_5dp">5.0dip</dimen>
+    <dimen name="size_6dp">6.0dip</dimen>
+    <dimen name="size_7dp">7.0dip</dimen>
+    <dimen name="size_7.5dp">7.5dip</dimen>
+    <dimen name="size_8dp">8.0dip</dimen>
+    <dimen name="size_9dp">9.0dip</dimen>
+    <dimen name="size_10dp">10.0dip</dimen>
+    <dimen name="size_11dp">11.0dip</dimen>
+    <dimen name="size_12dp">12.0dip</dimen>
+    <dimen name="size_12.5dp">12.5dip</dimen>
+    <dimen name="size_13dp">13.0dip</dimen>
+    <dimen name="size_14dp">14.0dip</dimen>
+    <dimen name="size_14.5dp">14.5dip</dimen>
+    <dimen name="size_15dp">15.0dip</dimen>
+    <dimen name="size_16dp">16.0dip</dimen>
+    <dimen name="size_17dp">17.0dip</dimen>
+    <dimen name="size_18dp">18.0dip</dimen>
+    <dimen name="size_19dp">19.0dip</dimen>
+    <dimen name="size_20.5dp">20.5dip</dimen>
+    <dimen name="size_20dp">20.0dip</dimen>
+    <dimen name="size_21dp">21.0dip</dimen>
+    <dimen name="size_22dp">22.0dip</dimen>
+    <dimen name="size_23dp">23.0dip</dimen>
+    <dimen name="size_24dp">24.0dip</dimen>
+    <dimen name="size_25dp">25.0dip</dimen>
+    <dimen name="size_26dp">26.0dip</dimen>
+    <dimen name="size_27dp">27.0dip</dimen>
+    <dimen name="size_28dp">28.0dip</dimen>
+    <dimen name="size_29dp">29.0dip</dimen>
+    <dimen name="size_30dp">30.0dip</dimen>
+    <dimen name="size_31dp">31.0dip</dimen>
+    <dimen name="size_32dp">32.0dip</dimen>
+    <dimen name="size_33dp">33.0dip</dimen>
+    <dimen name="size_34dp">34.0dip</dimen>
+    <dimen name="size_35dp">35.0dip</dimen>
+    <dimen name="size_36dp">36.0dip</dimen>
+    <dimen name="size_37dp">37.0dip</dimen>
+    <dimen name="size_38dp">38.0dip</dimen>
+    <dimen name="size_39dp">39.0dip</dimen>
+    <dimen name="size_40dp">40.0dip</dimen>
+    <dimen name="size_41dp">41.0dip</dimen>
+    <dimen name="size_42dp">42.0dip</dimen>
+    <dimen name="size_43dp">43.0dip</dimen>
+    <dimen name="size_44dp">44.0dip</dimen>
+    <dimen name="size_45dp">45.0dip</dimen>
+    <dimen name="size_46dp">46.0dip</dimen>
+    <dimen name="size_47dp">47.0dip</dimen>
+    <dimen name="size_48dp">48.0dip</dimen>
+    <dimen name="size_49dp">49.0dip</dimen>
+    <dimen name="size_50dp">50.0dip</dimen>
+    <dimen name="size_51dp">51.0dip</dimen>
+    <dimen name="size_52dp">52.0dip</dimen>
+    <dimen name="size_53dp">53.0dip</dimen>
+    <dimen name="size_54dp">54.0dip</dimen>
+    <dimen name="size_55dp">55.0dip</dimen>
+    <dimen name="size_56dp">56.0dip</dimen>
+    <dimen name="size_57dp">57.0dip</dimen>
+    <dimen name="size_58dp">58.0dip</dimen>
+    <dimen name="size_59dp">59.0dip</dimen>
+    <dimen name="size_60dp">60.0dip</dimen>
+    <dimen name="size_61dp">61.0dip</dimen>
+    <dimen name="size_62dp">62.0dip</dimen>
+    <dimen name="size_63dp">63.0dip</dimen>
+    <dimen name="size_64dp">64.0dip</dimen>
+    <dimen name="size_65dp">65.0dip</dimen>
+    <dimen name="size_66dp">66.0dip</dimen>
+    <dimen name="size_67dp">67.0dip</dimen>
+    <dimen name="size_68dp">68.0dip</dimen>
+    <dimen name="size_69dp">69.0dip</dimen>
+    <dimen name="size_70dp">70.0dip</dimen>
+    <dimen name="size_71dp">71.0dip</dimen>
+    <dimen name="size_72dp">72.0dip</dimen>
+    <dimen name="size_73dp">73.0dip</dimen>
+    <dimen name="size_74dp">74.0dip</dimen>
+    <dimen name="size_75dp">75.0dip</dimen>
+    <dimen name="size_76dp">76.0dip</dimen>
+    <dimen name="size_77dp">77.0dip</dimen>
+    <dimen name="size_78dp">78.0dip</dimen>
+    <dimen name="size_79dp">79.0dip</dimen>
+    <dimen name="size_80dp">80.0dip</dimen>
+    <dimen name="size_81dp">81.0dip</dimen>
+    <dimen name="size_82dp">82.0dip</dimen>
+    <dimen name="size_83dp">83.0dip</dimen>
+    <dimen name="size_84dp">84.0dip</dimen>
+    <dimen name="size_85dp">85.0dip</dimen>
+    <dimen name="size_86dp">86.0dip</dimen>
+    <dimen name="size_87dp">87.0dip</dimen>
+    <dimen name="size_88dp">88.0dip</dimen>
+    <dimen name="size_89dp">89.0dip</dimen>
+    <dimen name="size_90dp">90.0dip</dimen>
+    <dimen name="size_91dp">91.0dip</dimen>
+    <dimen name="size_92dp">92.0dip</dimen>
+    <dimen name="size_93dp">93.0dip</dimen>
+    <dimen name="size_94dp">94.0dip</dimen>
+    <dimen name="size_95dp">95.0dip</dimen>
+    <dimen name="size_96dp">96.0dip</dimen>
+    <dimen name="size_97dp">97.0dip</dimen>
+    <dimen name="size_98dp">98.0dip</dimen>
+    <dimen name="size_99dp">99.0dip</dimen>
+
+    <!--margin大小 100以上-->
+    <dimen name="size_100dp">100.0dip</dimen>
+    <dimen name="size_101dp">101.0dip</dimen>
+    <dimen name="size_102dp">102.0dip</dimen>
+    <dimen name="size_103dp">103.0dip</dimen>
+    <dimen name="size_104dp">104.0dip</dimen>
+    <dimen name="size_105dp">105.0dip</dimen>
+    <dimen name="size_106dp">106.0dip</dimen>
+    <dimen name="size_107dp">107.0dip</dimen>
+    <dimen name="size_108dp">108.0dip</dimen>
+    <dimen name="size_109dp">109.0dip</dimen>
+    <dimen name="size_110dp">110.0dip</dimen>
+    <dimen name="size_111dp">111.0dip</dimen>
+    <dimen name="size_112dp">112.0dip</dimen>
+    <dimen name="size_113dp">113.0dip</dimen>
+    <dimen name="size_114dp">114.0dip</dimen>
+    <dimen name="size_115dp">115.0dip</dimen>
+    <dimen name="size_116dp">116.0dip</dimen>
+    <dimen name="size_117dp">117.0dip</dimen>
+    <dimen name="size_118dp">118.0dip</dimen>
+    <dimen name="size_119dp">119.0dip</dimen>
+    <dimen name="size_120dp">120.0dip</dimen>
+    <dimen name="size_121dp">121.0dip</dimen>
+    <dimen name="size_122dp">122.0dip</dimen>
+    <dimen name="size_123dp">123.0dip</dimen>
+    <dimen name="size_124dp">124.0dip</dimen>
+    <dimen name="size_125dp">125.0dip</dimen>
+    <dimen name="size_126dp">126.0dip</dimen>
+    <dimen name="size_127dp">127.0dip</dimen>
+    <dimen name="size_128dp">128.0dip</dimen>
+    <dimen name="size_129dp">129.0dip</dimen>
+    <dimen name="size_130dp">130.0dip</dimen>
+    <dimen name="size_131dp">131.0dip</dimen>
+    <dimen name="size_132dp">132.0dip</dimen>
+    <dimen name="size_133dp">133.0dip</dimen>
+    <dimen name="size_134dp">134.0dip</dimen>
+    <dimen name="size_135dp">135.0dip</dimen>
+    <dimen name="size_136dp">136.0dip</dimen>
+    <dimen name="size_137dp">137.0dip</dimen>
+    <dimen name="size_138dp">138.0dip</dimen>
+    <dimen name="size_139dp">139.0dip</dimen>
+    <dimen name="size_140dp">140.0dip</dimen>
+    <dimen name="size_141dp">141.0dip</dimen>
+    <dimen name="size_142dp">142.0dip</dimen>
+    <dimen name="size_143dp">143.0dip</dimen>
+    <dimen name="size_144dp">144.0dip</dimen>
+    <dimen name="size_145dp">145.0dip</dimen>
+    <dimen name="size_146dp">146.0dip</dimen>
+    <dimen name="size_147dp">147.0dip</dimen>
+    <dimen name="size_148dp">148.0dip</dimen>
+    <dimen name="size_149dp">149.0dip</dimen>
+    <dimen name="size_150dp">150.0dip</dimen>
+    <dimen name="size_151dp">151.0dip</dimen>
+    <dimen name="size_152dp">152.0dip</dimen>
+    <dimen name="size_153dp">153.0dip</dimen>
+    <dimen name="size_154dp">154.0dip</dimen>
+    <dimen name="size_155dp">155.0dip</dimen>
+    <dimen name="size_156dp">156.0dip</dimen>
+    <dimen name="size_157dp">157.0dip</dimen>
+    <dimen name="size_158dp">158.0dip</dimen>
+    <dimen name="size_159dp">159.0dip</dimen>
+    <dimen name="size_160dp">160.0dip</dimen>
+    <dimen name="size_161dp">161.0dip</dimen>
+    <dimen name="size_162dp">162.0dip</dimen>
+    <dimen name="size_163dp">163.0dip</dimen>
+    <dimen name="size_164dp">164.0dip</dimen>
+    <dimen name="size_165dp">165.0dip</dimen>
+    <dimen name="size_166dp">166.0dip</dimen>
+    <dimen name="size_167dp">167.0dip</dimen>
+    <dimen name="size_168dp">168.0dip</dimen>
+    <dimen name="size_169dp">169.0dip</dimen>
+    <dimen name="size_170dp">170.0dip</dimen>
+    <dimen name="size_171dp">171.0dip</dimen>
+    <dimen name="size_172dp">172.0dip</dimen>
+    <dimen name="size_173dp">173.0dip</dimen>
+    <dimen name="size_174dp">174.0dip</dimen>
+    <dimen name="size_175dp">175.0dip</dimen>
+    <dimen name="size_176dp">176.0dip</dimen>
+    <dimen name="size_177dp">177.0dip</dimen>
+    <dimen name="size_178dp">178.0dip</dimen>
+    <dimen name="size_179dp">179.0dip</dimen>
+    <dimen name="size_180dp">180.0dip</dimen>
+    <dimen name="size_181dp">181.0dip</dimen>
+    <dimen name="size_182dp">182.0dip</dimen>
+    <dimen name="size_183dp">183.0dip</dimen>
+    <dimen name="size_184dp">184.0dip</dimen>
+    <dimen name="size_185dp">185.0dip</dimen>
+    <dimen name="size_186dp">186.0dip</dimen>
+    <dimen name="size_187dp">187.0dip</dimen>
+    <dimen name="size_188dp">188.0dip</dimen>
+    <dimen name="size_189dp">189.0dip</dimen>
+    <dimen name="size_190dp">190.0dip</dimen>
+    <dimen name="size_191dp">191.0dip</dimen>
+    <dimen name="size_192dp">192.0dip</dimen>
+    <dimen name="size_193dp">193.0dip</dimen>
+    <dimen name="size_194dp">194.0dip</dimen>
+    <dimen name="size_195dp">195.0dip</dimen>
+    <dimen name="size_196dp">196.0dip</dimen>
+    <dimen name="size_197dp">197.0dip</dimen>
+    <dimen name="size_198dp">198.0dip</dimen>
+    <dimen name="size_199dp">199.0dip</dimen>
+    <dimen name="size_200dp">200.0dip</dimen>
+    <dimen name="size_240dp">240.0dip</dimen>
+    <dimen name="size_260dp">260.0dip</dimen>
+
+
+</resources>

+ 6 - 0
common/src/main/res/values/strings.xml

@@ -0,0 +1,6 @@
+<resources>
+    <string name="app_name">Common</string>
+
+    <string name="current_internet_invalid">当前网络未连接</string>
+
+</resources>

+ 51 - 0
common/src/main/res/values/styles.xml

@@ -0,0 +1,51 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="Base.AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimary</item>
+        <item name="colorAccent">@color/colorPrimary</item>
+
+    </style>
+
+    <style name="AppTheme" parent="Base.AppTheme" />
+
+    <!--无ActionBar的AppTheme-->
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+    </style>
+
+    <!--启动页样式及窗口背景-->
+    <!--<style name="SplashTheme" parent="TransparentStatusBarTheme">-->
+    <!--<item name="android:windowBackground">@drawable/bg_launch</item>-->
+    <!--<item name="android:windowFullscreen">false</item>-->
+    <!--</style>-->
+
+
+    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+    <style name="Toolbar" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+    <!--全屏-->
+    <style name="FullscreenTheme" parent="AppTheme.NoActionBar">
+        <item name="android:windowActionBarOverlay">true</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowBackground">@null</item>
+    </style>
+
+    <!-- 自定义loading dialog -->
+    <style name="ProgressDialogTheme" parent="Theme.AppCompat.Light.Dialog">
+        <item name="android:windowFrame">@null</item><!--边框-->
+        <item name="android:windowNoTitle">true</item><!--无标题-->
+        <item name="android:windowBackground">@android:color/transparent</item><!--背景透明-->
+        <item name="android:windowIsTranslucent">false</item><!--半透明-->
+        <item name="android:windowIsFloating">true</item><!--是否浮现在activity之上-->
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:backgroundDimEnabled">false</item><!--模糊-->
+    </style>
+
+</resources>

+ 1 - 0
girls/.gitignore

@@ -0,0 +1 @@
+/build

+ 50 - 0
girls/build.gradle

@@ -0,0 +1,50 @@
+if (isModule.toBoolean()) {
+    apply plugin: 'com.android.application'
+} else {
+    apply plugin: 'com.android.library'
+}
+
+apply plugin: 'com.neenbedankt.android-apt'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode rootProject.ext.versionCode
+        versionName rootProject.ext.versionName
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    sourceSets {
+        main {
+            if (isModule.toBoolean()) {
+                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
+            } else {
+                manifest.srcFile 'src/main/release/AndroidManifest.xml'
+                //release模式下排除debug文件夹中的所有Java文件
+                java {
+                    exclude 'debug/**'
+                }
+            }
+        }
+    }
+    //设置了resourcePrefix值后,所有的资源名必须以指定的字符串做前缀,否则会报错。
+    //但是resourcePrefix这个值只能限定xml里面的资源,并不能限定图片资源,所有图片资源仍然需要手动去修改资源名。
+    //resourcePrefix "girls_"
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile project(':common')
+    //router
+    apt "com.github.mzule.activityrouter:compiler:$rootProject.aptCompilerVersion"
+}

+ 17 - 0
girls/proguard-rules.pro

@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive 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:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

+ 23 - 0
girls/src/main/debug/AndroidManifest.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.girls">
+
+    <application
+        android:name="debug.GirlsApplication"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity
+            android:name=".GirlsActivity"
+            android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

+ 14 - 0
girls/src/main/java/com/guiying/girls/Girls.java

@@ -0,0 +1,14 @@
+package com.guiying.girls;
+
+import com.github.mzule.activityrouter.annotation.Module;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2017/2/15 16:34
+ * @version V1.2.0
+ * @name Girls
+ */
+@Module("girls")
+public class Girls {
+}

+ 16 - 0
girls/src/main/java/com/guiying/girls/GirlsActivity.java

@@ -0,0 +1,16 @@
+package com.guiying.girls;
+
+import android.os.Bundle;
+
+import com.github.mzule.activityrouter.annotation.Router;
+import com.guiying.common.base.BaseActivity;
+
+@Router("girls")
+public class GirlsActivity extends BaseActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_girls);
+    }
+}

+ 18 - 0
girls/src/main/java/debug/GirlsApplication.java

@@ -0,0 +1,18 @@
+package debug;
+
+import com.guiying.common.base.BaseApplication;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2017/2/15 20:09
+ * @version V1.2.0
+ * @name GirlsApplication
+ */
+public class GirlsApplication extends BaseApplication {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+}

+ 10 - 0
girls/src/main/release/AndroidManifest.xml

@@ -0,0 +1,10 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.girls">
+
+    <application android:theme="@style/AppTheme">
+        <activity
+            android:name=".GirlsActivity"
+            android:screenOrientation="portrait" />
+    </application>
+
+</manifest>

+ 13 - 0
girls/src/main/res/layout/activity_girls.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/activity_girls"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.guiying.girls.GirlsActivity">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!" />
+</RelativeLayout>

BIN
girls/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
girls/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
girls/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
girls/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 4 - 0
girls/src/main/res/values/colors.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+</resources>

+ 4 - 0
girls/src/main/res/values/dimens.xml

@@ -0,0 +1,4 @@
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+
+</resources>

+ 3 - 0
girls/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Girls</string>
+</resources>

+ 2 - 1
gradle.properties

@@ -20,5 +20,6 @@ org.gradle.parallel=true
 
 localBuildToolsVersion=25.0.1
 localGradlePluginVersion=2.2.3
+
 # 每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
-isModule=true
+isModule=false

+ 1 - 0
news/.gitignore

@@ -0,0 +1 @@
+/build

+ 50 - 0
news/build.gradle

@@ -0,0 +1,50 @@
+if (isModule.toBoolean()) {
+    apply plugin: 'com.android.application'
+} else {
+    apply plugin: 'com.android.library'
+}
+
+apply plugin: 'com.neenbedankt.android-apt'
+
+android {
+    compileSdkVersion rootProject.ext.compileSdkVersion
+    buildToolsVersion rootProject.ext.buildToolsVersion
+
+    defaultConfig {
+        minSdkVersion rootProject.ext.minSdkVersion
+        targetSdkVersion rootProject.ext.targetSdkVersion
+        versionCode rootProject.ext.versionCode
+        versionName rootProject.ext.versionName
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    sourceSets {
+        main {
+            if (isModule.toBoolean()) {
+                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
+            } else {
+                manifest.srcFile 'src/main/release/AndroidManifest.xml'
+                //release模式下排除debug文件夹中的所有Java文件
+                java {
+                    exclude 'debug/**'
+                }
+            }
+        }
+    }
+    //设置了resourcePrefix值后,所有的资源名必须以指定的字符串做前缀,否则会报错。
+    //但是resourcePrefix这个值只能限定xml里面的资源,并不能限定图片资源,所有图片资源仍然需要手动去修改资源名。
+    //resourcePrefix "girls_"
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    compile project(':common')
+    //router
+    apt "com.github.mzule.activityrouter:compiler:$rootProject.aptCompilerVersion"
+}

+ 17 - 0
news/proguard-rules.pro

@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive 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:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

+ 22 - 0
news/src/main/debug/AndroidManifest.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.news">
+
+    <application
+        android:name="debug.NewsApplication"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity
+            android:name=".NewsActivity"
+            android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>

+ 15 - 0
news/src/main/java/com/guiying/news/News.java

@@ -0,0 +1,15 @@
+package com.guiying.news;
+
+import com.github.mzule.activityrouter.annotation.Module;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2017/2/15 16:31
+ * @version V1.2.0
+ * @name news
+ */
+
+@Module("news")
+public class News {
+}

+ 16 - 0
news/src/main/java/com/guiying/news/NewsActivity.java

@@ -0,0 +1,16 @@
+package com.guiying.news;
+
+import android.os.Bundle;
+
+import com.github.mzule.activityrouter.annotation.Router;
+import com.guiying.common.base.BaseActivity;
+
+@Router("news")
+public class NewsActivity extends BaseActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_news);
+    }
+}

+ 18 - 0
news/src/main/java/debug/NewsApplication.java

@@ -0,0 +1,18 @@
+package debug;
+
+import com.guiying.common.base.BaseApplication;
+
+/**
+ * <p>类说明</p>
+ *
+ * @author 张华洋 2017/2/15 20:11
+ * @version V1.2.0
+ * @name NewsApplication
+ */
+public class NewsApplication extends BaseApplication {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+}

+ 10 - 0
news/src/main/release/AndroidManifest.xml

@@ -0,0 +1,10 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.guiying.news">
+
+    <application android:theme="@style/AppTheme">
+        <activity
+            android:name=".NewsActivity"
+            android:screenOrientation="portrait" />
+    </application>
+
+</manifest>

+ 13 - 0
news/src/main/res/layout/activity_news.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/activity_news"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.guiying.news.NewsActivity">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Hello World!" />
+</RelativeLayout>

BIN
news/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
news/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
news/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
news/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 4 - 0
news/src/main/res/values/colors.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+</resources>

+ 4 - 0
news/src/main/res/values/dimens.xml

@@ -0,0 +1,4 @@
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+
+</resources>

+ 3 - 0
news/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">News</string>
+</resources>

+ 4 - 2
settings.gradle

@@ -1,2 +1,4 @@
-include ':app'
-
+include ':app',
+        ':girls',
+        ':news',
+        ':common'