Commit b13432ea by 罗翻

去除library

parent 0bfe590d
Showing with 15 additions and 3175 deletions
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
<option value="$PROJECT_DIR$/annotation_lib" /> <option value="$PROJECT_DIR$/annotation_lib" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/apt" /> <option value="$PROJECT_DIR$/apt" />
<option value="$PROJECT_DIR$/baselibrary" />
</set> </set>
</option> </option>
<option name="resolveModulePerSourceSet" value="false" /> <option name="resolveModulePerSourceSet" value="false" />
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<module fileurl="file://$PROJECT_DIR$/annotation_lib/annotation_lib.iml" filepath="$PROJECT_DIR$/annotation_lib/annotation_lib.iml" /> <module fileurl="file://$PROJECT_DIR$/annotation_lib/annotation_lib.iml" filepath="$PROJECT_DIR$/annotation_lib/annotation_lib.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/apt/apt.iml" filepath="$PROJECT_DIR$/apt/apt.iml" /> <module fileurl="file://$PROJECT_DIR$/apt/apt.iml" filepath="$PROJECT_DIR$/apt/apt.iml" />
<module fileurl="file://$PROJECT_DIR$/baselibrary/baselibrary.iml" filepath="$PROJECT_DIR$/baselibrary/baselibrary.iml" />
<module fileurl="file://$PROJECT_DIR$/dayu.iml" filepath="$PROJECT_DIR$/dayu.iml" /> <module fileurl="file://$PROJECT_DIR$/dayu.iml" filepath="$PROJECT_DIR$/dayu.iml" />
</modules> </modules>
</component> </component>
......
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 16
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
compileOptions {
targetCompatibility 1.8
sourceCompatibility 1.8
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:design:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
//mutidex
compile 'com.android.support:multidex:1.0.2'
//ARouter
// compile "com.alibaba:arouter-api:$arouter_api_version"
// annotationProcessor "c、om.alibaba:arouter-compiler:$arouter_api_version"
//eventBus
compile 'org.greenrobot:eventbus:3.1.1'
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile 'io.reactivex.rxjava2:rxjava:2.0.5'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
annotationProcessor project(':apt')
compile project(':annotation_lib')
}
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\AndroidSDK/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 *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#----------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------
#-------------------------------------------基本不用动区域--------------------------------------------
#---------------------------------基本指令区----------------------------------
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-adaptclassstrings
-keepattributes InnerClasses, EnclosingMethod, Signature, *Annotation*,Exceptions
-keepattributes SourceFile,LineNumberTable
#----------------------------------------------------------------------------
#---------------------------------默认保留区---------------------------------
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep class android.support.** {*;}
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keep class **.R$* {
*;
}
-keepclassmembers class * {
void *(**On*Event);
}
#----------------------------------------------------------------------------
#---------------------------------webview------------------------------------
-keepclassmembers class fqcn.of.javascript.interface.for.Webview {
public *;
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, jav.lang.String);
}
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}
#-------------------------------------------定制化区域----------------------------------------------
#---------------------------------1.实体类---------------------------------
-keep class com.dayu.bigfish.bean.** { *; }
-keep class com.dayu.bigfish.base.** { *; }
#-------------------------------------------------------------------------
#---------------------------------2.第三方包-------------------------------
#retrofit
-dontwarn okio.**
-dontwarn com.google.**
-dontwarn javax.annotation.**
-dontwarn javax.annotation.Nullable
-dontwarn javax.annotation.ParametersAreNonnullByDefault
-dontwarn okio.**
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-dontwarn javax.inject.**
# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
#############################################
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
# Gson
-keep class com.google.gson.stream.** { *; }
-keepattributes EnclosingMethod
#PictureSelector 2.0
-keep class com.luck.picture.lib.** { *; }
-dontwarn com.yalantis.ucrop**
-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }
#glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.AppGlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
#BaseRecyclerViewAdapterHelper
-keep class com.chad.library.adapter.** {
*;
}
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
-keepclassmembers class **$** extends com.chad.library.adapter.base.BaseViewHolder {
<init>(...);
}
#greendao
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties
# If you do not use SQLCipher:
-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
-dontwarn rx.**
#环信
-keep class com.hyphenate.** {*;}
-dontwarn com.hyphenate.**
#高德地图
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}
#友盟
-keepclassmembers class * {
public <init> (org.json.JSONObject);
}
-keep public class com.dayu.bigfish.R$*{
public static final int *;
}
-keep class com.umeng.error.UMError{ public *; }
-keep class com.umeng.error.UMErrorCatch{public *; }
-keep class com.umeng.error.UMErrorDataManger{ public *; }
-keep class com.umeng.error.BatteryUtils{ public *; }
#eventbus
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
#---------------------------------3.与js互相调用的类------------------------
#---------------------------------4.反射相关的类和方法-----------------------
package com.dayu.base;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.dayu.base.test", appContext.getPackageName());
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dayu.base" />
package com.dayu.base.api;
import android.net.ParseException;
import com.dayu.base.R;
import com.dayu.base.common.BaseApplication;
import com.google.gson.JsonParseException;
import org.apache.http.conn.ConnectTimeoutException;
import org.json.JSONException;
import java.net.ConnectException;
import retrofit2.HttpException;
import static com.dayu.base.api.APIException.ERROR.SERVER_ERROR;
/**
* Created by luofan on 2017/11/15.
*/
public class APIException {
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
public static final int REQUEST_TIMEOUT = 408;
public static final int INTERNAL_SERVER_ERROR = 500;
public static final int BAD_GATEWAY = 502;
public static final int SERVICE_UNAVAILABLE = 503;
public static final int GATEWAY_TIMEOUT = 504;
public static ResponeThrowable APIException(Throwable e) {
ResponeThrowable ex;
if (e instanceof HttpException) {
HttpException httpException = (HttpException) e;
ex = new ResponeThrowable(e, ERROR.HTTP_ERROR);
switch (httpException.code()) {
case FORBIDDEN:
ex.message = BaseApplication.getContext().getString(R.string.error_token);
ex.code = FORBIDDEN;
break;
case UNAUTHORIZED:
case NOT_FOUND:
case REQUEST_TIMEOUT:
ex.message = BaseApplication.getContext().getString(R.string.error_time_out);
ex.code = REQUEST_TIMEOUT;
break;
case GATEWAY_TIMEOUT:
case INTERNAL_SERVER_ERROR:
ex.message = BaseApplication.getContext().getString(R.string.error_connect);
ex.code = INTERNAL_SERVER_ERROR;
break;
case BAD_GATEWAY:
case SERVICE_UNAVAILABLE:
default:
ex.message = BaseApplication.getContext().getString(R.string.error_internet);
break;
}
return ex;
} else if (e instanceof ServerException) {
ServerException resultException = (ServerException) e;
ex = new ResponeThrowable(resultException, SERVER_ERROR, resultException.code);
ex.message = resultException.message;
return ex;
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
ex = new ResponeThrowable(e, ERROR.PARSE_ERROR);
ex.message = BaseApplication.getContext().getString(R.string.error_parse);
return ex;
} else if (e instanceof ConnectException) {
ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR);
ex.message = BaseApplication.getContext().getString(R.string.error_connect);
return ex;
} else if (e instanceof javax.net.ssl.SSLHandshakeException) {
ex = new ResponeThrowable(e, ERROR.SSL_ERROR);
ex.message = BaseApplication.getContext().getString(R.string.error_ssl);
return ex;
} else if (e instanceof ConnectTimeoutException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = BaseApplication.getContext().getString(R.string.error_time_out);
return ex;
} else if (e instanceof java.net.SocketTimeoutException) {
ex = new ResponeThrowable(e, ERROR.TIMEOUT_ERROR);
ex.message = BaseApplication.getContext().getString(R.string.error_time_out);
return ex;
} else {
ex = new ResponeThrowable(e, ERROR.UNKNOWN);
ex.message = BaseApplication.getContext().getString(R.string.error_unknow);
return ex;
}
}
/**
* 约定异常
*/
class ERROR {
/**
* 接口返回错误
*/
public static final int SERVER_ERROR = 999;
/**
* 未知错误
*/
public static final int UNKNOWN = 1000;
/**
* 解析错误
*/
public static final int PARSE_ERROR = 1001;
/**
* 网络错误
*/
public static final int NETWORD_ERROR = 1002;
/**
* 协议出错
*/
public static final int HTTP_ERROR = 1003;
/**
* 证书出错
*/
public static final int SSL_ERROR = 1005;
/**
* 连接超时
*/
public static final int TIMEOUT_ERROR = 1006;
}
public static class ResponeThrowable extends Exception {
public int code;
public String message;
public String subCode;
public ResponeThrowable(Throwable throwable, int code) {
super(throwable);
this.code = code;
}
public ResponeThrowable(Throwable throwable, int code, String subCode) {
super(throwable);
this.code = code;
this.subCode = subCode;
}
}
}
package com.dayu.base.api;
import com.dayu.base.api.protocol.BaseResponse;
import com.dayu.base.common.Constants;
import com.dayu.base.utils.LogUtils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Created by luofan on 2017/11/09.
*/
public class Api {
private static Retrofit mRetrofit;
private static final int DEFAULT_TIMEOUT = 60;
private static Retrofit mDownloadRetrofit;
/**
* 普通retrofit.
*
* @return
*/
public static <T> T getService(Class<T> cls) {
return getmRetrofit().create(cls);
}
/**
* 下载的retrofit.
*
* @return
*/
public static <T> T getDownloadService(Class<T> cls) {
return getmDownloadRetrofit().create(cls);
}
private static Retrofit getmRetrofit() {
if (mRetrofit == null) {
mRetrofit = getRetrofit("");
}
return mRetrofit;
}
private static Retrofit getmDownloadRetrofit() {
if (mDownloadRetrofit == null) {
mDownloadRetrofit = getRetrofit("download");
}
return mDownloadRetrofit;
}
private static Retrofit getRetrofit(String type) {
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(m -> LogUtils.i("request", m));
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
TokenInterceptord tokenInterceptord = new TokenInterceptord();
OkHttpClient.Builder build = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(tokenInterceptord)
.readTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS)
.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
if ("download".equals(type)) {
build.addNetworkInterceptor(chain -> {
Response response = chain.proceed(chain.request());
return response
.newBuilder()
.body(new FileResponseBody(response.body()))//将自定义的ResposeBody设置给它
.build();
});
}
return new Retrofit.Builder()
.client(build.build())
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
/**
* 统一加上token.
*/
public static class TokenInterceptord implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// UserInfo userInfo = UserManager.getInstance().getUser();
// if (userInfo != null) {
// String token = userInfo.getToken();
// if (!TextUtils.isEmpty(token) && !request.url().toString().contains(Constants.BASE_URL + Constants.LOGIN_URL)) {
// request = request.newBuilder()
// .header("token", token)
// .build();
// }
// }
return chain.proceed(request);
}
}
public static <T> ObservableTransformer<BaseResponse<T>, T> applySchedulers() {
return (ObservableTransformer<BaseResponse<T>, T>) transformer;
}
final static ObservableTransformer transformer = new ObservableTransformer() {
@Override
public ObservableSource apply(Observable upstream) {
return upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap((response) -> flatResponse((BaseResponse<Object>) response));
}
};
/**
* 对网络接口返回的Response进行分割操作
*
* @param response
* @param <T>
* @return
*/
public static <T> Observable<T> flatResponse(final BaseResponse<T> response) {
return Observable.create(e -> {
if (response.isSuccess()) {
if (!e.isDisposed()) {
e.onNext(response.getData());
}
} else {
if (!e.isDisposed()) {
e.onError(new ServerException(response.getSubCode(), response.getMsg()));
}
return;
}
if (!e.isDisposed()) {
e.onComplete();
}
});
}
}
package com.dayu.base.api;
import com.dayu.base.bean.event.DownloadBean;
import org.greenrobot.eventbus.EventBus;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
/**
* 下载文件
* Created by luofan on 2017/11/27.
*/
public class FileResponseBody extends ResponseBody {
private ResponseBody responseBody;
private BufferedSource bufferedSource;
public FileResponseBody(ResponseBody responseBody) {
this.responseBody = responseBody;
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
// read() returns the number of bytes read, or -1 if this source is exhausted.
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
EventBus.getDefault().post(new DownloadBean(contentLength(), totalBytesRead));
return bytesRead;
}
};
}
}
package com.dayu.base.api;
import com.dayu.base.R;
import com.dayu.base.common.BaseApplication;
import com.dayu.base.common.Constants;
/**
* Created by luofan on 2017/11/15.
* 错误码统一处理类.
*/
public class ServerException extends Exception {
public String code;
public String message;
public ServerException(String code, String message) {
this.code = code;
this.message = message;
processCode(code);
}
private void processCode(String code) {
if (code == null) {
message = BaseApplication.getContext().getString(R.string.get_info_failed);
return;
}
switch (code) {
/** order相关错误*/
case "ORDER0001":
message = BaseApplication.getContext().getString(R.string.order_not_exite);
break;
case "ORDER0002":
message = BaseApplication.getContext().getString(R.string.order_receive_already);
break;
case "ORDER0003":
message = BaseApplication.getContext().getString(R.string.order_cancle_not_receive);
break;
case "ORDER0004":
message = BaseApplication.getContext().getString(R.string.order_cancle_not_subcribe);
break;
case "ORDER0005":
message = BaseApplication.getContext().getString(R.string.order_cancle_not_begin);
break;
case "ORDER0006":
message = BaseApplication.getContext().getString(R.string.order_cancle_not_process);
break;
case "LOGISTICS0001":
message = BaseApplication.getContext().getString(R.string.query_logistics_failed);
break;
/** 用户相关错误*/
case "USER0002":
message = BaseApplication.getContext().getString(R.string.sms_code_unsend_or_expire);
break;
case "USER0004":
message = BaseApplication.getContext().getString(R.string.not_engineer);
break;
case "USER0005":
message = BaseApplication.getContext().getString(R.string.account_frozen);
break;
case "USER0020":
message = BaseApplication.getContext().getString(R.string.sms_code_unsend_or_expire);
break;
case "USER0021":
message = BaseApplication.getContext().getString(R.string.sms_code_error);
break;
case "USER0003":
case "USER0023":
message = BaseApplication.getContext().getString(R.string.engineer_acount_notavialibe);
break;
case "USER0024":
message = BaseApplication.getContext().getString(R.string.engineer_not_have_site);
break;
case "USER0062":
message = BaseApplication.getContext().getString(R.string.identity_is_error);
break;
case "USER0051":
message = BaseApplication.getContext().getString(R.string.not_audite_aviliable);
break;
/** 账户相关*/
case "SETTLEMENT0015":
message = Constants.NOT_SHOW;
break;
case "SETTLEMENT0001":
message = BaseApplication.getContext().getString(R.string.perameter_is_null);
break;
/**全局错误*/
case "GLOBAL0001":
message = "未知错误GLOBAL0001"; //参数错误
break;
case "GLOBAL0002":
message = "未知错误GLOBAL0002"; //格式化序列化错误
break;
case "GLOBAL0003":
message = "未知错误GLOBAL0003"; //业务错误
break;
case "GLOBAL0004":
message = "未知错误GLOBAL0004"; //全局错误
break;
case "GLOBAL0100":
message = "未知错误GLOBAL0100"; //远程调用失败
break;
case "GLOBAL0101":
message = "验证码错误"; //短信验证失败
break;
case "GLOBAL0102":
message = "未知错误GLOBAL0102"; // unknowable error type
break;
case "GLOBAL0103":
message = "验证码错误";
break;
case "GLOBAL0104":
message = "用户名和密码错误";
break;
case "GLOBAL0400":
message = "未知错误GLOBAL0400"; //Bad Request!
break;
case "GLOBAL0406":
message = "未知错误GLOBAL0406"; //not Acceptable
break;
case "GLOBAL0405":
message = "未知错误GLOBAL0405"; //Method Not Allowed
break;
case "GLOBAL0500":
message = "未知错误GLOBAL0500"; //Method Not Allowed
break;
case "GLOBAL1001":
message = "未知错误GLOBAL1001"; //空指针异常
break;
case "GLOBAL1002":
message = "未知错误GLOBAL1002"; //类型转换异
break;
case "GLOBAL1003":
message = "未知错误GLOBAL1003"; //IO异常
break;
case "GLOBAL1004":
message = "未知错误GLOBAL1004"; //未知方法异
break;
case "GLOBAL1005":
message = "未知错误GLOBAL1005"; //数据越界异
break;
default:
message = BaseApplication.getContext().getString(R.string.get_info_failed);
break;
}
}
}
package com.dayu.base.api.protocol;
import java.util.List;
/**
* 分页的basebean.
* Created by luofan on 2017/11/14.
*/
public class BasePageBean<T>{
private int pageNo;
private int pageSize;
private int totalRows;
private int totalPages;
private int previousPageNo;
private int nextPageNo;
private List<T> data;
public int getPageNo() {
return pageNo;
}
public void setPageNo(int pageNo) {
this.pageNo = pageNo;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getTotalRows() {
return totalRows;
}
public void setTotalRows(int totalRows) {
this.totalRows = totalRows;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
public int getPreviousPageNo() {
return previousPageNo;
}
public void setPreviousPageNo(int previousPageNo) {
this.previousPageNo = previousPageNo;
}
public int getNextPageNo() {
return nextPageNo;
}
public void setNextPageNo(int nextPageNo) {
this.nextPageNo = nextPageNo;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
}
package com.dayu.base.api.protocol;
/**
* Created by luofan on 2016/07/14.
*/
public class BaseResponse<T> {
private int code;
private String subCode;
private String msg;
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public boolean isSuccess() {
return code == 0;
}
public String getSubCode() {
return subCode;
}
public void setSubCode(String subCode) {
this.subCode = subCode;
}
}
package com.dayu.base.bean.event;
/**
* Created by luofan on 2017/11/27.
* 下载进度.
*/
public class DownloadBean {
private long total;
private long bytesReaded;
public DownloadBean(long total, long bytesReaded) {
this.total = total;
this.bytesReaded = bytesReaded;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public long getBytesReaded() {
return bytesReaded;
}
public void setBytesReaded(long bytesReaded) {
this.bytesReaded = bytesReaded;
}
}
package com.dayu.base.common;
import android.content.Context;
import android.support.multidex.MultiDex;
import android.support.multidex.MultiDexApplication;
/**
* Created by luofan
* on 2018/2/5.
*/
public class BaseApplication extends MultiDexApplication {
private static Context mContext;
@Override
public void onCreate() {
super.onCreate();
MultiDex.install(this);
}
public static Context getContext() {
return mContext;
}
public static synchronized BaseApplication context() {
return (BaseApplication) mContext;
}
}
package com.dayu.base.presenter;
import android.databinding.ObservableField;
/**
* Created by luofan on 2017/12/23.
*/
public abstract class BaseListPresenter<V> extends BasePresenter<V> {
/**
* 如果有下拉刷新,子类必须重写此方法.
*/
public void refresh() {
}
/**
* 如果有上拉加载,子类必须重写此方法.
*/
public void loadMore() {
}
/**
* 如果有头布局,子类重写此方法.
*
* @return 头布局数据源.
*/
public ObservableField<Object> getHeaderDatas() {
return null;
}
/**
*
* @return recy数据源.
*/
public abstract ObservableField<Object> getSourceDatas();
}
package com.dayu.base.presenter;
import android.util.Log;
import com.dayu.base.api.APIException;
import com.dayu.base.common.Constants;
import com.dayu.base.ui.BaseView;
import com.dayu.base.ui.activity.BaseActivity;
import com.dayu.base.ui.fragment.BaseFragment;
import com.dayu.base.utils.ProgressUtil;
import io.reactivex.Observer;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
/**
* Created by luofan on 17/11/02.
*/
public abstract class BasePresenter<V> {
protected static final String TAG = "BasePresenter";
protected V mView;
protected CompositeDisposable mComDisposable = new CompositeDisposable();
public void setView(V v) {
this.mView = v;
this.onAttached();
}
public abstract void onAttached();
public void onDetached() {
mComDisposable.dispose();
}
/**
* 创建观察者
*
* @param consumer
* @return
*/
public <M> Observer<M> baseObserver(final Consumer<? super M> consumer) {
return new Observer<M>() {
@Override
public void onSubscribe(Disposable d) {
mComDisposable.add(d);
}
@Override
public void onNext(M o) {
ProgressUtil.stopLoad();
try {
consumer.accept(o);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable e) {
ProgressUtil.stopLoad();
processException(e);
}
@Override
public void onComplete() {
}
};
}
/**
* 创建带错误的观察者
*
* @param consumer
* @return
*/
public <M> Observer<M> baseObserver(final Consumer<? super M> consumer, final Consumer<APIException.ResponeThrowable> tconsumer) {
return new Observer<M>() {
@Override
public void onSubscribe(Disposable d) {
mComDisposable.add(d);
}
@Override
public void onNext(M o) {
ProgressUtil.stopLoad();
try {
consumer.accept(o);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable e) {
Log.d("reguest+error", e.toString());
APIException.ResponeThrowable exception = processException(e);
ProgressUtil.stopLoad();
try {
tconsumer.accept(exception);
} catch (Exception e1) {
e1.printStackTrace();
}
}
@Override
public void onComplete() {
}
};
}
private APIException.ResponeThrowable processException(Throwable e) {
APIException.ResponeThrowable exception = APIException.APIException(e);
int code = exception.code;
String message = exception.message;
if (code == APIException.FORBIDDEN) {
showLoginDialog();
return exception;
}
if (!Constants.NOT_SHOW.equals(message) && mView instanceof BaseView) {
((BaseView) mView).showToast(message);
}
return exception;
}
/**
* 重新登录的提示框.
*/
private void showLoginDialog() {
if (mView instanceof BaseActivity) {
((BaseActivity) mView).showLoginDialog();
} else if (mView instanceof BaseFragment) {
((BaseFragment) mView).showLoginDialog();
}
}
public void dumpBack() {
if (mView instanceof BaseActivity) {
((BaseActivity) mView).dumpBack();
}
}
}
package com.dayu.base.ui;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.StringRes;
/**
* Created by luofan on 17/11/02.
*/
public interface BaseView {
void showToast(@StringRes int resId);
void showToast(String str);
void showDialog();
void showDialog(String str);
void hideDialog();
void dumpBack();
void dumpBack(int requestCode, Intent intent);
void startActivity(Class cls);
void startActivityForIntent(Intent intent);
void startActivity(Class<?> clz, Bundle bundle);
void startActvityAndFinish(Class<?> clz);
void startActivityForReult(Class<?> clz, int requestCode);
void startActivityForReult(Class<?> clz, Bundle bundle, int requestCode);
void startActivityAndFinish(Class<?> clz, Bundle bundle);
Bundle getBundle();
}
package com.dayu.base.ui.activity;
import android.content.Intent;
import android.databinding.ViewDataBinding;
import android.os.Bundle;
import com.dayu.base.R;
import com.dayu.base.common.Constants;
import com.dayu.base.presenter.BasePresenter;
import com.dayu.base.ui.BaseView;
import com.dayu.base.utils.InstanceUtil;
import com.dayu.base.utils.ProgressUtil;
import com.dayu.base.utils.ToastUtils;
import com.dayu.base.widgets.CustomDialog;
import java.lang.reflect.ParameterizedType;
/**
* Created by luofan on 17/11/02.
*/
public abstract class BaseActivity<P extends BasePresenter, B extends ViewDataBinding> extends BaseBindingActivity<B>
implements BaseView {
public P mPresenter;
private boolean isDialogShow = false;
@Override
protected void initPresenter() {
super.initPresenter();
if (this.getClass().getGenericSuperclass() instanceof ParameterizedType &&
((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments().length > 0) {
Class mPresenterClass = (Class) ((ParameterizedType) (this.getClass()
.getGenericSuperclass())).getActualTypeArguments()[0];
mPresenter = InstanceUtil.getInstance(mPresenterClass);
if (mPresenter != null) {
mPresenter.setView(this);
// mBind.setVariable(BR.presenter, mPresenter);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDetached();
}
}
public void showToast(String msg) {
ToastUtils.showShortToast(msg);
}
public void showToast(int resId) {
ToastUtils.showShortToast(resId);
}
public void showDialog() {
ProgressUtil.startLoad(this);
}
public void showDialog(String str) {
ProgressUtil.startLoad(this, str);
}
public void hideDialog() {
ProgressUtil.stopLoad();
}
public void dumpBack() {
mActivity.finish();
}
public void dumpBack(int requestCode, Intent intent) {
mActivity.setResult(requestCode, intent);
mActivity.finish();
}
public void startActivity(Class cls) {
mActivity.startActivity(new Intent(mActivity, cls));
}
public void startActivityForIntent(Intent intent) {
mActivity.startActivity(intent);
}
public void startActivity(Class<?> clz, Bundle bundle) {
Intent intent = new Intent(mActivity, clz);
if (bundle != null) {
intent.putExtra(Constants.BUNDLE, bundle);
}
mActivity.startActivity(intent);
}
public void startActivityForReult(Class<?> clz, Bundle bundle, int requestCode) {
Intent intent = new Intent(mActivity, clz);
if (bundle != null) {
intent.putExtra(Constants.BUNDLE, bundle);
}
mActivity.startActivityForResult(intent, requestCode);
}
public void startActivityForReult(Class<?> clz, int requestCode) {
mActivity.startActivityForResult(new Intent(mActivity, clz), requestCode);
}
public void startActvityAndFinish(Class<?> clz) {
mActivity.startActivity(new Intent(mActivity, clz));
mActivity.finish();
}
public void startActivityAndFinish(Class<?> clz, Bundle bundle) {
Intent intent = new Intent(mActivity, clz);
if (bundle != null) {
intent.putExtra(Constants.BUNDLE, bundle);
}
mActivity.startActivity(intent);
mActivity.finish();
}
public Bundle getBundle() {
return getIntent().getBundleExtra(Constants.BUNDLE);
}
/**
* token超时提示重新登录.
*/
public void showLoginDialog() {
if (isDialogShow) {
return;
}
CustomDialog mDialog = new CustomDialog(mActivity, R.style.CustomDialog, getString(R.string.login_state_no)
, (dialog, confirm) -> {
if (confirm) {
// EMClient.getInstance().logout(true);
// UserManager.getInstance().clearUserInfo();
// Intent intent = new Intent(mActivity, LoginActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
// mActivity.startActivity(intent);
}
dialog.dismiss();
isDialogShow = false;
});
mDialog.setTitle(getString(R.string.notice))
.setPositiveButton(getString(R.string.login_again))
.setOneButton(true);
mDialog.show();
isDialogShow = true;
}
}
package com.dayu.base.ui.activity;
import android.app.Activity;
import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public abstract class BaseBindingActivity<B extends ViewDataBinding> extends AppCompatActivity {
public Activity mActivity;
public B mBind;
protected String mClassName;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = getLayoutInflater().inflate(this.getLayoutId(), null, false);
mBind = DataBindingUtil.bind(rootView);
this.setContentView(rootView);
mActivity = this;
mClassName = mActivity.getClass().getSimpleName();
initPresenter();
initView();
}
protected void initPresenter() {
}
public abstract int getLayoutId();
public abstract void initView();
// @Override
// public void onResume() {
// super.onResume();
// if (!"MainActivity".equals(mActivity.getClass().getSimpleName())
// && !"OrderDetailsActivity".equals(mActivity.getClass().getSimpleName())) {
// MobclickAgent.onPageStart(mClassName);
// }
// MobclickAgent.onResume(mActivity);
// }
//
// @Override
// public void onPause() {
// super.onPause();
// if (!"MainActivity".equals(mActivity.getClass().getSimpleName())
// && !"OrderDetailsActivity".equals(mActivity.getClass().getSimpleName())) {
// MobclickAgent.onPageEnd(mClassName);
// }
// MobclickAgent.onPause(mActivity);
// }
}
package com.dayu.base.ui.fragment;
import android.app.Activity;
import android.content.Context;
import android.databinding.DataBindingUtil;
import android.databinding.ViewDataBinding;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.disposables.CompositeDisposable;
public abstract class BaseBindingFragment<B extends ViewDataBinding> extends Fragment {
public B mBind;
protected Activity mActivity;
private boolean isVisible; //是否可见状态
private boolean isPrepared; //标志位,View已经初始化完成。
private boolean isFirstLoad = true;
protected CompositeDisposable mDisposable = new CompositeDisposable();
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
onAttachToContext((Activity) context);
}
}
@SuppressWarnings("deprecation")
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
onAttachToContext(activity);
}
}
private void onAttachToContext(Activity context) {
mActivity = context;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isFirstLoad = true;
isPrepared = true;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(getLayoutId(), container, false);
mBind = DataBindingUtil.bind(view);
initPresenter();
initView();
return view;
}
public abstract void initView();
public abstract int getLayoutId();
protected void initPresenter() {
}
/**
* 如果是与ViewPager一起使用,调用的是setUserVisibleHint
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getUserVisibleHint()) {
isVisible = true;
//显示加载效果,延时
mDisposable.add(Observable.timer(300, TimeUnit.MILLISECONDS).subscribe(aLong -> onVisible()));
} else {
isVisible = false;
onInvisible();
}
}
/**
* 如果是通过FragmentTransaction的show和hide的方法来控制显示,调用的是onHiddenChanged.
* 若是初始就show的Fragment 为了触发该事件 需要先hide再show
*/
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (!hidden) {
isVisible = true;
onVisible();
} else {
isVisible = false;
onInvisible();
}
}
protected void onVisible() {
doInit();
}
protected void onInvisible() {
}
protected void doInit() {
if (!isPrepared || !isVisible || !isFirstLoad) {
return;
}
isFirstLoad = false;
lazyLoad();
}
protected void lazyLoad() {
}
@Override
public void onDetach() {
super.onDetach();
mDisposable.dispose();
}
}
package com.dayu.base.ui.fragment;
import android.content.Intent;
import android.databinding.ViewDataBinding;
import android.os.Bundle;
import com.dayu.base.R;
import com.dayu.base.common.Constants;
import com.dayu.base.presenter.BasePresenter;
import com.dayu.base.ui.BaseView;
import com.dayu.base.utils.InstanceUtil;
import com.dayu.base.utils.ProgressUtil;
import com.dayu.base.utils.ToastUtils;
import com.dayu.base.widgets.CustomDialog;
import java.lang.reflect.ParameterizedType;
/**
* Created by luo on 2017/11/14.
*/
public abstract class BaseFragment<P extends BasePresenter, B extends ViewDataBinding> extends BaseBindingFragment<B>
implements BaseView {
public P mPresenter;
private boolean isDialogShow = false;
@Override
protected void initPresenter() {
super.initPresenter();
if (this.getClass().getGenericSuperclass() instanceof ParameterizedType &&
((ParameterizedType) (this.getClass().getGenericSuperclass())).getActualTypeArguments().length > 0) {
Class mPresenterClass = (Class) ((ParameterizedType) (this.getClass()
.getGenericSuperclass())).getActualTypeArguments()[0];
mPresenter = InstanceUtil.getInstance(mPresenterClass);
if (mPresenter != null) {
mPresenter.setView(this);
// mBind.setVariable(BR.presenter, mPresenter);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mPresenter != null) mPresenter.onDetached();
}
public void dumpBack() {
mActivity.finish();
}
public void dumpBack(int requestCode, Intent intent) {
mActivity.setResult(requestCode, intent);
mActivity.finish();
}
public void showToast(int resId) {
ToastUtils.showShortToast(resId);
}
public void showToast(String msg) {
ToastUtils.showShortToast(msg);
}
public void showDialog() {
ProgressUtil.startLoad(mActivity);
}
public void showDialog(String str) {
ProgressUtil.startLoad(mActivity, str);
}
public void hideDialog() {
ProgressUtil.stopLoad();
}
public void startActivity(Class cls) {
mActivity.startActivity(new Intent(mActivity, cls));
}
public void startActivityForIntent(Intent intent) {
mActivity.startActivity(intent);
}
public void startActivity(Class<?> clz, Bundle bundle) {
Intent intent = new Intent(mActivity, clz);
if (bundle != null) {
intent.putExtra(Constants.BUNDLE, bundle);
}
mActivity.startActivity(intent);
}
public void startActivityForReult(Class<?> clz, int requestCode) {
mActivity.startActivityForResult(new Intent(mActivity, clz), requestCode);
}
public void startActivityForReult(Class<?> clz, Bundle bundle, int requestCode) {
Intent intent = new Intent(mActivity, clz);
if (bundle != null) {
intent.putExtra(Constants.BUNDLE, bundle);
}
mActivity.startActivityForResult(intent, requestCode);
}
public void startActvityAndFinish(Class<?> clz) {
mActivity.startActivity(new Intent(mActivity, clz));
mActivity.finish();
}
public void startActivityAndFinish(Class<?> clz, Bundle bundle) {
Intent intent = new Intent(mActivity, clz);
if (bundle != null) {
intent.putExtra(Constants.BUNDLE, bundle);
}
mActivity.startActivity(intent);
mActivity.finish();
}
public Bundle getBundle() {
return getArguments();
}
public void showLoginDialog() {
if (isDialogShow) {
return;
}
CustomDialog mDialog = new CustomDialog(mActivity, R.style.CustomDialog, getString(R.string.login_state_no)
, (dialog, confirm) -> {
if (confirm) {
// UserManager.getInstance().clearUserInfo();
// EMClient.getInstance().logout(true);
// UserManager.getInstance().clearUserInfo();
// Intent intent = new Intent(mActivity, LoginActivity.class);
// intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
// mActivity.startActivity(intent);
}
dialog.dismiss();
isDialogShow = false;
});
mDialog.setTitle(getString(R.string.notice))
.setPositiveButton(getString(R.string.login_again))
.setOneButton(true);
mDialog.show();
isDialogShow = true;
}
}
package com.dayu.base.utils;
/**
* Created by luofan on 2017/11/24.
*/
public class InstanceUtil {
/**
* 通过实例工厂去实例化相应类
*
* @param <T> 返回实例的泛型类型
* @return
*/
public static <T> T getInstance(Class cls) {
try {
return (T) cls.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassCastException e) {
e.printStackTrace();
}
return null;
}
}
package com.dayu.base.utils;
import android.text.TextUtils;
import android.util.Log;
import com.dayu.base.common.Constants;
import java.util.List;
/**
* @author itheima
* @time 2017-8-20 上午11:33:49
* @des 日志级别是LEVEL_ALL显示所有信息,包括System.out.println信息
* @des 日志级别是LEVEL_OFF关闭所有信息,包括System.out.println信息
* @updateAuthor TODO
* @updateTime TODO
* @updateDes TODO
*/
public class LogUtils {
/** 日志输出时的TAG */
private static String mTag = "BigFish";
/** 日志输出级别NONE */
public static final int LEVEL_OFF = 0;
/** 日志输出级别NONE */
public static final int LEVEL_ALL = 7;
/** 日志输出级别V */
public static final int LEVEL_VERBOSE = 1;
/** 日志输出级别D */
public static final int LEVEL_DEBUG = 2;
/** 日志输出级别I */
public static final int LEVEL_INFO = 3;
/** 日志输出级别W */
public static final int LEVEL_WARN = 4;
/** 日志输出级别E */
public static final int LEVEL_ERROR = 5;
/** 日志输出级别S,自定义定义的一个级别 */
public static final int LEVEL_SYSTEM = 6;
/** 是否允许输出log */
private static int mDebuggable = Constants.DEBUGLEVEL;
/** 用于记时的变量 */
private static long mTimestamp = 0;
/** 写文件的锁对象 */
private static final Object mLogLock = new Object();
/**---------------日志输出,已固定TAG begin---------------**/
/** 以级别为 d 的形式输出LOG */
public static void v(String msg) {
if (mDebuggable >= LEVEL_VERBOSE) {
Log.v(mTag, msg);
}
}
/** 以级别为 d 的形式输出LOG */
public static void d(String msg) {
if (mDebuggable >= LEVEL_DEBUG) {
Log.d(mTag, msg);
}
}
/** 以级别为 i 的形式输出LOG */
public static void i(String msg) {
if (mDebuggable >= LEVEL_INFO) {
Log.i(mTag, msg);
}
}
/** 以级别为 w 的形式输出LOG */
public static void w(String msg) {
if (mDebuggable >= LEVEL_WARN) {
Log.w(mTag, msg);
}
}
/** 以级别为 w 的形式输出Throwable */
public static void w(Throwable tr) {
if (mDebuggable >= LEVEL_WARN) {
Log.w(mTag, "", tr);
}
}
/** 以级别为 w 的形式输出LOG信息和Throwable */
public static void w(String msg, Throwable tr) {
if (mDebuggable >= LEVEL_WARN && null != msg) {
Log.w(mTag, msg, tr);
}
}
/** 以级别为 e 的形式输出LOG */
public static void e(String msg) {
if (mDebuggable >= LEVEL_ERROR) {
Log.e(mTag, msg);
}
}
/** 以级别为 s 的形式输出LOG,主要是为了System.out.println,稍微格式化了一下 */
public static void sf(String msg) {
if (mDebuggable >= LEVEL_ERROR) {
System.out.println("----------" + msg + "----------");
}
}
/** 以级别为 s 的形式输出LOG,主要是为了System.out.println */
public static void s(String msg) {
if (mDebuggable >= LEVEL_ERROR) {
System.out.println(msg);
}
}
/** 以级别为 e 的形式输出Throwable */
public static void e(Throwable tr) {
if (mDebuggable >= LEVEL_ERROR) {
Log.e(mTag, "", tr);
}
}
/** 以级别为 e 的形式输出LOG信息和Throwable */
public static void e(String msg, Throwable tr) {
if (mDebuggable >= LEVEL_ERROR && null != msg) {
Log.e(mTag, msg, tr);
}
}
/**---------------日志输出,已固定TAG end---------------**/
/**---------------日志输出,未固定TAG begin---------------**/
/** 以级别为 d 的形式输出LOG */
public static void v(String tag, String msg) {
if (mDebuggable >= LEVEL_VERBOSE) {
Log.v(tag, msg);
}
}
/** 以级别为 d 的形式输出LOG */
public static void d(String tag, String msg) {
if (mDebuggable >= LEVEL_DEBUG) {
Log.d(tag, msg);
}
}
/** 以级别为 i 的形式输出LOG */
public static void i(String tag, String msg) {
if (mDebuggable >= LEVEL_INFO) {
Log.i(tag, msg);
}
}
/** 以级别为 w 的形式输出LOG */
public static void w(String tag, String msg) {
if (mDebuggable >= LEVEL_WARN) {
Log.w(tag, msg);
}
}
/** 以级别为 e 的形式输出LOG */
public static void e(String tag, String msg) {
if (mDebuggable >= LEVEL_ERROR) {
Log.e(tag, msg);
}
}
/**---------------日志输出,未固定TAG end---------------**/
/**
* 以级别为 e 的形式输出msg信息,附带时间戳,用于输出一个时间段起始点
*
* @param msg
* 需要输出的msg
*/
public static void msgStartTime(String msg) {
mTimestamp = System.currentTimeMillis();
if (!TextUtils.isEmpty(msg)) {
e("[Started:" + mTimestamp + "]" + msg);
}
}
/** 以级别为 e 的形式输出msg信息,附带时间戳,用于输出一个时间段结束点* @param msg 需要输出的msg */
public static void elapsed(String msg) {
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - mTimestamp;
mTimestamp = currentTime;
e("[Elapsed:" + elapsedTime + "]" + msg);
}
public static <T> void printList(List<T> list) {
if (list == null || list.size() < 1) {
return;
}
int size = list.size();
i("---begin---");
for (int i = 0; i < size; i++) {
i(i + ":" + list.get(i).toString());
}
i("---end---");
}
public static <T> void printArray(T[] array) {
if (array == null || array.length < 1) {
return;
}
int length = array.length;
i("---begin---");
for (int i = 0; i < length; i++) {
i(i + ":" + array[i].toString());
}
i("---end---");
}
}
package com.dayu.base.utils;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import com.dayu.base.R;
import java.util.List;
/**
* Created by luofan on 16/7/20.
*/
public class ProgressUtil {
private static final int START_DIALOG = 0;//开始对话框
private static final int UPDATE_DIALOG = 1;//更新对话框
private static final int STOP_DIALOG = 2;//销毁对话框
private static AlertDialog dialog = null;
private static TextView title = null;
private static Context context = null;
private static Handler handler = new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
String message = "";
switch (msg.what) {
case START_DIALOG:// 启动加载框
message = (String) msg.obj;
if (dialog != null) {
stopLoad();
startLoad(context, message);
return;
}
init(context, message);
isTouchDismiss(true);
break;
case UPDATE_DIALOG:// 更新加载框
message = (String) msg.obj;
if (title.VISIBLE == View.VISIBLE) {
if (TextUtils.isEmpty(message)) {
title.setVisibility(View.GONE);
} else {
title.setText(message);
}
} else {
if (!TextUtils.isEmpty(message)) {
title.setText(message);
title.setVisibility(View.VISIBLE);
}
}
break;
case STOP_DIALOG:// 停止加载框
if (dialog != null) {
dialog.dismiss();
dialog.cancel();
dialog = null;
title = null;
}
break;
}
}
;
};
/**
* @方法说明:加载控件与布局
* @方法名称:init
* @返回值:void
*/
private static void init(Context context, String mssg) {
if (isBackground(context)) {// 如果程序在后台,则不加载
return;
}
if (null != context) {
LayoutInflater flat = LayoutInflater.from(context);
View v = flat.inflate(R.layout.dialog_loading_layout, null);
// v.setBackgroundColor(mContext.getResources().getColor(android.R.color.transparent));
// 创建对话
dialog = new AlertDialog.Builder(context, R.style.Dialog).create();
// 设置返回键点击消失对话框
dialog.setCancelable(true);
// 设置点击返回框外边不消失
dialog.setCanceledOnTouchOutside(true);
// 给该对话框增加系统权限
// dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
// 显示对话
dialog.show();
// 加载控件
title = (TextView) v.findViewById(R.id.loading_text);
if (TextUtils.isEmpty(mssg)) {
title.setVisibility(View.GONE);
} else {
title.setVisibility(View.VISIBLE);
title.setText(mssg);
}
// 必须放到显示对话框下面,否则显示不出效果
Window window = dialog.getWindow();
// window.getAttributes().x = 0;
// window.getAttributes().y = 0;//设置y坐标
WindowManager.LayoutParams params = window.getAttributes();
params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.CENTER;
// params.alpha = 0.6f;
window.setAttributes(params); // 加载布局组件
dialog.getWindow().setContentView(v);
}
}
/**
* @param
* @param msg
* @方法说明:启动对话框
* @方法名称:startLoad
* @返回值:void
*/
public static void startLoad(Context activity, String msg) {
context = activity;
if (context == null) {
return;
}
if (isBackground(context)) {// 如果程序在后台,则不加载
return;
}
Message mssage = new Message();
mssage.what = START_DIALOG;
mssage.obj = msg;
handler.sendMessage(mssage);
}
/**
* @param
* @方法说明:启动对话框
* @方法名称:startLoad
* @返回值:void
*/
public static void startLoad(Context activity) {
context = activity;
if (context == null) {
return;
}
if (isBackground(context)) {// 如果程序在后台,则不加载
return;
}
Message mssage = new Message();
mssage.what = START_DIALOG;
mssage.obj = "加载中...";
handler.sendMessage(mssage);
}
/**
* @param msg
* @方法说明:更新显示的内容
* @方法名称:UpdateMsg
* @返回值:void
*/
public static void UpdateMsg(String msg) {
Message message = new Message();
message.what = UPDATE_DIALOG;
message.obj = msg;
handler.sendMessage(message);
}
/**
* @param flag
* @方法说明:允许加载条转动的时候去点击系统返回键
* @方法名称:openCancelable
* @返回值:void
*/
public static void openCancelable(boolean flag) {
if (dialog != null) {
dialog.setCancelable(flag);
}
}
/**
* @param isdimiss
* @方法说明:允许点击对话框触摸消失
* @方法名称:isTouchDismiss
* @返回值:void
*/
public static void isTouchDismiss(boolean isdimiss) {
if (dialog != null) {
dialog.setCanceledOnTouchOutside(isdimiss);
}
}
/**
* @方法说明:让警告框消失
* @方法名称:dismiss
* @返回值:void
*/
public static void stopLoad() {
handler.sendEmptyMessage(STOP_DIALOG);
}
/**
* @param context
* @return
* @方法说明:判断当前应用程序是否后台运行
* @方法名称:isBackground
* @返回值:boolean
*/
public static boolean isBackground(Context context) {
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager
.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.processName.equals(context.getPackageName())) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
// 后台运行
return true;
} else {
// 前台运行
return false;
}
}
}
return false;
}
}
package com.dayu.base.utils;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.StringRes;
import android.widget.Toast;
/**
* 吐司工具类
* on 2017/8/22.
*/
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(UIUtils.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(UIUtils.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(UIUtils.getContext(), text, duration);
} else {
sToast.setText(text);
sToast.setDuration(duration);
}
sToast.show();
}
/**
* 取消吐司显示
*/
public static void cancelToast() {
if (sToast != null) {
sToast.cancel();
sToast = null;
}
}
}
package com.dayu.base.utils;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import com.dayu.base.common.BaseApplication;
/**
* 封装和ui相关的操作
* MrWang on
* 2017/8/20.
*/
public class UIUtils {
/**
* 得到上下文
*/
public static Context getContext() {
return BaseApplication.getContext();
}
/**
* 得到Resources对象
*/
public static Resources getResources() {
return getContext().getResources();
}
/**
*
*
*/
public static String getString(int resId) {
return getResources().getString(resId);
}
/**
* 得到String.xml中的字符串数组信息
*/
public static String[] getStrings(int resId) {
return getResources().getStringArray(resId);
}
/**
* 得到Color.xml中的颜色信息
*/
public static int getColor(int resId) {
return getResources().getColor(resId);
}
/**
* 得到应用程序的包名
*/
public static String getPackageName() {
return getContext().getPackageName();
}
/**
* 把一个dp的值转换为px值
*
* @param dp
* @return
*/
public int dp2px(int dp) {
// 获取手机屏幕的密度
float density = getResources().getDisplayMetrics().density;
return (int) (dp * density + 0.5f); // 加0.5是为了四舍五入
}
/**
* 判断TextView的内容宽度是否超出其可用宽度
*
* @param tv
* @return
*/
public static boolean isOverFlowed(TextView tv) {
int availableWidth = tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight();
Paint textViewPaint = tv.getPaint();
float textWidth = textViewPaint.measureText(tv.getText().toString()) / 2;
if (textWidth > availableWidth) {
return true;
} else {
return false;
}
}
public static void hideInput(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}
}
package com.dayu.base.widgets;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import com.dayu.base.R;
/**
* Created by luofan on 2017/11/10.
*/
public class CustomDialog extends Dialog implements View.OnClickListener {
private TextView contentTxt;
private TextView titleTxt;
private TextView submitTxt;
private TextView cancelTxt;
private Context mContext;
private String content;
private OnCloseListener listener;
private String positiveName;
private String negativeName;
private String title;
private int positiveColor;
private boolean flag;
private View line;
public CustomDialog(Context context) {
super(context);
this.mContext = context;
}
public CustomDialog(Context context, int themeResId, String content) {
super(context, themeResId);
this.mContext = context;
this.content = content;
}
public CustomDialog(Context context, int themeResId, String content, OnCloseListener listener) {
super(context, themeResId);
this.mContext = context;
this.content = content;
this.listener = listener;
}
protected CustomDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
this.mContext = context;
}
public CustomDialog setTitle(String title) {
this.title = title;
return this;
}
public CustomDialog setPositiveButton(String name) {
this.positiveName = name;
return this;
}
public CustomDialog setPositiveButtonColor(int colorId) {
this.positiveColor = colorId;
return this;
}
public CustomDialog setNegativeButton(String name) {
this.negativeName = name;
return this;
}
public CustomDialog setOneButton(boolean flag) {
this.flag = flag;
return this;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_custom);
setCanceledOnTouchOutside(false);
setCancelable(false);
initView();
}
private void initView() {
contentTxt = (TextView) findViewById(R.id.content);
titleTxt = (TextView) findViewById(R.id.title);
line = findViewById(R.id.v_line);
submitTxt = (TextView) findViewById(R.id.submit);
submitTxt.setOnClickListener(this);
cancelTxt = (TextView) findViewById(R.id.cancel);
cancelTxt.setOnClickListener(this);
contentTxt.setText(content);
if (!TextUtils.isEmpty(positiveName)) {
submitTxt.setText(positiveName);
}
if (positiveColor != 0) {
submitTxt.setTextColor(positiveColor);
}
if (!TextUtils.isEmpty(negativeName)) {
cancelTxt.setText(negativeName);
}
if (!TextUtils.isEmpty(title)) {
titleTxt.setText(title);
}
if (flag) {
line.setVisibility(View.GONE);
cancelTxt.setVisibility(View.GONE);
}
}
@Override
public void onClick(View v) {
int i = v.getId();
if (i == R.id.cancel) {
if (listener != null) {
listener.onClick(this, false);
}
this.dismiss();
} else if (i == R.id.submit) {
if (listener != null) {
listener.onClick(this, true);
}
this.dismiss();
}
}
public interface OnCloseListener {
void onClick(Dialog dialog, boolean confirm);
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="8dp"/>
<solid android:color="@color/cl_white"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="270.0"
android:centerColor="#99000000"
android:centerY="0.5"
android:endColor="#99000000"
android:startColor="#99000000" >
</gradient>
<corners android:radius="5dip" />
<padding
android:bottom="0dp"
android:left="0dp"
android:right="0dp"
android:top="0dp" />
</shape>
\ No newline at end of file
<animated-rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:pivotX="50%" android:pivotY="50%"
android:fromDegrees="0"
android:toDegrees="720">
<shape
android:shape="ring"
android:innerRadiusRatio="3"
android:thicknessRatio="15"
android:useLevel="false">
<gradient
android:type="sweep"
android:useLevel="false"
android:startColor="#55c6c6c6"
android:centerColor="#c6c6c6"
android:centerY="0.50"
android:endColor="#c6c6c6" />
</shape>
</animated-rotate>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:background="@drawable/item_shape"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="20dp"
android:text="@string/notice"
android:textColor="#FF030303"
android:textSize="17sp" />
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="20dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:gravity="center"
android:lineSpacingExtra="3dp"
android:textColor="#FF030303"
android:textSize="13sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/cl_selector_hui" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/cancel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:gravity="center"
android:text="@string/cancle"
android:textColor="@color/cl_receiving_order_item_data"
android:textSize="17sp" />
<View
android:id="@+id/v_line"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/cl_selector_hui" />
<TextView
android:id="@+id/submit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:gravity="center"
android:text="@string/comfirm"
android:textColor="@color/cl_receiving_order_item_data"
android:textSize="17sp" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container_dialog"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:layout_width="80dip"
android:layout_height="80dip"
android:layout_centerInParent="true"
android:background="@drawable/loading_progress_bg"
android:gravity="center"
android:orientation="vertical">
<ProgressBar
android:layout_width="45dp"
android:layout_height="45dp"
android:indeterminateDrawable="@drawable/progressbar"
android:layout_gravity="center"
/>
<TextView
android:id="@+id/loading_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="12sp"
android:text="@string/loading"
android:textColor="#9a9b98"/>
</LinearLayout>
</RelativeLayout>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment