From 5f50bd6ea5d5bdb7b8ea4d9e9a5851067b9aec1b Mon Sep 17 00:00:00 2001 From: Administrator <123> Date: Tue, 09 Nov 2021 02:22:38 +0000 Subject: [PATCH] 网络请求框架 --- app/src/main/java/com/duqing/missions/retrofit/api/LoginApiCenter.java | 21 app/src/main/res/anim/slide_in_left.xml | 7 app/src/main/res/navigation/mobile_navigation.xml | 30 app/src/main/java/com/duqing/missions/retrofit/converter/GsonRequestBodyConverter.java | 44 + app/src/main/res/values/strings.xml | 1 app/src/main/java/com/duqing/missions/retrofit/Interceptor/EncryptInterceptor.java | 104 +++ app/src/main/java/com/duqing/missions/base/model/ItemViewModel.java | 16 app/src/main/AndroidManifest.xml | 21 app/src/main/java/com/duqing/missions/retrofit/utils/HttpPrintUtils.java | 210 ++++++ app/src/main/res/xml/network_security_config.xml | 4 app/src/main/java/com/duqing/missions/util/NetWorkUtils.java | 212 ++++++ app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonResponseBodyConverter.java | 95 ++ app/src/main/java/com/duqing/missions/retrofit/NetWorkCost.java | 14 app/src/main/java/com/duqing/missions/retrofit/AndroidScheduler.java | 39 + app/src/main/java/com/duqing/missions/retrofit/NetWorkListenear.java | 173 +++++ app/src/main/java/com/duqing/missions/retrofit/api/CommonApiCenter.java | 65 + app/src/main/java/com/duqing/missions/retrofit/RetrofitUtils.java | 126 +++ app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java | 50 + app/src/main/java/com/duqing/missions/retrofit/utils/RSAUtils.java | 178 +++++ app/src/main/java/com/duqing/missions/util/GsonUtils.java | 256 +++++++ app/src/main/java/com/duqing/missions/retrofit/Interceptor/HttpLoggingInterceptor.java | 277 ++++++++ app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonConverterFactory.java | 59 + app/build.gradle | 16 23 files changed, 2,012 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0b61201..6fe0e3c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,18 @@ } buildTypes { + debug{ + minifyEnabled false + buildConfigField 'String','HOST_IP_ADDR','"http://192.168.100.82:8080/"' + buildConfigField 'String','ENVIRONMENT','"release"' + resValue "string", "app_name", "趣为帮扶测试" + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } release { minifyEnabled false + buildConfigField 'String','HOST_IP_ADDR','"http://192.168.100.82:8080/"' + buildConfigField 'String','ENVIRONMENT','"release"' + resValue "string", "app_name", "趣为帮扶" proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } @@ -58,10 +68,10 @@ implementation 'com.github.d-max:spots-dialog:1.1@aar'//loading view implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.ansen.http:okhttpencapsulation:1.0.1'//版本更新下载 - implementation 'com.squareup.okhttp3:okhttp:3.12.1' - implementation 'com.squareup.retrofit2:retrofit:2.3.0' + implementation 'com.squareup.okhttp3:okhttp:4.9.0' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' //RXjava和retrofit结合 - implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0' + implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0' implementation 'com.permissionx.guolindev:permissionx:1.2.2' //权限依赖让你推广你就发群里?没有别的群了? implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 066f60b..c95a50d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,26 @@ tools:ignore="ProtectedPermissions" package="com.duqing.missions" > + + <uses-permission android:name="android.permission.CALL_PHONE" /> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.GET_TASKS" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> + <uses-permission android:name="android.permission.READ_PHONE_STATE" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> + <!--写入SD卡的权限:如果你希望保存相机拍照后的照片--> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <!--读取SD卡的权限:打开相册选取图片所必须的权限--> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <!--android Q 安装权限--> + <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> + <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" + tools:ignore="ProtectedPermissions" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <application android:name=".MyApplication" @@ -12,6 +32,7 @@ android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" + android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/Theme.Missions" > <activity android:name=".ui.main.MainActivity" diff --git a/app/src/main/java/com/duqing/missions/base/model/ItemViewModel.java b/app/src/main/java/com/duqing/missions/base/model/ItemViewModel.java new file mode 100644 index 0000000..8107563 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/base/model/ItemViewModel.java @@ -0,0 +1,16 @@ +package com.duqing.missions.base.model; + +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +/** + * Created by Administrator on 2021/11/5 0005. + */ +public class ItemViewModel<T> extends ViewModel { + + MutableLiveData<T> liveData = new MutableLiveData<>(); + + public MutableLiveData<T> getLiveData() { + return liveData; + } +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/AndroidScheduler.java b/app/src/main/java/com/duqing/missions/retrofit/AndroidScheduler.java new file mode 100644 index 0000000..3760cc8 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/AndroidScheduler.java @@ -0,0 +1,39 @@ +package com.duqing.missions.retrofit; + +import android.os.Handler; +import android.os.Looper; + +import androidx.annotation.NonNull; + +import java.util.concurrent.Executor; + +import io.reactivex.Scheduler; +import io.reactivex.schedulers.Schedulers; + +/** + * Created by Administrator on 2021/11/8 0008. + */ +public class AndroidScheduler implements Executor { + private static AndroidScheduler instance; + + private final Scheduler mMainScheduler; + private final Handler mHandler; + + private AndroidScheduler() { + mHandler = new Handler(Looper.myLooper()); + mMainScheduler = Schedulers.from(this); + } + + public static synchronized Scheduler mainThread() { + if (instance == null) { + instance = new AndroidScheduler(); + } + return instance.mMainScheduler; + } + + @Override + public void execute(@NonNull Runnable command) { + mHandler.post(command); + } + +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/Interceptor/EncryptInterceptor.java b/app/src/main/java/com/duqing/missions/retrofit/Interceptor/EncryptInterceptor.java new file mode 100644 index 0000000..6aa7ba2 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/Interceptor/EncryptInterceptor.java @@ -0,0 +1,104 @@ +package com.duqing.missions.retrofit.Interceptor; + + +import com.duqing.missions.retrofit.utils.RSAUtils; + +import org.json.JSONObject; + +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +import okhttp3.FormBody; +import okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okio.Buffer; + +/** + * My father is Object, ites purpose of 加密拦截器 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-10-8. + */ + +public class EncryptInterceptor implements Interceptor { + + protected static final Charset UTF8 = Charset.forName("UTF-8"); + private final String ENCRYPT = "encrypt"; + + @Override + public Response intercept(Chain chain) throws IOException { + return chain.proceed(encryptRequest(chain.request())); + } + + + //加密 + protected Request encryptRequest(Request request) throws IOException { + Headers headers = request.headers(); + RequestBody requestBody = request.body(); + Request.Builder builder = request.newBuilder(); + for(int i = 0 ; i < headers.size() ; i ++){ + builder.addHeader(headers.name(i),headers.value(i)); + } + if(requestBody != null){ + Charset charset = UTF8; + MediaType contentType = requestBody.contentType(); + if (contentType != null) { + charset = contentType.charset(UTF8); + } + HashMap param = new HashMap(); + if(requestBody instanceof MultipartBody){ + MultipartBody body = (MultipartBody) requestBody; + for(MultipartBody.Part part:body.parts()){ + Buffer buffer1 = new Buffer(); + part.body().writeTo(buffer1); + String str=buffer1.readString(charset).replaceAll("%(?![0-9a-fA-F]{2})","%25"); + param.put(part.headers().get(part.headers().name(0)), URLDecoder.decode(str, "UTF-8")); + } + MultipartBody.Builder mbuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); + mbuilder.addFormDataPart(ENCRYPT,encryptParam(param)); + builder.post(mbuilder.build()); + }else if(requestBody instanceof FormBody){ + FormBody body = (FormBody) requestBody; + for(int i = 0 ; i < body.size() ; i ++ ){ + param.put(body.name(i),body.value(i)); + } + FormBody.Builder formBuild = new FormBody.Builder(); + formBuild.add(ENCRYPT,encryptParam(param)); + builder.post(formBuild.build()); + }else{ + Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + String str = buffer.readString(charset); + String encrypt = encryptJson(str); + param.put(ENCRYPT,encrypt); + builder.post(RequestBody.create(MediaType.parse("application/json;charset=utf-8"), new JSONObject(param).toString())); + } + } + return builder.build(); + } + + + /** + * 加密传递的参数 + * @param params + * @return + */ + public static String encryptParam(Map<String, Object> params){ + return encryptJson(new JSONObject(params).toString()); + } + public static String encryptJson(String json){ + try { + return RSAUtils.encrypt(json,RSAUtils.getPublicKey(RSAUtils.PUBLIC_KEY)); + }catch (Exception e){ + e.printStackTrace(); + return e.getMessage(); + } + } +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/Interceptor/HttpLoggingInterceptor.java b/app/src/main/java/com/duqing/missions/retrofit/Interceptor/HttpLoggingInterceptor.java new file mode 100644 index 0000000..9d0f496 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/Interceptor/HttpLoggingInterceptor.java @@ -0,0 +1,277 @@ +package com.duqing.missions.retrofit.Interceptor; + +import android.util.Log; + +import com.duqing.missions.retrofit.NetWorkCost; +import com.duqing.missions.retrofit.NetWorkListenear; +import com.duqing.missions.retrofit.utils.HttpPrintUtils; +import com.duqing.missions.util.GsonUtils; + +import org.json.JSONObject; + +import java.io.EOFException; +import java.io.IOException; +import java.net.URLDecoder; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; + +import okhttp3.FormBody; +import okhttp3.Headers; +import okhttp3.MediaType; +import okhttp3.MultipartBody; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.internal.http.HttpHeaders; +import okio.Buffer; +import okio.BufferedSource; + +/** + * My father is Object, ites purpose of log打印 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2020-10-21. + */ + +public class HttpLoggingInterceptor extends EncryptInterceptor { + + final String TAG = "HttpLogging"; + + public HttpLoggingInterceptor() { + } + + + @Override + public Response intercept(Chain chain) throws IOException { + + Request request = chain.request(); + int hashCode = request.hashCode(); + ArrayList<String> logArrays = getRequestLog(request); + int position = logArrays.size() +2; + Response response; + try { + request = encryptRequest(request);//加密 + response = chain.proceed(request); + logArrays.addAll(getResponseLog(response)); + Log.d(TAG,"hashcode:"+hashCode); + NetWorkCost netWorkCost = NetWorkListenear.workCostMap.get(hashCode); + String cost = String.format("dns:%s,secure:%s,connect:%s,requestH:%s,requestB:%s,responseH:%s,responseB:%s", convertTimes(netWorkCost.dns) ,convertTimes(netWorkCost.secure) , convertTimes(netWorkCost.connect),convertTimes(netWorkCost.requestHeader),convertTimes(netWorkCost.requestBody) ,convertTimes(netWorkCost.resposeHeader),convertTimes(netWorkCost.resposeBody) ); + logArrays.add(position,"<-- costtimes : "+convertTimes(netWorkCost.total)+" (" +cost + ')'); + NetWorkListenear.workCostMap.remove(hashCode); + new Thread(){ + @Override + public void run() { + HttpPrintUtils.getInstance().printLog(logArrays, true);//线程安全方法,需在新线程执行,避免阻塞当前线程,导致程序无响应 + } + }.start(); + } catch (Exception e) { + logArrays.add("<-- response url:" + URLDecoder.decode(request.url().toString(), "UTF-8")); + NetWorkCost netWorkCost = NetWorkListenear.workCostMap.get(hashCode); + String cost = String.format("dns:%s,secure:%s,connect:%s,requestH:%s,requestB:%s,responseH:%s,responseB:%s", convertTimes(netWorkCost.dns) ,convertTimes(netWorkCost.secure) , convertTimes(netWorkCost.connect),convertTimes(netWorkCost.requestHeader),convertTimes(netWorkCost.requestBody) ,convertTimes(netWorkCost.resposeHeader),convertTimes(netWorkCost.resposeBody) ); + logArrays.add("<-- costtimes : "+convertTimes(netWorkCost.total)+" (" +cost + ')'); + logArrays.add("<-- response failed " + e.getLocalizedMessage()); + logArrays.add("<-- " + e.toString()); + new Thread(){ + @Override + public void run() { + HttpPrintUtils.getInstance().printLog(logArrays, false);//线程安全方法,需在新线程执行,避免阻塞当前线程,导致程序无响应 + } + }.start(); + throw e;//抛出异常,用于请求接收信息 + } + return response; + } + + private ArrayList<String> getRequestLog(Request request) throws IOException { + RequestBody requestBody = request.body(); + ArrayList<String> logArrays = new ArrayList<>(); + String requestStartMessage = "--> " + request.method() + ' ' + URLDecoder.decode(request.url().toString() ,"UTF-8")+ ' ' ; + if ( requestBody != null) { + requestStartMessage += " (" + requestBody.contentLength() + "-byte body)"; + } + logArrays.add(requestStartMessage); + Headers headers = request.headers(); + logArrays.add("---------->REQUEST HEADER<----------"); + for (int i = 0, count = headers.size(); i < count; i++) { + logArrays.add(headers.name(i) + ": " + headers.value(i)); + } + if (requestBody == null) { + logArrays.add("--> END " + request.method()); + } else if (bodyEncoded(request.headers())) { + logArrays.add("--> END " + request.method() + " (encoded body omitted)"); + } else { + + Charset charset = UTF8; + MediaType contentType = requestBody.contentType(); + if (contentType != null) { + charset = contentType.charset(UTF8); + } + HashMap param = new HashMap(); + if(requestBody instanceof MultipartBody){ + logArrays.add("---------->REQUEST BODY[MultipartBody]<----------"); + MultipartBody body = (MultipartBody) requestBody; + for(MultipartBody.Part part:body.parts()){ + Buffer buffer1 = new Buffer(); + part.body().writeTo(buffer1); + String str=buffer1.readString(charset).replaceAll("%(?![0-9a-fA-F]{2})","%25"); + param.put(part.headers().get(part.headers().name(0)),URLDecoder.decode(str, "UTF-8")); + } + logArrays.add(GsonUtils.retractJson(new JSONObject(param).toString())); + }else if(requestBody instanceof FormBody){ + logArrays.add("---------->REQUEST BODY[FormBody]<----------"); + FormBody body = (FormBody) requestBody; + for(int i = 0 ; i < body.size() ; i ++ ){ + param.put(body.name(i),body.value(i)); + } + logArrays.add(GsonUtils.retractJson(new JSONObject(param).toString())); + }else{ + Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + logArrays.add("---------->REQUEST BODY<----------"); + String str = buffer.readString(charset); + try{ + logArrays.add(GsonUtils.retractJson(URLDecoder.decode(str, "UTF-8"))); + }catch (Exception e){ + logArrays.add(str); + } + } + logArrays.add("--> END " + request.method() + " " + contentType + " ( " + + requestBody.contentLength() + "-byte body )"); + } + return logArrays; + + } + + + private ArrayList<String> getResponseLog(Response response) throws IOException { + ArrayList<String> logArrays = new ArrayList<>(); + ResponseBody responseBody = response.body(); + long contentLength = responseBody.contentLength(); + String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length"; + logArrays.add("<-- response code:" + response.code() + " message:" + response.message()+" contentlength:"+bodySize ); + logArrays.add("<-- response url:"+URLDecoder.decode(response.request().url().toString(),"UTF-8") ); + + if ( !HttpHeaders.hasBody(response)) { + logArrays.add("<-- END HTTP"); + } else if (bodyEncoded(response.headers())) { + logArrays.add("<-- END HTTP (encoded body omitted)"); + } else { + BufferedSource source = responseBody.source(); + source.request(Long.MAX_VALUE); // Buffer the entire body. + Buffer buffer = source.buffer(); + + Charset charset = UTF8; + MediaType contentType = responseBody.contentType(); + if (contentType != null) { + charset = contentType.charset(UTF8); + } + + if (isPlaintext(buffer)) { + logArrays.add("---------->RESPONSE BODY<----------"); + if (contentLength != 0) { + logArrays.add(retractJson(buffer.clone().readString(charset))); + } + + logArrays.add("<-- END HTTP (" + buffer.size() + "-byte body)"); + } + + } + return logArrays; + } + + /** + * 字符串缩进 + * @param json + * @return + */ + private String retractJson(String json){ + int level = 0 ; + StringBuffer jsonForMatStr = new StringBuffer(); + for(int index=0;index<json.length();index++)//将字符串中的字符逐个按行输出 + { + //获取s中的每个字符 + char c = json.charAt(index); + // System.out.println(s.charAt(index)); + + //level大于0并且jsonForMatStr中的最后一个字符为\n,jsonForMatStr加入\t + if (level > 0 && '\n' == jsonForMatStr.charAt(jsonForMatStr.length() - 1)) { + jsonForMatStr.append(getLevelStr(level)); + // System.out.println("123"+jsonForMatStr); + } + //遇到"{"和"["要增加空格和换行,遇到"}"和"]"要减少空格,以对应,遇到","要换行 + switch (c) { + case '{': + case '[': + jsonForMatStr.append(c + "\n"); + level++; + break; + case ',': + jsonForMatStr.append(c + "\n"); + break; + case '}': + case ']': + jsonForMatStr.append("\n"); + level--; + jsonForMatStr.append(getLevelStr(level)); + jsonForMatStr.append(c); + break; + default: + jsonForMatStr.append(c); + break; + } + } + return jsonForMatStr.toString(); + } + + private String getLevelStr(int level) { + StringBuffer levelStr = new StringBuffer(); + for (int levelI = 0; levelI < level; levelI++) { + levelStr.append("\t");//\t或空格 + } + return levelStr.toString(); + } + + /** + * Returns true if the body in question probably contains human readable text. Uses a small sample + * of code points to detect unicode control characters commonly used in binary file signatures. + */ + static boolean isPlaintext(Buffer buffer) { + try { + Buffer prefix = new Buffer(); + long byteCount = buffer.size() < 64 ? buffer.size() : 64; + buffer.copyTo(prefix, 0, byteCount); + for (int i = 0; i < 16; i++) { + if (prefix.exhausted()) { + break; + } + int codePoint = prefix.readUtf8CodePoint(); + if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { + return false; + } + } + return true; + } catch (EOFException e) { + return false; // Truncated UTF-8 sequence. + } + } + + private boolean bodyEncoded(Headers headers) { + String contentEncoding = headers.get("Content-Encoding"); + return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity"); + } + + private String convertTimes(long ms){ + String m = null,s=null; + final int utilS = 1000; + final int utilM = utilS*60; + if(ms/utilM>0){ + m = ms/utilM+"m"; + } + if(ms%utilM/utilS>0){ + s = ms%utilM/utilS+"s"; + } + return (m!=null?m:"")+(s!=null?s:"")+ms%utilS+"ms"; + } + +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/NetWorkCost.java b/app/src/main/java/com/duqing/missions/retrofit/NetWorkCost.java new file mode 100644 index 0000000..53bb716 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/NetWorkCost.java @@ -0,0 +1,14 @@ +package com.duqing.missions.retrofit; + + +/** + * My father is Object, ites purpose of 网络消耗 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-3-12. + */ + +public class NetWorkCost { + + //网络消耗时间 + public long dns,connect,total,secure,requestHeader,requestBody,resposeHeader,resposeBody; +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/NetWorkListenear.java b/app/src/main/java/com/duqing/missions/retrofit/NetWorkListenear.java new file mode 100644 index 0000000..106346a --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/NetWorkListenear.java @@ -0,0 +1,173 @@ +package com.duqing.missions.retrofit; + +import androidx.annotation.Nullable; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.nio.charset.Charset; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import okhttp3.Call; +import okhttp3.EventListener; +import okhttp3.Handshake; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; + +/** + * My father is Object, ites purpose of 接口请求耗时监听 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-7-9. + */ + +public class NetWorkListenear extends EventListener { + + private static final String TAG = "NetworkEventListener"; + final Charset UTF8 = Charset.forName("UTF-8"); + public static Map<Integer, NetWorkCost> workCostMap = new HashMap<>(); + + public static Factory get(){ + Factory factory = new Factory() { + @NotNull + @Override + public EventListener create(@NotNull Call call) { + return new NetWorkListenear(); + } + }; + return factory; + } + + @Override + public void callStart(@NotNull Call call) { + super.callStart(call); + //mRequestId = mNextRequestId.getAndIncrement() + ""; + //getAndAdd,在多线程下使用cas保证原子性 + NetWorkCost netWorkCost = new NetWorkCost(); + netWorkCost.total = new Date().getTime(); + workCostMap.put(call.request().hashCode(),netWorkCost); + } + + @Override + public void dnsStart(@NotNull Call call, @NotNull String domainName) { + super.dnsStart(call, domainName); + //Log.d(TAG, "dnsStart"); + workCostMap.get(call.request().hashCode()).dns = new Date().getTime(); + } + + @Override + public void dnsEnd(@NotNull Call call, @NotNull String domainName, @NotNull List<InetAddress> inetAddressList) { + super.dnsEnd(call, domainName, inetAddressList); + //Log.d(TAG, "dnsEnd"); + workCostMap.get(call.request().hashCode()).dns = new Date().getTime() - workCostMap.get(call.request().hashCode()).dns; + } + + @Override + public void connectStart(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy) { + super.connectStart(call, inetSocketAddress, proxy); + //Log.d(TAG, "connectStart"); + workCostMap.get(call.request().hashCode()).connect = new Date().getTime(); + } + + @Override + public void secureConnectStart(@NotNull Call call) { + super.secureConnectStart(call); + //Log.d(TAG, "secureConnectStart"); + workCostMap.get(call.request().hashCode()).secure = new Date().getTime(); + } + + @Override + public void secureConnectEnd(@NotNull Call call, @Nullable Handshake handshake) { + super.secureConnectEnd(call, handshake); + //Log.d(TAG, "secureConnectEnd"); + workCostMap.get(call.request().hashCode()).secure = new Date().getTime() - workCostMap.get(call.request().hashCode()).secure; + } + + @Override + public void connectEnd(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, + @NotNull Proxy proxy, @Nullable Protocol protocol) { + super.connectEnd(call, inetSocketAddress, proxy, protocol); + //Log.d(TAG, "connectEnd"); + workCostMap.get(call.request().hashCode()).connect = new Date().getTime() - workCostMap.get(call.request().hashCode()).connect; + } + + @Override + public void connectFailed(@NotNull Call call, @NotNull InetSocketAddress inetSocketAddress, @NotNull Proxy proxy, @Nullable Protocol protocol, @NotNull IOException ioe) { + super.connectFailed(call, inetSocketAddress, proxy, protocol, ioe); + workCostMap.get(call.request().hashCode()).connect = new Date().getTime() - workCostMap.get(call.request().hashCode()).connect; + workCostMap.get(call.request().hashCode()).total = new Date().getTime() - workCostMap.get(call.request().hashCode()).total; + //Log.d(TAG, "connectFailed"); + } + + @Override + public void requestHeadersStart(@NotNull Call call) { + super.requestHeadersStart(call); + //Log.d(TAG, "requestHeadersStart"); + workCostMap.get(call.request().hashCode()).requestHeader = new Date().getTime(); + } + + @Override + public void requestHeadersEnd(@NotNull Call call, @NotNull Request request) { + super.requestHeadersEnd(call, request); + //Log.d(TAG, "requestHeadersEnd"); + workCostMap.get(call.request().hashCode()).requestHeader = new Date().getTime() - workCostMap.get(call.request().hashCode()).requestHeader; + } + + @Override + public void requestBodyStart(@NotNull Call call) { + super.requestBodyStart(call); + //Log.d(TAG, "requestBodyStart"); + workCostMap.get(call.request().hashCode()).requestBody = new Date().getTime(); + } + + @Override + public void requestBodyEnd(@NotNull Call call, long byteCount) { + super.requestBodyEnd(call, byteCount); + //Log.d(TAG, "requestBodyEnd"); + workCostMap.get(call.request().hashCode()).requestBody = new Date().getTime() - workCostMap.get(call.request().hashCode()).requestBody; + } + + @Override + public void responseHeadersStart(@NotNull Call call) { + super.responseHeadersStart(call); + //Log.d(TAG, "responseHeadersStart"); + workCostMap.get(call.request().hashCode()).resposeHeader = new Date().getTime(); + } + + @Override + public void responseHeadersEnd(@NotNull Call call, @NotNull Response response) { + super.responseHeadersEnd(call, response); + //Log.d(TAG, "responseHeadersEnd"); + workCostMap.get(call.request().hashCode()).resposeHeader = new Date().getTime() - workCostMap.get(call.request().hashCode()).resposeHeader; + } + + @Override + public void responseBodyStart(@NotNull Call call) { + super.responseBodyStart(call); + //Log.d(TAG, "responseBodyStart"); + workCostMap.get(call.request().hashCode()).resposeBody = new Date().getTime(); + } + + @Override + public void responseBodyEnd(@NotNull Call call, long byteCount) { + super.responseBodyEnd(call, byteCount); + //Log.d(TAG, "responseBodyEnd"); + workCostMap.get(call.request().hashCode()).resposeBody = new Date().getTime() - workCostMap.get(call.request().hashCode()).resposeBody; + workCostMap.get(call.request().hashCode()).total = new Date().getTime() - workCostMap.get(call.request().hashCode()).total; + } + + + @Override + public void callFailed(@NotNull Call call, @NotNull IOException ioe) { + super.callFailed(call, ioe); + workCostMap.get(call.request().hashCode()).total = new Date().getTime() - workCostMap.get(call.request().hashCode()).total; + //Log.d(TAG, "callFailed"); + } + +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/RetrofitUtils.java b/app/src/main/java/com/duqing/missions/retrofit/RetrofitUtils.java new file mode 100644 index 0000000..1260308 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/RetrofitUtils.java @@ -0,0 +1,126 @@ +package com.duqing.missions.retrofit; + + +import com.duqing.missions.BuildConfig; +import com.duqing.missions.retrofit.Interceptor.EncryptInterceptor; +import com.duqing.missions.retrofit.Interceptor.HttpLoggingInterceptor; +import com.duqing.missions.retrofit.api.CommonApiCenter; +import com.duqing.missions.retrofit.converter.DecryptGsonConverterFactory; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; + +/** + * My father is Object, ites purpose of + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-7-9. + */ + +public class RetrofitUtils { + public static String HOST_IP_ADDR; + static RetrofitUtils instance; + Retrofit retrofit/*log输出,驼峰转换*/,unHumpRetrofit/*log输出,不强制驼峰转换*/, + unLogRetrofit/*log不输出,驼峰转换*/,unLogHumpRetorfit/*log不输出,不强制驼峰转换*/; + CommonApiCenter commonApi;//常用接口 + + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .addInterceptor(new EncryptInterceptor()); + OkHttpClient.Builder logBuilder = new OkHttpClient.Builder() + .addInterceptor(new HttpLoggingInterceptor());//log打印拦截器 + + public static RetrofitUtils getInstance() { + if(instance == null){ + instance = new RetrofitUtils(); + } + return instance; + } + + /** + * log输出,gson驼峰转换 + * @return + */ + public <T> T getRetrofit(Class<T> clas) { + if(retrofit == null){ + retrofit = getRetrofit(getOkHttpClient(logBuilder), + new Retrofit.Builder().addConverterFactory(DecryptGsonConverterFactory.create(true))) ; + } + if(!BuildConfig.DEBUG){//正式版 不打印log + return getUnLogRetrofit(clas); + } + return retrofit.create(clas); + } + + /** + * log输出,gson不转换驼峰 + * @return + */ + public <T> T getUnHumpRetrofit(Class<T> clas) { + if(unHumpRetrofit == null){ + unHumpRetrofit = getRetrofit(getOkHttpClient(logBuilder), + new Retrofit.Builder().addConverterFactory(DecryptGsonConverterFactory.create())) ; + } + if(!BuildConfig.DEBUG){//正式版 不打印log + return getUnLogHumpRetorfit(clas); + } + return unHumpRetrofit.create(clas); + } + + /** + * log不输出,gson驼峰转换 + * @return + */ + public <T> T getUnLogRetrofit(Class<T> clas) { + if(unLogRetrofit == null){ + unLogRetrofit = getRetrofit(getOkHttpClient(builder), + new Retrofit.Builder().addConverterFactory(DecryptGsonConverterFactory.create(true))) ; + } + return unLogRetrofit.create(clas); + } + + /** + * log不输出,gson不转换驼峰 + * @return + */ + public <T> T getUnLogHumpRetorfit(Class<T> clas) { + if(unLogHumpRetorfit == null){ + unLogHumpRetorfit = getRetrofit(getOkHttpClient(builder), + new Retrofit.Builder().addConverterFactory(DecryptGsonConverterFactory.create())) ; + } + return unLogHumpRetorfit.create(clas); + } + + private OkHttpClient getOkHttpClient(OkHttpClient.Builder builder){ + return builder.connectTimeout(10, TimeUnit.SECONDS)//设置连接超时时间 + .readTimeout(30, TimeUnit.SECONDS)//设置读取超时时间 + .protocols(Collections.singletonList(Protocol.HTTP_1_1)) + .eventListenerFactory(NetWorkListenear.get()) + .build(); + } + + private Retrofit getRetrofit(OkHttpClient client,Retrofit.Builder builder){ + return builder + //设置OKHttpClient + .client(client) + //设置baseUrl,注意,baseUrl必须后缀"/" + .baseUrl(BuildConfig.ENVIRONMENT.equals("develop")?HOST_IP_ADDR:BuildConfig.HOST_IP_ADDR) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .build(); + } + + + /** + * 常用接口 + * @return + */ + public CommonApiCenter getCommonApi(){ + if(commonApi == null){ + commonApi = getRetrofit(CommonApiCenter.class); + } + return commonApi; + } +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/api/CommonApiCenter.java b/app/src/main/java/com/duqing/missions/retrofit/api/CommonApiCenter.java new file mode 100644 index 0000000..93fc000 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/api/CommonApiCenter.java @@ -0,0 +1,65 @@ +package com.duqing.missions.retrofit.api; + + +import com.duqing.missions.data.ApkUpGradeResult; + +import java.util.Map; + +import io.reactivex.Observable; +import retrofit2.http.Field; +import retrofit2.http.FieldMap; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.GET; +import retrofit2.http.POST; +import retrofit2.http.Query; +import retrofit2.http.QueryMap; +import retrofit2.http.Url; + +/** + * My father is Object, ites purpose of 常用接口 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-7-21. + */ + +public interface CommonApiCenter { + + @GET + Observable<Object> getData(@Url String url, @QueryMap Map<String,String> param); + + @FormUrlEncoded + @POST + Observable<Object> postData(@Url String url, @FieldMap Map<String,String> param); + + + /** + * 分页数据 + * @param url 请求地址 + * @param pageNum 页数 + * @param pageSize 每页数量 + * @param param 其他参数 + * @return + */ + @GET + Observable<Object> getPageData(@Url String url, @Query("pageNum") int pageNum, @Query("pageSize") int pageSize, @QueryMap Map<String,String> param); + + /** + * 分页数据 + * @param url 请求地址 + * @param pageNum 页数 + * @param pageSize 每页数量 + * @param param 其他参数 + * @return + */ + @FormUrlEncoded + @POST + Observable<Object> postPageData(@Url String url, @Field("pageNum") int pageNum, @Field("pageSize") int pageSize, @FieldMap Map<String,String> param); + + /** + * app更新 + * @return + */ + @GET("system/appupgrade/tourist/get/2") + Observable<ApkUpGradeResult> getAppUpdate(); + + +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/api/LoginApiCenter.java b/app/src/main/java/com/duqing/missions/retrofit/api/LoginApiCenter.java new file mode 100644 index 0000000..f061e10 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/api/LoginApiCenter.java @@ -0,0 +1,21 @@ +package com.duqing.missions.retrofit.api; + +import io.reactivex.Observable; +import retrofit2.Call; +import retrofit2.http.Field; +import retrofit2.http.FormUrlEncoded; +import retrofit2.http.POST; + +/** + * Created by Administrator on 2021/11/8 0008. + */ +public interface LoginApiCenter { + + @FormUrlEncoded + @POST("api/v1/login") + Observable<Object> login(@Field("paramString") String str); + + @FormUrlEncoded + @POST("api/v1/login") + Call<Object> loginCall(@Field("paramString") String str); +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonConverterFactory.java b/app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonConverterFactory.java new file mode 100644 index 0000000..497543e --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonConverterFactory.java @@ -0,0 +1,59 @@ +package com.duqing.missions.retrofit.converter; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.reflect.TypeToken; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import okhttp3.RequestBody; +import okhttp3.ResponseBody; +import retrofit2.Converter; +import retrofit2.Retrofit; + +/** + * My father is Object, ites purpose of 解密gson转换 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-7-22. + */ + +public class DecryptGsonConverterFactory extends Converter.Factory { + + public static DecryptGsonConverterFactory create() { + return create(new Gson(),false); + } + + public static DecryptGsonConverterFactory create(boolean transHump) { + return create(new Gson(),transHump); + } + + + public static DecryptGsonConverterFactory create(Gson gson,boolean transHump) { + return new DecryptGsonConverterFactory(gson,transHump); + } + + private final Gson gson; + + private final boolean transHump; + + public DecryptGsonConverterFactory(Gson gson, boolean transHump) { + if (gson == null) throw new NullPointerException("gson == null"); + this.gson = gson; + this.transHump = transHump; + } + + @Override + public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, + Retrofit retrofit) { + TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); + return new DecryptGsonResponseBodyConverter<>(gson, adapter,transHump); + } + + @Override + public Converter<?, RequestBody> requestBodyConverter(Type type, + Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) { + TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type)); + return new GsonRequestBodyConverter<>(gson, adapter); + } +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonResponseBodyConverter.java b/app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonResponseBodyConverter.java new file mode 100644 index 0000000..e8b708a --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/converter/DecryptGsonResponseBodyConverter.java @@ -0,0 +1,95 @@ +package com.duqing.missions.retrofit.converter; + +import android.util.Log; + +import com.duqing.missions.data.BaseApiResult; +import com.duqing.missions.retrofit.utils.RSAUtils; +import com.duqing.missions.util.GsonUtils; +import com.google.gson.Gson; +import com.google.gson.JsonIOException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +import okhttp3.ResponseBody; +import retrofit2.Converter; + +/** + * My father is Object, ites purpose of 解密gson转换器 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-7-22. + */ + +public class DecryptGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> { + private final Gson gson; + private final TypeAdapter<T> adapter; + private final Charset UTF_8 = Charset.forName("UTF-8"); + private final boolean transHump;//驼峰转换 + private final String ENCRYPT = "encrypt"; + + public DecryptGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter, boolean transHump) { + this.gson = gson; + this.adapter = adapter; + this.transHump = transHump; + } + + @Override + public T convert(ResponseBody value) throws IOException { + String response = null; + try { + String val = new String(value.bytes(),UTF_8); + response = decryptJsonStr(val);//解密 + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + e.printStackTrace(); + BaseApiResult apiResult = new BaseApiResult<>(); + apiResult.code = 412; + apiResult.msg = "解密数据出错"+e.getMessage(); + response = new Gson().toJson(apiResult); + } catch (JSONException e) { + e.printStackTrace(); + BaseApiResult apiResult = new BaseApiResult<>(); + apiResult.code = 414; + apiResult.msg = "非标准json"; + response = new Gson().toJson(apiResult); + }catch (Exception e){ + JsonReader jsonReader = gson.newJsonReader(value.charStream()); + return adapter.read(jsonReader); + } finally { + InputStream inputStream = new ByteArrayInputStream(response.getBytes()); + JsonReader jsonReader = gson.newJsonReader(new InputStreamReader(inputStream, UTF_8)); + T result = adapter.read(jsonReader); + if (jsonReader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonIOException("JSON document was not fully consumed."); + } + value.close(); + return result; + } + } + + /** + * 解密json + * @param body + * @return + * @throws Exception + */ + protected String decryptJsonStr(String body) throws Exception { + Log.e("Converter","decryptJsonStr body:"+body); + if(body.indexOf("{") == 0) { + JSONObject json = new JSONObject(body); + body = RSAUtils.decrypt(json.getString(ENCRYPT), RSAUtils.getPublicKey(RSAUtils.PUBLIC_KEY));// + } + return transHump? GsonUtils.toHumpJson(body):body; + } + +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/converter/GsonRequestBodyConverter.java b/app/src/main/java/com/duqing/missions/retrofit/converter/GsonRequestBodyConverter.java new file mode 100644 index 0000000..cdd3ac7 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/converter/GsonRequestBodyConverter.java @@ -0,0 +1,44 @@ +package com.duqing.missions.retrofit.converter; + +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.Charset; + +import okhttp3.MediaType; +import okhttp3.RequestBody; +import okio.Buffer; +import retrofit2.Converter; + +/** + * My father is Object, ites purpose of + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-7-15. + */ + +final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> { + private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8"); + private static final Charset UTF_8 = Charset.forName("UTF-8"); + + private final Gson gson; + private final TypeAdapter<T> adapter; + + GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) { + this.gson = gson; + this.adapter = adapter; + } + + @Override + public RequestBody convert(T value) throws IOException { + Buffer buffer = new Buffer(); + Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8); + JsonWriter jsonWriter = gson.newJsonWriter(writer); + adapter.write(jsonWriter, value); + jsonWriter.close(); + return RequestBody.create(MEDIA_TYPE, buffer.readByteString()); + } +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/utils/HttpPrintUtils.java b/app/src/main/java/com/duqing/missions/retrofit/utils/HttpPrintUtils.java new file mode 100644 index 0000000..ad46bc7 --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/utils/HttpPrintUtils.java @@ -0,0 +1,210 @@ +package com.duqing.missions.retrofit.utils; + +import com.duqing.missions.BuildConfig; +import com.duqing.missions.util.GsonUtils; +import com.duqing.missions.util.MyLog; + +import java.io.EOFException; +import java.util.ArrayList; + +import okio.Buffer; + +/** + * My father is Object, ites purpose of 单例模式 保证synchronized方法的线程安全性 + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2021-5-13. + */ + +public class HttpPrintUtils { + String TAG = "HttpPrintUtils"; + static HttpPrintUtils instance; + public static HttpPrintUtils getInstance(){ + if(instance == null){ + instance = new HttpPrintUtils(); + } + return instance; + } + + /** + * 打印log + * @param list + */ + public synchronized void printLog(ArrayList<String> list, boolean info){ + int length = 0 ;//计算每行最长的长度 + ArrayList<String> logArrays = new ArrayList<>(); + for(String str : list){ + if(str.indexOf("\n")>-1){//有换行的拆分处理 + String[] split = str.split("\n"); + for(String s : split){ + s = s.replace("\t"," ");//缩进替换空格 + if(length<s.length()){ + length = s.length(); + } + } + }else{ + if(length<str.length()){ + length = str.length(); + } + } + } + length+=14;//左右间距 + if(length>300){ + length = 300; + } + String head = "HTTP REQUEST START"; + logArrays.add(" \n\n\n"+"\n"); + //打印头部 + String logHead = "┏"+getEmptyStr((length-head.length())/2,"━")+head+getEmptyStr((length-head.length())/2,"━")+"┓"; + logArrays.add(logHead+"\n"); + //打印内容 + for(String str : list){ + String logStr = ""; + if(str.indexOf("\n")>-1){//内部换行替换 + splitStr(str,logHead.length(),logArrays); + }else{ + if(str.length()> logHead.length()){ + outOflength(str,logHead.length(),logArrays); + }else { + logStr = "┃ " + str + getEmptyStr((length - 14 - str.length()), " "); + //处理中文间距,保证打印无偏差 + logArrays.add(logStr + getEmptyStr((logHead.length() - logStr.length() - 1 - hasCNchar(logStr)), " ") + "┃ \n"); + } + } + } + String end = "HTTP REQUEST END"; + //打印结尾 + logArrays.add("┗"+getEmptyStr((length-end.length())/2,"━")+end+getEmptyStr((length-end.length())/2,"━")+"┛\n"); + logArrays.add(" \n\n\n"); + //Logger.DEFAULT.log(sb.toString());//打印log,避免多个log语句,导致log输出时其他线程的log输出切入此输出阵列内 + if(BuildConfig.DEBUG) { + for(int i = 0 ; i < logArrays.size() ; i ++ ){ + String str = logArrays.get(i); + if (info) { + MyLog.i(TAG , str.replace("\n","")+" "+logArrays.size()+" "+i); + } else { + MyLog.e(TAG , str.replace("\n","")+" "+logArrays.size()+" "+i); + } + } + } + } + + /** + * 拆分 + * @param str + * @param totalLength + * @param list + */ + private void splitStr(String str,int totalLength,ArrayList<String> list){ + String logStr = ""; + String[] split = str.split("\n"); + for(String s : split){ + if(s.length()/totalLength>3){ + s = s.substring(0,totalLength*3)+"..."; + } + s = s.replace("\t"," ");//缩进替换空格 + if(s.indexOf("\":{\"")>-1 || s.indexOf("\":[{\"")>-1 || s.indexOf("\":[[")>-1){//内容非校正缩进,且为json字符规范 + splitStr(s.substring(0,s.indexOf("\":")+2)+ GsonUtils.retractJson(s.substring(s.indexOf("\":")+2)),totalLength,list); + }else { + if(s.length()> totalLength){ + outOflength(s,totalLength,list); + }else { + logStr = "┃ " + s + getEmptyStr((totalLength - 16 - s.length()), " "); + //处理中文间距,保证打印无偏差 + list.add(logStr + getEmptyStr((totalLength - logStr.length() - 1 - hasCNchar(logStr)), " ") + "┃ "/*+logStr.length()+" "+logStr.getBytes().length+" "+(" ").getBytes().length +" "+hasCNchar(s)*/ + "\n"); + } + } + } + } + + + /** + * 超长字符拆分 + * @param str + * @param total + * @param list + */ + private void outOflength(String str,int total,ArrayList<String> list){ + String logStr = ""; + //缩进空间 + String space = getEmptyStr(str.length() - str.trim().length()+4," "); + //要拆分的实际长度 + int length = (str.length()-space.length()); + //每行数量 + int count = total-16-space.length();//总长度-间距-缩进空间是每行的数量 + //最终拆分数量 + int lines = (length/count) + (length%(count)>0?1:0); + + for(int i = 0 ; i < lines ; i ++){ + int start = space.length() + (count * (i+1));//起始位 + int end = start+count;//结束位 + String s = ""; + if(start > str.length() && i > 0){ + break; + } else if(end > str.length() && i > 0 || i == lines-1){ + s = str.substring(start); + } else if(i == 0 ){ + s = str.substring(0, start); + } else { + s = str.substring(start, end); + } + if(i>0) { + s = space + s; + } + logStr = "┃ " + s + getEmptyStr((total - 16 - s.length()), " "); + list.add(logStr + getEmptyStr((total - logStr.length() - 1 - hasCNchar(logStr)), " ") + "┃ \n"); + } + } + + + //返回包含中文数量, + private int hasCNchar(String str){ + str = str.replace("┃",""); + int size = 0 ; + for(int i = 0 ; i < str.length() ; i ++){ + char c = str.charAt(i); + + if((c >= 0x0391 && c <= 0xFFE5)) { //中文字符 + size++; + } + } + return size>0?(int)(size/3.0*2):0;//+1为修正在log中与英文字符短一位 + } + + /** + * 占位符填充 + * @param length 占位数量 + * @param space 占位符 + * @return + */ + private String getEmptyStr(int length,String space){ + StringBuilder sb = new StringBuilder(); + for(int i = 0 ; i < length ; i ++){ + sb.append(space); + } + return sb.toString(); + } + /** + * Returns true if the body in question probably contains human readable text. Uses a small sample + * of code points to detect unicode control characters commonly used in binary file signatures. + */ + static boolean isPlaintext(Buffer buffer) { + try { + Buffer prefix = new Buffer(); + long byteCount = buffer.size() < 64 ? buffer.size() : 64; + buffer.copyTo(prefix, 0, byteCount); + for (int i = 0; i < 16; i++) { + if (prefix.exhausted()) { + break; + } + int codePoint = prefix.readUtf8CodePoint(); + if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { + return false; + } + } + return true; + } catch (EOFException e) { + return false; // Truncated UTF-8 sequence. + } + } + +} diff --git a/app/src/main/java/com/duqing/missions/retrofit/utils/RSAUtils.java b/app/src/main/java/com/duqing/missions/retrofit/utils/RSAUtils.java new file mode 100644 index 0000000..b11526c --- /dev/null +++ b/app/src/main/java/com/duqing/missions/retrofit/utils/RSAUtils.java @@ -0,0 +1,178 @@ +package com.duqing.missions.retrofit.utils; + +import android.util.Base64; + +import java.io.ByteArrayOutputStream; +import java.security.Key; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.Cipher; + + +public class RSAUtils { + + public static final String PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALBGei0scHoOjTLImPHvASaGqYNrdLie0ckWp74Nkqv7FVeXPOvWEG8_jRJCVjJ1grr8SGd9sVY2sxn5XIz7fUEBfx7Vm8m0DaCNBWJpFLGw9xiaVZ2AUKoNyTD4NgZobbwZbt6ZNB6_fggPrGF18pq6GPyCndX1JW8ZiZKj33VBAgMBAAECgYB0q-EX3y7_CnyYXT8l-mxHhJ_T9R6HR89QimcyGqe2nvRMSjSvX7r29xg3OqL0uORzQKHnpcDncELw8SQ5yAbpENeIsD0dvdFlkoyFYU4ljeUbJ46binwwg20TNARjTbpNos9zbhTPh8qixdblxppXA1WC18HtXhixgca5bNG9lQJBAPQfNdpNdDL9l8Tw4hYVuDMszcFuZYbHbm0S4xcwqj-dXNWBztNf5W_K92-N5GIoHbOypkGzjlBjSZi_oKA0HusCQQC42irhw682CG44mKdP6YRDxy6OaauVX4yE9WnsbO8JFSSc9ZCKMMD0F3NGtytDrVMAJxG1iPWXa4ptEdtgwCmDAkAUW1npR1YuPllekdu4jb0bf1v1ClirAYxiyhVnxKYdweiQ4U827yM5zEoP4lwuFzxK1NXqWqe-alkjxK8HTPFbAkAviQLf_adP2MknSrIzzZQSreTeAHR8PA7xnf54KucpScOZjVh3AOSNoH4nYDEC_U5LysA2E5s8Lg5xz9a_QYsrAkEAwV6gNED7_SYDsYyEWimQ6znUb_QSY-sSChnSCY-ILG1wpynBHw_t1Oi3ljl6gL_cYKG1O3uwOtvZtb-Vr1bNkQ"; + + public static final String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwRnotLHB6Do0yyJjx7wEmhqmDa3S4ntHJFqe-DZKr-xVXlzzr1hBvP40SQlYydYK6_EhnfbFWNrMZ-VyM-31BAX8e1ZvJtA2gjQViaRSxsPcYmlWdgFCqDckw-DYGaG28GW7emTQev34ID6xhdfKauhj8gp3V9SVvGYmSo991QQIDAQAB"; + + + /** + * RSA最大加密明文大小 + */ + private static final int MAX_ENCRYPT_BLOCK = 117; + + /** + * RSA最大解密密文大小 + */ + private static final int MAX_DECRYPT_BLOCK = 128; + + static final String KEY_RSA = "RSA"; //android标准 “RSA/ECB/PKCS1Padding” 服务端标准 “RSA” + + /** + * 获取密钥对 + * + * @return 密钥对 + */ + public static KeyPair getKeyPair() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance(KEY_RSA); + generator.initialize(1024); + return generator.generateKeyPair(); + } + + /** + * 获取私钥 + * + * @param privateKey 私钥字符串 + * @return + */ + public static PrivateKey getPrivateKey(String privateKey) throws Exception { + KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA); + byte[] decodedKey = Base64.decode(privateKey.getBytes(), Base64.URL_SAFE); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey); + return keyFactory.generatePrivate(keySpec); + } + + /** + * 获取公钥 + * + * @param publicKey 公钥字符串 + * @return + */ + public static PublicKey getPublicKey(String publicKey) throws Exception { + KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA); + byte[] decodedKey = Base64.decode(publicKey.getBytes(), Base64.URL_SAFE); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey); + return keyFactory.generatePublic(keySpec); + } + + /** + * RSA加密 + * + * @param data 待加密数据 + * @param key 密钥 + * @return + */ + public static String encrypt(String data, Key key) throws Exception { + Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key); + int inputLen = data.getBytes().length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offset = 0; + byte[] cache; + int i = 0; + // 对数据分段加密 + while (inputLen - offset > 0) { + if (inputLen - offset > MAX_ENCRYPT_BLOCK) { + cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK); + } else { + cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset); + } + out.write(cache, 0, cache.length); + i++; + offset = i * MAX_ENCRYPT_BLOCK; + } + byte[] encryptedData = out.toByteArray(); + out.close(); + // 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串 + // 加密后的字符串 + return Base64.encodeToString(encryptedData,Base64.URL_SAFE | Base64.NO_WRAP); + } + + /** + * RSA解密 + * + * @param data 待解密数据 + * @param key 密钥 + * @return + */ + public static String decrypt(String data, Key key) throws Exception { + Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding"); + cipher.init(Cipher.DECRYPT_MODE, key); + byte[] dataBytes = Base64.decode(data.getBytes(), Base64.URL_SAFE); + int inputLen = dataBytes.length; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int offset = 0; + byte[] cache; + int i = 0; + // 对数据分段解密 + while (inputLen - offset > 0) { + if (inputLen - offset > MAX_DECRYPT_BLOCK) { + cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK); + } else { + cache = cipher.doFinal(dataBytes, offset, inputLen - offset); + } + out.write(cache, 0, cache.length); + i++; + offset = i * MAX_DECRYPT_BLOCK; + } + byte[] decryptedData = out.toByteArray(); + out.close(); + // 解密后的内容 + return new String(decryptedData, "UTF-8"); + } + + /** + * 签名 + * + * @param data 待签名数据 + * @param privateKey 私钥 + * @return 签名 + */ + public static String sign(String data, PrivateKey privateKey) throws Exception { + byte[] keyBytes = privateKey.getEncoded(); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA); + PrivateKey key = keyFactory.generatePrivate(keySpec); + Signature signature = Signature.getInstance("MD5withRSA"); + signature.initSign(key); + signature.update(data.getBytes()); + return Base64.encodeToString(signature.sign(),Base64.DEFAULT); + } + + /** + * 验签 + * + * @param srcData 原始字符串 + * @param publicKey 公钥 + * @param sign 签名 + * @return 是否验签通过 + */ + public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception { + byte[] keyBytes = publicKey.getEncoded(); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA); + PublicKey key = keyFactory.generatePublic(keySpec); + Signature signature = Signature.getInstance("MD5withRSA"); + signature.initVerify(key); + signature.update(srcData.getBytes()); + return signature.verify(Base64.decode(sign.getBytes(),Base64.DEFAULT)); + } + +} diff --git a/app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java b/app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java index 896d934..93e7ac4 100644 --- a/app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java +++ b/app/src/main/java/com/duqing/missions/ui/login/view/LoginActivity.java @@ -21,6 +21,12 @@ import com.duqing.missions.R; import com.duqing.missions.base.activities.BaseTitleBarActivity; import com.duqing.missions.databinding.ActivityLoginBinding; +import com.duqing.missions.retrofit.RetrofitUtils; +import com.duqing.missions.retrofit.api.LoginApiCenter; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; public class LoginActivity extends BaseTitleBarActivity<ActivityLoginBinding,LoginViewModel> { @@ -116,8 +122,48 @@ loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - viewModel.login(phoneEdit.getText().toString(), - passwordEditText.getText().toString()); + /*RetrofitUtils.getInstance().getRetrofit(LoginApiCenter.class).login(";lajks;dkfjal;ksjdf") + + .subscribeOn(Schedulers.io())//指定网络请求在io后台线程中进行 + .observeOn(AndroidScheduler.mainThread())//指定observer回调在UI主线程中进行 + .subscribe(new io.reactivex.Observer<Object>() { + @Override + public void onSubscribe(Disposable d) { + v.setEnabled(false); + Log.d(TAG,"onSubscribe"); + } + + @Override + public void onNext(Object value) { + Log.d(TAG,"onNext value:"+value); + + } + + @Override + public void onError(Throwable e) { + v.setEnabled(true); + Log.d(TAG,"onError "+e.getMessage()); + + } + + @Override + public void onComplete() { + v.setEnabled(true); + Log.d(TAG,"onComplete"); + + } + });//发起请求,请求的结果会回调到订阅者observer中*/ + RetrofitUtils.getInstance().getRetrofit(LoginApiCenter.class).loginCall("asdfasdfasdfasdfasdf").enqueue(new Callback<Object>() { + @Override + public void onResponse(Call<Object> call, Response<Object> response) { + + } + + @Override + public void onFailure(Call<Object> call, Throwable t) { + + } + }); } }); diff --git a/app/src/main/java/com/duqing/missions/util/GsonUtils.java b/app/src/main/java/com/duqing/missions/util/GsonUtils.java new file mode 100644 index 0000000..5d572da --- /dev/null +++ b/app/src/main/java/com/duqing/missions/util/GsonUtils.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2017 Baidu, Inc. All Rights Reserved. + */ +package com.duqing.missions.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonParseException; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Json工具类. + */ +public class GsonUtils { + private static Gson gson = new GsonBuilder().create(); + + public static String toJson(Object value) { + return gson.toJson(value); + } + + public static <T> T fromJson(String json, Class<T> classOfT) throws JsonParseException { + return gson.fromJson(json, classOfT); + } + + public static <T> T fromJson(String json, Type typeOfT) throws JsonParseException { + return (T) gson.fromJson(json, typeOfT); + } + + /** + * 将对象转换为驼峰命名的json + * @param value + * @return + */ + public static String toHumpJson(Object value) { + try { + if(value instanceof Collection){ + return convertToHumpJsonArray(new JSONArray(gson.toJson(value)) ).toString(); + }else { + return convertToHumpJsonObj(new JSONObject(gson.toJson(value)) ).toString(); + } + } catch (JSONException e) { + e.printStackTrace(); + return gson.toJson(value); + } + } + + /** + * + * 将json转换为驼峰命名的json + * @param json + * @return + */ + public static String toHumpJson(String json) throws JSONException { + if(json.indexOf("[") == 0){ + return convertToHumpJsonArray(new JSONArray(json) ).toString(); + }else { + return convertToHumpJsonObj(new JSONObject(json) ).toString(); + } + } + + /** + * 驼峰命名转换 + * @param json + * @param classOfT + * @param <T> + * @return + * @throws JsonParseException + */ + public static <T> T fromJsonToHump(String json, Class<T> classOfT) throws JsonParseException, JSONException { + return gson.fromJson(toHumpJson(json), classOfT); + } + + /** + * 驼峰命名转换 + * @param json + * @param typeOfT + * @param <T> + * @return + * @throws JsonParseException + */ + public static <T> T fromJsonToHump(String json, Type typeOfT) throws JsonParseException, JSONException { + return (T) gson.fromJson(toHumpJson(json), typeOfT); + } + + /** + * 转换驼峰命名 + * @param jsonObject + * @return + */ + public static JSONObject convertToHumpJsonObj(JSONObject jsonObject){ + JSONObject temp = new JSONObject(); + Iterator<String> it = jsonObject.keys(); + while ( it.hasNext()){ + String key = it.next(); + String humpKey = humpName(key); + try { + if(jsonObject.get(key) instanceof JSONObject){ + temp.put(humpKey,convertToHumpJsonObj(jsonObject.getJSONObject(key))); + }else if(jsonObject.get(key) instanceof JSONArray){ + temp.put(humpKey,convertToHumpJsonArray(jsonObject.getJSONArray(key))); + }else { + temp.put(humpKey,jsonObject.get(key)); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + return temp; + + } + + public static JSONArray convertToHumpJsonArray(JSONArray array){ + JSONArray jsons = new JSONArray(); + for(int i = 0 ; i < array.length() ; i ++){ + try { + if(array.get(i) instanceof JSONObject){ + jsons.put(convertToHumpJsonObj(array.getJSONObject(i))); + }else if(array.get(i) instanceof JSONArray){ + jsons.put(convertToHumpJsonArray(array.getJSONArray(i))); + }else { + jsons.put(array.get(i)); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + return jsons; + + } + /** + * 将key转换为驼峰 + * @param param + * @return + */ + public static Map convertToHumpMap(Map<String, Object> param){ + Map temp = new TreeMap(); + for(String key: param.keySet()){ + String humpKey = humpName(key); + if(param.get(key) instanceof Map){ + temp.put(humpKey,convertToHumpMap((Map<String, Object>) param.get(key))); + }else if(param.get(key) instanceof List){ + temp.put(humpKey,convertToHumpList((List)param.get(key))); + }else { + temp.put(humpKey,param.get(key)); + } + } + return temp; + } + + + public static List convertToHumpList(List list){ + List ars = new ArrayList(); + for(Object object : list){ + if(object instanceof Map){ + ars.add(convertToHumpMap((Map)object)); + }else if(object instanceof List){ + ars.add(convertToHumpList((List)object)); + }else { + ars.add(object); + } + } + return ars; + } + /** + * 驼峰命名 + * @param name + * @return + */ + public static String humpName(String name){ + String[] strings = name.split("_"); + StringBuilder sb = new StringBuilder(); + sb.append(strings[0]); + for(int i = 1 ; i < strings.length ; i ++){ + sb.append(toUperFirst(strings[i])); + } + if(sb.toString().equals("new")){//关键字 转成大写 + return "NEW"; + } + return sb.toString(); + } + + /** + * 首字母大写 + * @param name + * @return + */ + public static String toUperFirst(String name){ + return name.substring(0,1).toUpperCase()+name.substring(1); + } + + /** + * json字符串缩进 + * @param json + * @return + */ + public static String retractJson(String json){ + int level = 0 ; + StringBuffer jsonForMatStr = new StringBuffer(); + for(int index=0;index<json.length();index++)//将字符串中的字符逐个按行输出 + { + //获取s中的每个字符 + char c = json.charAt(index); + // System.out.println(s.charAt(index)); + + //level大于0并且jsonForMatStr中的最后一个字符为\n,jsonForMatStr加入\t + if (level > 0 && '\n' == jsonForMatStr.charAt(jsonForMatStr.length() - 1)) { + jsonForMatStr.append(getLevelStr(level)); + // System.out.println("123"+jsonForMatStr); + } + //遇到"{"和"["要增加空格和换行,遇到"}"和"]"要减少空格,以对应,遇到","要换行 + switch (c) { + case '{': + case '[': + jsonForMatStr.append(c + "\n"); + level++; + break; + case ',': + if(index>0 && index < json.length()-2 && (json.charAt(index-1) != '\n') && json.charAt(index+1) == '"'){ + jsonForMatStr.append(c + "\n"); + }else{ + jsonForMatStr.append(c); + } + break; + case '}': + case ']': + jsonForMatStr.append("\n"); + level--; + jsonForMatStr.append(getLevelStr(level)); + jsonForMatStr.append(c); + break; + default: + jsonForMatStr.append(c); + break; + } + } + return jsonForMatStr.toString(); + } + private static String getLevelStr(int level) { + StringBuffer levelStr = new StringBuffer(); + for (int levelI = 0; levelI < level; levelI++) { + levelStr.append("\t");//\t或空格 + } + return levelStr.toString(); + } +} diff --git a/app/src/main/java/com/duqing/missions/util/NetWorkUtils.java b/app/src/main/java/com/duqing/missions/util/NetWorkUtils.java new file mode 100644 index 0000000..138e22d --- /dev/null +++ b/app/src/main/java/com/duqing/missions/util/NetWorkUtils.java @@ -0,0 +1,212 @@ +package com.duqing.missions.util; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.MalformedURLException; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * My father is Object, ites purpose of + * + * @purpose Created by Runt (qingingrunt2010@qq.com) on 2019-3-19. + */ + +public class NetWorkUtils { + + + /** + * 获取外网IP地址 + * @return + */ + public static String getNetIp() { + final Map<String,String> param = new HashMap<>(); + Thread thread = new Thread(new Runnable() { + @Override + public void run() { + String line = ""; + URL infoUrl = null; + InputStream inStream = null; + try { + infoUrl = new URL("http://pv.sohu.com/cityjson?ie=utf-8"); + URLConnection connection = infoUrl.openConnection(); + HttpURLConnection httpConnection = (HttpURLConnection) connection; + int responseCode = httpConnection.getResponseCode(); + if (responseCode == HttpURLConnection.HTTP_OK) { + inStream = httpConnection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "utf-8")); + StringBuilder strber = new StringBuilder(); + while ((line = reader.readLine()) != null) + strber.append(line + "\n"); + inStream.close(); + // 从反馈的结果中提取出IP地址 + int start = strber.indexOf("{"); + int end = strber.indexOf("}"); + String json = strber.substring(start, end + 1); + if (json != null) { + try { + JSONObject jsonObject = new JSONObject(json); + line = jsonObject.optString("cip"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + param.put("ip",line); + } + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + try { + thread.start(); + thread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return param.get("ip"); + } + + /*** + * 获取局域网ip + * @param context + * @return + */ + @SuppressLint("MissingPermission") + public static String getLocalIpAddress(Context context) { + + ConnectivityManager connect = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + //检查网络连接 + NetworkInfo info = connect.getActiveNetworkInfo(); + + if (info == null) { + return ""; + } + int netType = info.getType(); + int netSubtype = info.getSubtype(); + + if (netType == ConnectivityManager.TYPE_WIFI) { //WIFI + return getWifiIpAddress(context); + } else /*if (netType == ConnectivityManager.TYPE_MOBILE) { //MOBILE + return getGPRSIpAddress(); + } else */if (netType == ConnectivityManager.TYPE_ETHERNET) { //MOBILE + return getEthernetIp(); + } else { + return ""; + } + } + + /** + * 使用wifi + * @param context + * @return + */ + @SuppressLint("MissingPermission") + public static String getWifiIpAddress(Context context) { + + //获取wifi服务 + WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + //判断wifi是否开启 + // if (!wifiManager.isWifiEnabled()) { + // <span style="white-space:pre"> </span> wifiManager.setWifiEnabled(true); + // } + WifiInfo wifiInfo = wifiManager.getConnectionInfo(); + int ipAddress = wifiInfo.getIpAddress(); + String ip = intToIp(ipAddress); + return ip; + } + + private static String intToIp(int i) { + return (i & 0xFF) + "." + + ((i >> 8) & 0xFF) + "." + + ((i >> 16) & 0xFF) + "." + + (i >> 24 & 0xFF); + } + /** + * 使用GPRS + * @return + */ + public static String getGPRSIpAddress() { + try { + for (Enumeration<NetworkInterface> en = NetworkInterface + .getNetworkInterfaces(); en.hasMoreElements(); ) { + NetworkInterface intf = en.nextElement(); + for (Enumeration<InetAddress> enumIpAddr = intf + .getInetAddresses(); enumIpAddr.hasMoreElements(); ) { + InetAddress inetAddress = enumIpAddr.nextElement(); + if (!inetAddress.isLoopbackAddress()) { + return inetAddress.getHostAddress().toString(); + } + } + } + } catch (SocketException ex) { + MyLog.e("Exception", ex.toString()); + } + return "127.0.0.1"; + } + + public static String getEthernetIp() { + String hostIp = null; + try { + Enumeration nis = NetworkInterface.getNetworkInterfaces(); + InetAddress ia = null; + while (nis.hasMoreElements()) { + NetworkInterface ni = (NetworkInterface) nis.nextElement(); + Enumeration<InetAddress> ias = ni.getInetAddresses(); + while (ias.hasMoreElements()) { + ia = ias.nextElement(); + if (ia instanceof Inet6Address) { + continue;// skip ipv6 + } + String ip = ia.getHostAddress(); + if (!"127.0.0.1".equals(ip)) { + hostIp = ia.getHostAddress(); + break; + } + } + } + } catch (SocketException e) { + Log.i("yao", "SocketException"); + e.printStackTrace(); + } + return hostIp; + } + + + /** + * 将得到的int类型的IP转换为String类型 + * + * @param ip + * @return + */ + public static String intIP2StringIP(int ip) { + return (ip & 0xFF) + "." + + ((ip >> 8) & 0xFF) + "." + + ((ip >> 16) & 0xFF) + "." + + (ip >> 24 & 0xFF); + } + +} diff --git a/app/src/main/res/anim/slide_in_left.xml b/app/src/main/res/anim/slide_in_left.xml new file mode 100644 index 0000000..59d1c77 --- /dev/null +++ b/app/src/main/res/anim/slide_in_left.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <translate android:fromXDelta="-50%p" android:toXDelta="0" + android:duration="@android:integer/config_mediumAnimTime"/> + <alpha android:fromAlpha="0.0" android:toAlpha="1.0" + android:duration="@android:integer/config_mediumAnimTime" /> +</set> diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml new file mode 100644 index 0000000..08bc86b --- /dev/null +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/mobile_navigation" + app:startDestination="@+id/navigation_home"> + + <fragment + android:id="@+id/navigation_home" + android:name="com.duqing.missions.ui.main.home.HomeFragment" + android:label="@string/title_home" + tools:layout="@layout/fragment_home" /> + + <fragment + android:id="@+id/navigation_hall" + android:name="com.duqing.missions.ui.main.hall.HallFragment" + android:label="@string/title_hall" + tools:layout="@layout/layout_tab_viewpager" /> + + <fragment + android:id="@+id/navigation_dynamic" + android:name="com.duqing.missions.ui.main.dynamic.DynamicFragment" + android:label="@string/title_dynamic" + tools:layout="@layout/layout_tab_viewpager" /> + <fragment + android:id="@+id/navigation_mine" + android:name="com.duqing.missions.ui.main.mine.MineFragment" + android:label="@string/title_mine" + tools:layout="@layout/fragment_mine" /> +</navigation> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eaaea45..6b5f248 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ <resources> - <string name="app_name">Missions</string> <string name="title_home">Home</string> <string name="title_hall">Hall</string> <string name="title_dynamic">Dynamic</string> diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..dca93c0 --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<network-security-config> + <base-config cleartextTrafficPermitted="true" /> +</network-security-config> \ No newline at end of file -- Gitblit v1.9.1