commit 80cc1248b22014225bf25a53dbfa9d474d81c31e Author: wangcongtao Date: Mon Dec 23 15:08:04 2019 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..603b140773 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..681f41ae2a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000000..d291b3d7cd --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,16 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..29bb4c57c1 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000000..7f68460d8b --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000000..f266ec223b --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,29 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + buildToolsVersion rootProject.ext.android.buildToolsVersion + defaultConfig { + applicationId rootProject.ext.android.applicationId + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation rootProject.ext.dependencies.androidxappcompat + implementation rootProject.ext.dependencies.androidxconstraintlayout + testImplementation rootProject.ext.dependencies.junit + androidTestImplementation rootProject.ext.dependencies.androidxjunit + androidTestImplementation rootProject.ext.dependencies.androidxespressocore +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/androidTest/java/com/mogo/launcher/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/mogo/launcher/ExampleInstrumentedTest.java new file mode 100644 index 0000000000..2784cd1c3f --- /dev/null +++ b/app/src/androidTest/java/com/mogo/launcher/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.mogo.launcher; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith( AndroidJUnit4.class ) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals( "com.mogo.launcher", appContext.getPackageName() ); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..a1d4aa5c1c --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/mogo/launcher/MogoApplication.java b/app/src/main/java/com/mogo/launcher/MogoApplication.java new file mode 100644 index 0000000000..7a7ac48037 --- /dev/null +++ b/app/src/main/java/com/mogo/launcher/MogoApplication.java @@ -0,0 +1,17 @@ +package com.mogo.launcher; + +import android.app.Application; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * Launcher application + */ +public class MogoApplication extends Application { + + @Override + public void onCreate() { + super.onCreate(); + } +} diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..1f6bb29060 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..0d025f9bf6 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..eca70cfe52 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..eca70cfe52 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..898f3ed59a Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000..dffca3601e Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..64ba76f75e Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000..dae5e08234 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..e5ed46597e Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..14ed0af350 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b0907cac3b Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..d8ae031549 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2c18de9e66 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..beed3cdd2c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..69b22338c6 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #008577 + #00574B + #D81B60 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..146d275293 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + MogoLauncher + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..e6b4523fd9 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/app/src/test/java/com/mogo/launcher/ExampleUnitTest.java b/app/src/test/java/com/mogo/launcher/ExampleUnitTest.java new file mode 100644 index 0000000000..cde4cc10d4 --- /dev/null +++ b/app/src/test/java/com/mogo/launcher/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.mogo.launcher; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals( 4, 2 + 2 ); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..53d9475dce --- /dev/null +++ b/build.gradle @@ -0,0 +1,45 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +apply from: "config.gradle" +buildscript { + ext.kotlin_version = '1.3.61+' + repositories { + maven { + url 'http://maven.aliyun.com/nexus/content/groups/public/' + } + maven { + url 'http://nexus.zhidaoauto.com/repository/maven-releases/' + } + maven { + url 'http://nexus.zhidaoauto.com/repository/maven-public/' + } + google() + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.5.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + maven { + url 'http://maven.aliyun.com/nexus/content/groups/public/' + } + maven { + url 'http://nexus.zhidaoauto.com/repository/maven-releases/' + } + maven { + url 'http://nexus.zhidaoauto.com/repository/maven-public/' + } + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/config.gradle b/config.gradle new file mode 100644 index 0000000000..5d361f9b3d --- /dev/null +++ b/config.gradle @@ -0,0 +1,53 @@ +ext { + android = [ + applicationId : "com.mogo.launcher", + compileSdkVersion: 29, + buildToolsVersion: "29.0.2", + minSdkVersion : 19, + targetSdkVersion : 22, + versionCode : 1, + versionName : "1.0.0", + ] + + dependencies = [ + // androidx + androidxappcompat : "androidx.appcompat:appcompat:1.0.2", + androidxconstraintlayout: "androidx.constraintlayout:constraintlayout:1.1.3", + // 测试 + junit : "junit:junit:4.12", + androidxjunit : "androidx.test.ext:junit:1.1.0", + androidxespressocore : "androidx.test.espresso:espresso-core:3.1.1", + // 地图 + amapnavi3dmap : "com.amap.api:navi-3dmap:latest.integration", + amapsearch : "com.amap.api:search:latest.integration", + amaplocation : "com.amap.api:location:latest.integration", + // json 转换 + gson : "com.google.code.gson:gson:2.8.4", + // 内存泄漏检测 + debugleakcanary : "com.squareup.leakcanary:leakcanary-android:1.6.1", + releaseleakcanary : "com.squareup.leakcanary:leakcanary-android-no-op:1.6.1", + testleakcanary : "com.squareup.leakcanary:leakcanary-android-no-op:1.6.1", + //rxJava + rxjava : "io.reactivex.rxjava2:rxjava:2.2.2", + rxandroid : "io.reactivex.rxjava2:rxandroid:2.1.0", + // arouter + arouter : "com.alibaba:arouter-api:1.5.0", + aroutercompiler : "com.alibaba:arouter-compiler:1.2.2", + // glide + glide : 'com.github.bumptech.glide:glide:4.8.0', + glideokhttp3 : 'com.github.bumptech.glide:okhttp3-integration:4.8.0', + glideanno : 'com.github.bumptech.glide:annotations:4.8.0', + glidecompiler : 'com.github.bumptech.glide:compiler:4.8.0', + supportannos : "com.android.support:support-annotations:28.0.0", + // fresco + fresco : 'com.facebook.fresco:fresco:1.1.0', + // 公司服务 - 语音 + aiassist : "com.zhidaoauto.common:service:1.0.4", + + // retrofit + retrofit : "com.squareup.retrofit2:retrofit:2.3.0", + retrofitadapter : "com.squareup.retrofit2:adapter-rxjava:2.1.0", + retrofitconvertergson : "com.squareup.retrofit2:converter-gson:2.3.0", + retrofitconverterscalars: "com.squareup.retrofit2:converter-scalars:2.1.0", + ] +} \ No newline at end of file diff --git a/foudations/mogo-commons/.gitignore b/foudations/mogo-commons/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/foudations/mogo-commons/.gitignore @@ -0,0 +1 @@ +/build diff --git a/foudations/mogo-commons/build.gradle b/foudations/mogo-commons/build.gradle new file mode 100644 index 0000000000..351e0d375c --- /dev/null +++ b/foudations/mogo-commons/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + api rootProject.ext.dependencies.aiassist + implementation project(":foudations:mogo-utils") +} diff --git a/foudations/mogo-commons/consumer-rules.pro b/foudations/mogo-commons/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/foudations/mogo-commons/proguard-rules.pro b/foudations/mogo-commons/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/foudations/mogo-commons/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/foudations/mogo-commons/src/main/AndroidManifest.xml b/foudations/mogo-commons/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..ddb5e04218 --- /dev/null +++ b/foudations/mogo-commons/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/foudations/mogo-commons/src/main/java/com/mogo/commons/AbsMogoApplication.java b/foudations/mogo-commons/src/main/java/com/mogo/commons/AbsMogoApplication.java new file mode 100644 index 0000000000..eeb155ead3 --- /dev/null +++ b/foudations/mogo-commons/src/main/java/com/mogo/commons/AbsMogoApplication.java @@ -0,0 +1,24 @@ +package com.mogo.commons; + +import android.app.Application; + +/** + * @author congtaowang + * @since 2019-12-23 + *

+ * 描述 + */ +public class AbsMogoApplication extends Application { + + private static Application sApp; + + @Override + public void onCreate() { + super.onCreate(); + sApp = this; + } + + public static Application getApp() { + return sApp; + } +} diff --git a/foudations/mogo-commons/src/main/java/com/mogo/commons/data/BaseData.java b/foudations/mogo-commons/src/main/java/com/mogo/commons/data/BaseData.java new file mode 100644 index 0000000000..82693e807d --- /dev/null +++ b/foudations/mogo-commons/src/main/java/com/mogo/commons/data/BaseData.java @@ -0,0 +1,12 @@ +package com.mogo.commons.data; + +import java.io.Serializable; + +/** + * Created by congtaowang on 2019/1/7. + */ +public class BaseData implements Serializable, Cloneable { + + public int code = -1; + public String msg; +} diff --git a/foudations/mogo-commons/src/main/java/com/mogo/commons/network/AllAllowedHostnameVerifier.java b/foudations/mogo-commons/src/main/java/com/mogo/commons/network/AllAllowedHostnameVerifier.java new file mode 100644 index 0000000000..21368f008f --- /dev/null +++ b/foudations/mogo-commons/src/main/java/com/mogo/commons/network/AllAllowedHostnameVerifier.java @@ -0,0 +1,18 @@ +package com.mogo.commons.network; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLSession; + +/** + * @author congtaowang + * @since 2019-08-30 + *

+ * 信任所有域名 + */ +public class AllAllowedHostnameVerifier implements HostnameVerifier { + + @Override + public boolean verify( String hostname, SSLSession session ) { + return true; + } +} diff --git a/foudations/mogo-commons/src/main/java/com/mogo/commons/network/X509TrustManagerImpl.java b/foudations/mogo-commons/src/main/java/com/mogo/commons/network/X509TrustManagerImpl.java new file mode 100644 index 0000000000..7fda1722cc --- /dev/null +++ b/foudations/mogo-commons/src/main/java/com/mogo/commons/network/X509TrustManagerImpl.java @@ -0,0 +1,30 @@ +package com.mogo.commons.network; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +/** + * @author congtaowang + * @since 2019-08-30 + *

+ * 描述 + */ +public class X509TrustManagerImpl implements X509TrustManager { + + @Override + public void checkClientTrusted( X509Certificate[] chain, String authType ) throws CertificateException { + + } + + @Override + public void checkServerTrusted( X509Certificate[] chain, String authType ) throws CertificateException { + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } +} diff --git a/foudations/mogo-commons/src/main/java/com/mogo/commons/voice/AIAssist.java b/foudations/mogo-commons/src/main/java/com/mogo/commons/voice/AIAssist.java new file mode 100644 index 0000000000..7f50aeae8e --- /dev/null +++ b/foudations/mogo-commons/src/main/java/com/mogo/commons/voice/AIAssist.java @@ -0,0 +1,102 @@ +package com.mogo.commons.voice; + +import android.content.Context; + +import com.zhidao.auto.platform.voice.VoiceClient; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author congtaowang + * @since 2019-10-03 + *

+ * 语音助手通信助手 + */ +public class AIAssist implements VoiceClient.VoiceCmdCallBack { + + private static volatile AIAssist sInstance; + + public static AIAssist getInstance( Context context ) { + if ( sInstance == null ) { + synchronized ( AIAssist.class ) { + if ( sInstance == null ) { + sInstance = new AIAssist( context ); + } + } + } + return sInstance; + } + + public synchronized void release() { + sInstance = null; + } + + private final VoiceClient mVoiceClient; + private Map< String, IMogoVoiceCmdCallBack > mUnWakeupCmdMap = new HashMap<>(); + + private AIAssist( Context context ) { + // private constructor + mVoiceClient = new VoiceClient( context.getApplicationContext() ); + mVoiceClient.setCallBack( this ); + } + + @Override + public void onCmdSelected( String cmd ) { + final IMogoVoiceCmdCallBack cmdCallBack = mUnWakeupCmdMap.get( cmd ); + if ( cmdCallBack != null ) { + cmdCallBack.onCmdSelected( cmd ); + } + } + + @Override + public void onCmdAction( String speakText ) { + + } + + @Override + public void onCmdCancel( String speakText ) { + + } + + @Override + public void onSpeakEnd( String speakText ) { + IMogoVoiceCmdCallBack callBack = mUnWakeupCmdMap.get( speakText ); + if ( callBack != null ) { + callBack.onSpeakEnd( speakText ); + } + } + + @Override + public void onSpeakSelectTimeOut( String speakText ) { + IMogoVoiceCmdCallBack callBack = mUnWakeupCmdMap.get( speakText ); + if ( callBack != null ) { + callBack.onSpeakSelectTimeOut( speakText ); + } + } + + public void speakTTSVoice( String text ) { + try { + mVoiceClient.speakDefault( text ); + } catch ( Exception e ) { + } + } + + public void registerUnWakeupCommand( String cmd, String[] cmdWords, IMogoVoiceCmdCallBack callBack ) { + mUnWakeupCmdMap.put( cmd, callBack ); + mVoiceClient.registerCustomWakeupCmd( cmd, cmdWords ); + } + + public void unregisterUnWakeupCommand( String cmd ) { + mUnWakeupCmdMap.remove( cmd ); + mVoiceClient.unRegisterCustomWakeupCmd( cmd ); + } + + public void registerTTSCallback( String tts, IMogoVoiceCmdCallBack cmdCallBack ) { + mUnWakeupCmdMap.put( tts, cmdCallBack ); + } + + public void unregisterTTSCallback( String tts ) { + mUnWakeupCmdMap.remove( tts ); + } +} diff --git a/foudations/mogo-commons/src/main/java/com/mogo/commons/voice/IMogoVoiceCmdCallBack.java b/foudations/mogo-commons/src/main/java/com/mogo/commons/voice/IMogoVoiceCmdCallBack.java new file mode 100644 index 0000000000..b213d117fa --- /dev/null +++ b/foudations/mogo-commons/src/main/java/com/mogo/commons/voice/IMogoVoiceCmdCallBack.java @@ -0,0 +1,38 @@ +package com.mogo.commons.voice; + +public interface IMogoVoiceCmdCallBack { + /** + * 免唤醒命令响应回调 + * + * @param cmd + */ + void onCmdSelected( String cmd ); + + /** + * 语音播报临时免唤醒“确定”命令 + * + * @param speakText 播报内容 + */ + void onCmdAction( String speakText ); + + /** + * 语音播报临时免唤醒“取消”命令 + * + * @param speakText 播报内容 + */ + void onCmdCancel( String speakText ); + + /** + * 语音播报完毕 + * + * @param speakText 播报内容 + */ + void onSpeakEnd( String speakText ); + + /** + * 语音播报完临时命令选择超时 + * + * @param speakText 播报内容 + */ + void onSpeakSelectTimeOut( String speakText ); +} \ No newline at end of file diff --git a/foudations/mogo-commons/src/main/res/values/colors.xml b/foudations/mogo-commons/src/main/res/values/colors.xml new file mode 100644 index 0000000000..a6b3daec93 --- /dev/null +++ b/foudations/mogo-commons/src/main/res/values/colors.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/foudations/mogo-commons/src/main/res/values/strings.xml b/foudations/mogo-commons/src/main/res/values/strings.xml new file mode 100644 index 0000000000..0bfdd06f8b --- /dev/null +++ b/foudations/mogo-commons/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + mogo-commons + diff --git a/foudations/mogo-utils/.gitignore b/foudations/mogo-utils/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/foudations/mogo-utils/.gitignore @@ -0,0 +1 @@ +/build diff --git a/foudations/mogo-utils/build.gradle b/foudations/mogo-utils/build.gradle new file mode 100644 index 0000000000..11eb8bcdca --- /dev/null +++ b/foudations/mogo-utils/build.gradle @@ -0,0 +1,40 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + api rootProject.ext.dependencies.glide + implementation rootProject.ext.dependencies.glideanno + implementation rootProject.ext.dependencies.glideokhttp3 + implementation rootProject.ext.dependencies.supportannos + annotationProcessor rootProject.ext.dependencies.supportannos + annotationProcessor rootProject.ext.dependencies.glidecompiler + api rootProject.ext.dependencies.retrofit + api rootProject.ext.dependencies.retrofitadapter + api rootProject.ext.dependencies.retrofitconvertergson + api rootProject.ext.dependencies.retrofitconverterscalars + implementation rootProject.ext.dependencies.androidxappcompat +} diff --git a/foudations/mogo-utils/consumer-rules.pro b/foudations/mogo-utils/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/foudations/mogo-utils/proguard-rules.pro b/foudations/mogo-utils/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/foudations/mogo-utils/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/foudations/mogo-utils/src/androidTest/java/com/mogo/utils/ExampleInstrumentedTest.java b/foudations/mogo-utils/src/androidTest/java/com/mogo/utils/ExampleInstrumentedTest.java new file mode 100644 index 0000000000..dca1040e3f --- /dev/null +++ b/foudations/mogo-utils/src/androidTest/java/com/mogo/utils/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.mogo.utils; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith( AndroidJUnit4.class ) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals( "com.mogo.utils.test", appContext.getPackageName() ); + } +} diff --git a/foudations/mogo-utils/src/main/AndroidManifest.xml b/foudations/mogo-utils/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..7447df8a92 --- /dev/null +++ b/foudations/mogo-utils/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ActivityLifecycleManager.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ActivityLifecycleManager.java new file mode 100644 index 0000000000..5297a356e0 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ActivityLifecycleManager.java @@ -0,0 +1,335 @@ +package com.mogo.utils; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.Application; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; + +import java.util.ArrayList; +import java.util.HashMap; + +public class ActivityLifecycleManager { + private Application mApplication; + /** + * 当前出于 Start 状态的 Activity + */ + private ArrayList< Activity > mStartedActivities = new ArrayList<>(); + + private ArrayList mAppStateListeners = new ArrayList<>(); + + /** + * 当前存活的 Activity + */ + private HashMap< Activity, ActivityTrace> mAliveActivities = new HashMap<>(); + + /** + * App 是否出于前台 + */ + private volatile boolean mIsAppActive = false; + + private Activity mCurrentResumedActivity; + + /** + * Home 键事件广播的接受器 + */ + private HomeKeyEventReceiver mHomeKeyEventReceiver; + /** + * Home 键事件监听者列表 + */ + private ArrayList mHomeKeyEventListeners = new ArrayList<>(); + + private DefActivityLifecycleCallbacks mInnerActivityListener = new DefActivityLifecycleCallbacks() { + @Override + public void onActivityStarted( Activity activity) { + if (mStartedActivities.isEmpty()) { + mIsAppActive = true; + notifyAppStateChanged( AppStateListener.ACTIVE); + } + mStartedActivities.add(activity); + + ActivityTrace trace = mAliveActivities.get(activity); + if (trace != null) { + trace.startCnt++; + } + } + + @Override + public void onActivityStopped( Activity activity) { + mStartedActivities.remove(activity); + if (mStartedActivities.isEmpty()) { + mIsAppActive = false; + notifyAppStateChanged( AppStateListener.INACTIVE); + } + + ActivityTrace trace = mAliveActivities.get(activity); + if (trace != null) { + trace.stopCnt++; + } + } + + @Override + public void onActivityResumed( Activity activity) { + mCurrentResumedActivity = activity; + ActivityTrace trace = mAliveActivities.get(activity); + if (trace != null) { + trace.resumeCnt++; + } + } + + @Override + public void onActivityPaused( Activity activity) { + ActivityTrace trace = mAliveActivities.get(activity); + if (trace != null) { + trace.pauseCnt++; + } + } + + @Override + public void onActivityCreated( Activity activity, Bundle savedInstanceState) { + mAliveActivities.put(activity, new ActivityTrace(activity)); + } + + @Override + public void onActivityDestroyed( Activity activity) { + mAliveActivities.remove(activity); + } + }; + + private ActivityLifecycleManager() {} + + public static ActivityLifecycleManager getInstance() { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder{ + private static final ActivityLifecycleManager INSTANCE = new ActivityLifecycleManager(); + } + + public void start( Application application) { + this.mApplication = application; + registerInnerActivityListener(); + registerHomeKeyEventReceiver(); + } + + public void stop(){ + unregisterActivityLifecycleCallbacks(mInnerActivityListener); + unregisterHomeKeyEventReceiver(); + } + + /** + * 注册 Activity 生命周期的回调 + * + * @param callbacks Activity 生命周期的回调 + */ + public void registerActivityLifecycleCallbacks( Application.ActivityLifecycleCallbacks callbacks) { + if (mApplication == null) { + return; + } + + if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return; + } + + mApplication.registerActivityLifecycleCallbacks(callbacks); + } + + /** + * 取消注册 Activity 生命周期的回调 + * + * @param callbacks Activity 生命周期的回调 + */ + public void unregisterActivityLifecycleCallbacks( Application.ActivityLifecycleCallbacks callbacks) { + if (mApplication == null) { + return; + } + + if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return; + } + + mApplication.unregisterActivityLifecycleCallbacks(callbacks); + } + + /** + * 应用是否出于前台 + */ + public boolean isAppActive() { + return mIsAppActive; + } + + public Activity getCurrentActivity(){ + return mCurrentResumedActivity; + } + + /** + * 添加应用状态的监听 + */ + public void addAppStateListener(AppStateListener listener) { + synchronized (mAppStateListeners) { + mAppStateListeners.add(listener); + } + } + + /** + * 移除应用状态的监听 + * + * @param listener + */ + public void removeAppStateListener(AppStateListener listener) { + synchronized (mAppStateListeners) { + mAppStateListeners.remove(listener); + } + } + + /** + * 添加 home 键的事件监听 + */ + public void addHomeKeyEventListener(HomeKeyEventListener listener) { + if (listener == null) { + return; + } + synchronized (mHomeKeyEventListeners) { + mHomeKeyEventListeners.add(listener); + } + } + + /** + * 移除 home 键的事件监听 + */ + public void removeHomeKeyEventListener(HomeKeyEventListener listener) { + if (listener == null) { + return; + } + + synchronized (mHomeKeyEventListeners) { + mHomeKeyEventListeners.remove(listener); + } + } + + /** + * 注册Activity生命周期的监听 + */ + private void registerInnerActivityListener() { + registerActivityLifecycleCallbacks(mInnerActivityListener); + } + + private void notifyAppStateChanged(int state) { + Object[] listeners = collectAppStateListeners(); + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + ((AppStateListener) listeners[i]).onStateChanged(state); + } + } + } + + private Object[] collectAppStateListeners() { + Object[] listeners = null; + synchronized (mAppStateListeners) { + if (mAppStateListeners.size() > 0) { + listeners = mAppStateListeners.toArray(); + } + } + return listeners; + } + + private Object[] collectHomeKeyEventListeners() { + Object[] listeners = null; + synchronized (mHomeKeyEventListeners) { + if (mHomeKeyEventListeners.size() > 0) { + listeners = mHomeKeyEventListeners.toArray(); + } + } + return listeners; + } + + private void registerHomeKeyEventReceiver() { + android.content.IntentFilter filter = new android.content.IntentFilter(); + filter.addAction( Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + + mHomeKeyEventReceiver = new HomeKeyEventReceiver(); + mApplication.registerReceiver(mHomeKeyEventReceiver, filter); + } + + private void unregisterHomeKeyEventReceiver() { + mApplication.unregisterReceiver(mHomeKeyEventReceiver); + } + + private void onHomeKeyPressed() { + Object[] listeners = collectHomeKeyEventListeners(); + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + ((HomeKeyEventListener) listeners[i]).onHomeKeyPressed(); + } + } + } + + @TargetApi( Build.VERSION_CODES.ICE_CREAM_SANDWICH) + public static abstract class DefActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks { + @Override + public void onActivityCreated( Activity activity, Bundle savedInstanceState) {} + @Override + public void onActivityStarted( Activity activity) {} + @Override + public void onActivityResumed( Activity activity) {} + @Override + public void onActivityPaused( Activity activity) {} + @Override + public void onActivityStopped( Activity activity) {} + @Override + public void onActivitySaveInstanceState( Activity activity, Bundle outState) {} + @Override + public void onActivityDestroyed( Activity activity) {} + } + + public interface AppStateListener { + int INACTIVE = 0; + int ACTIVE = 1; + /** + * App 状态的回调 + */ + void onStateChanged( int state ); + + } + + /** + * home 键的监听 + */ + public interface HomeKeyEventListener { + void onHomeKeyPressed(); + } + + /** + * Activity 的生命周期调用痕迹 + */ + static class ActivityTrace { + Activity activity; + int resumeCnt; + int pauseCnt; + int startCnt; + int stopCnt; + + ActivityTrace( Activity activity) { + this.activity = activity; + } + } + + private final class HomeKeyEventReceiver extends BroadcastReceiver { + private final String SYSTEM_DIALOG_REASON_KEY = "reason"; + private final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; + + @Override + public void onReceive( Context context, Intent intent) { + String action = intent.getAction(); + if ( Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { + String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); + if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) { + onHomeKeyPressed(); + } + } + } + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ActivityStack.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ActivityStack.java new file mode 100644 index 0000000000..b315ae569c --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ActivityStack.java @@ -0,0 +1,84 @@ +package com.mogo.utils; + +import android.app.Activity; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.Iterator; + +public class ActivityStack { + private static final Deque< Activity > ACTIVITY_STACK = new ArrayDeque<>(); + + public static synchronized void addActivity( Activity activity) { + if(activity != null){ + ACTIVITY_STACK.offer(activity); + } + } + + public static synchronized Activity currentActivity() { + return ACTIVITY_STACK.peekLast(); + } + + public static synchronized void finishCurrentActivity() { + Activity activity = ACTIVITY_STACK.pop(); + if (!activity.isFinishing()) { + activity.finish(); + } + } + + public static synchronized void finishActivity( Activity activity) { + ACTIVITY_STACK.remove(activity); + if (!activity.isFinishing()) { + activity.finish(); + } + } + + public static synchronized void finishActivity( Class cls) { + Iterator< Activity > iterator = ACTIVITY_STACK.iterator(); + while (iterator.hasNext()) { + Activity next = iterator.next(); + if(next.getClass().equals(cls)){ + iterator.remove(); + if(!next.isFinishing()){ + next.finish(); + } + } + } + } + + public static synchronized void finishActivityExcept( Class cls) { + Iterator< Activity > iterator = ACTIVITY_STACK.iterator(); + while (iterator.hasNext()) { + Activity next = iterator.next(); + if (!next.getClass().equals(cls)) { + iterator.remove(); + if(!next.isFinishing()){ + next.finish(); + } + } + } + } + + public static synchronized void finishAllActivity() { + Iterator< Activity > iterator = ACTIVITY_STACK.iterator(); + while (iterator.hasNext()) { + iterator.remove(); + Activity next = iterator.next(); + if(!next.isFinishing()){ + next.finish(); + } + } + } + + public static synchronized boolean isActivityExist( Class cls){ + Iterator< Activity > iterator = ACTIVITY_STACK.iterator(); + while (iterator.hasNext()) { + Activity next = iterator.next(); + if(next.getClass().equals(cls)){ + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ArrayUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ArrayUtils.java new file mode 100644 index 0000000000..1a92a858e2 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ArrayUtils.java @@ -0,0 +1,464 @@ +package com.mogo.utils; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.RandomAccess; + +@SuppressWarnings({ "unchecked" }) +public class ArrayUtils { + + /** + * 加强版的asList会检查传入是否为null,以保证不会抛出异常 并且返回的arraylist改为{@link ArrayList} + * + * @param + * @param array + * @return + */ + public static ArrayList asList( T... array) { + if (array == null) { + return new ArrayList(0); + } + ArrayList list = new ArrayList( Arrays.asList(array)); + return list; + } + + public static List asReadOnlyList( T... array) { + return new ReadOnlyArrayList(array); + } + + public static boolean contains( Object[] array, Object value) { + if (array == null) { + return false; + } + for ( Object object : array) { + if (object == null) { + if (value == null) { + return true; + } + } else if (object.equals(value)) { + return true; + } + } + return false; + } + + /** + * 在已排序数组中查询是否存在某值 + * + * @param array + * @param value + * @return + */ + public static boolean contains(int[] array, int value) { + return Arrays.binarySearch(array, value) >= 0 ? true : false; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with false (if necessary) so the copy has the + * specified length. For all indices that are valid in both the original array and the copy, the two arrays will + * contain identical values. For any indices that are valid in the copy but not the original, the copy will contain + * false. Such indices will exist if and only if the specified length is greater than that of the original + * array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with false elements to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static boolean[] copyOf(boolean[] original, int newLength) { + boolean[] copy = new boolean[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with zeros (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain (byte)0. + * Such indices will exist if and only if the specified length is greater than that of the original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with zeros to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static byte[] copyOf(byte[] original, int newLength) { + byte[] copy = new byte[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with null characters (if necessary) so the copy has the + * specified length. For all indices that are valid in both the original array and the copy, the two arrays will + * contain identical values. For any indices that are valid in the copy but not the original, the copy will contain + * '\\u000'. Such indices will exist if and only if the specified length is greater than that of the + * original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with null characters to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static char[] copyOf(char[] original, int newLength) { + char[] copy = new char[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with zeros (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain 0d. Such + * indices will exist if and only if the specified length is greater than that of the original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with zeros to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static double[] copyOf(double[] original, int newLength) { + double[] copy = new double[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with zeros (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain 0f. Such + * indices will exist if and only if the specified length is greater than that of the original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with zeros to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static float[] copyOf(float[] original, int newLength) { + float[] copy = new float[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with zeros (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain 0. Such + * indices will exist if and only if the specified length is greater than that of the original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with zeros to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static int[] copyOf(int[] original, int newLength) { + int[] copy = new int[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with zeros (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain 0L. Such + * indices will exist if and only if the specified length is greater than that of the original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with zeros to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static long[] copyOf(long[] original, int newLength) { + long[] copy = new long[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with zeros (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain (short)0. + * Such indices will exist if and only if the specified length is greater than that of the original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with zeros to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static short[] copyOf(short[] original, int newLength) { + short[] copy = new short[newLength]; + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with nulls (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain null. + * Such indices will exist if and only if the specified length is greater than that of the original array. The + * resulting array is of exactly the same class as the original array. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @return a copy of the original array, truncated or padded with nulls to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + */ + public static T[] copyOf(T[] original, int newLength) { + return (T[]) copyOf(original, newLength, original.getClass()); + } + + /** + * 从jdk1.6拷贝过来,android中没有这些方法。
+ * Copies the specified array, truncating or padding with nulls (if necessary) so the copy has the specified length. + * For all indices that are valid in both the original array and the copy, the two arrays will contain identical + * values. For any indices that are valid in the copy but not the original, the copy will contain null. + * Such indices will exist if and only if the specified length is greater than that of the original array. The + * resulting array is of the class newType. + * + * @param original the array to be copied + * @param newLength the length of the copy to be returned + * @param newType the class of the copy to be returned + * @return a copy of the original array, truncated or padded with nulls to obtain the specified length + * @throws NegativeArraySizeException if newLength is negative + * @throws NullPointerException if original is null + * @throws ArrayStoreException if an element copied from original is not of a runtime type that can be + * stored in an array of class newType + */ + public static T[] copyOf(U[] original, int newLength, Class newType) { + T[] copy = ( Object ) newType == ( Object ) Object[].class ? (T[]) new Object[newLength] : (T[]) Array.newInstance( + newType.getComponentType(), newLength); + System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); + return copy; + } + + /** + * 在数组中查询某值所在位置 + * + * @param array + * @param value + * @return + */ + public static int indexOf(int[] array, int value) { + for (int i = 0; i < array.length; i++) { + if (array[i] == value) { + return i; + } + } + throw new ArrayIndexOutOfBoundsException(value + "is not in " + Arrays.toString(array)); + } + + /** + * 在数组中查询某值所在位置 + * + * @param + * @param array + * @param value + * @return + */ + public static int indexOf(T[] array, T value) { + for (int i = 0; i < array.length; i++) { + if (array[i].equals(value)) { + return i; + } + } + throw new ArrayIndexOutOfBoundsException(value.toString() + "is not in " + Arrays.toString(array)); + } + + private static class ReadOnlyArrayList extends AbstractList implements List, Serializable, RandomAccess { + + private static final long serialVersionUID = 1L; + private final E[] a; + + ReadOnlyArrayList(E[] storage) { + a = storage; + } + + @Override + public boolean contains( Object object) { + if (a == null) { + return false; + } + if (object != null) { + for (E element : a) { + if (object.equals(element)) { + return true; + } + } + } else { + for (E element : a) { + if (element == null) { + return true; + } + } + } + return false; + } + + @Override + public E get(int location) { + try { + return a[location]; + } catch ( ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException(); + } catch ( NullPointerException e) { + throw new IndexOutOfBoundsException(); + } + } + + @Override + public int indexOf( Object object) { + if (a == null) { + return -1; + } + if (object != null) { + for (int i = 0; i < a.length; i++) { + if (object.equals(a[i])) { + return i; + } + } + } else { + for (int i = 0; i < a.length; i++) { + if (a[i] == null) { + return i; + } + } + } + return -1; + } + + @Override + public int lastIndexOf( Object object) { + if (a == null) { + return -1; + } + if (object != null) { + for (int i = a.length - 1; i >= 0; i--) { + if (object.equals(a[i])) { + return i; + } + } + } else { + for (int i = a.length - 1; i >= 0; i--) { + if (a[i] == null) { + return i; + } + } + } + return -1; + } + + @Override + public E set(int location, E object) { + if (a == null) { + throw new IndexOutOfBoundsException(); + } + try { + E result = a[location]; + a[location] = object; + return result; + } catch ( ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException(); + } catch ( ArrayStoreException e) { + throw new ClassCastException(); + } + } + + @Override + public int size() { + return a == null ? 0 : a.length; + } + + @Override + public Object[] toArray() { + if (a == null) { + return new Object[0]; + } + return a.clone(); + } + + @Override + public T[] toArray(T[] contents) { + if (a == null) { + return contents; + } + int size = size(); + if (size > contents.length) { + Class ct = contents.getClass().getComponentType(); + contents = (T[]) Array.newInstance(ct, size); + } + System.arraycopy(a, 0, contents, 0, size); + if (size < contents.length) { + contents[size] = null; + } + return contents; + } + } + + /** + * @param array + * @return + */ + public static boolean isEmpty( Collection array) { + if (array == null || array.size() == 0) { + return true; + } + return false; + } + + /** + * @param array + * @return + */ + public static boolean isEmpty(T[] array) { + if (array == null || array.length == 0) { + return true; + } + return false; + } + + /** + * 合并2个array + * + * @param head + * @param tail + * @return + */ + public static T[] join(T[] head, T[] tail) { + if (head == null) { + return tail; + } + if (tail == null) { + return head; + } + Class type = head.getClass().getComponentType(); + T[] result = (T[]) Array.newInstance(type, head.length + tail.length); + + System.arraycopy(head, 0, result, 0, head.length); + System.arraycopy(tail, 0, result, head.length, tail.length); + + return result; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/BitmapHelper.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/BitmapHelper.java new file mode 100644 index 0000000000..a2a84d73ac --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/BitmapHelper.java @@ -0,0 +1,673 @@ +package com.mogo.utils; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.media.ExifInterface; +import android.media.MediaMetadataRetriever; +import android.net.Uri; +import android.opengl.GLES10; +import android.os.Build; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; + +/** + * @author wangzhiyuan + * @since 2017/8/30 + */ + +public class BitmapHelper { + private static final String TAG = "BitmapHelper"; + + static int ONE_KB = 1024; + static int ONE_MB = ONE_KB * 1024; + + static int SIZE_DEFAULT = 2048; + static int SIZE_LIMIT = 2048; + + /** + * 根据原图添加圆角 + * + * @param source + * @return + */ + public static Bitmap createRoundCornerImage( Bitmap source, float corner ) { + final Paint paint = new Paint(); + paint.setAntiAlias( true ); + Bitmap target = Bitmap.createBitmap( source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888 ); + Canvas canvas = new Canvas( target ); + RectF rect = new RectF( 0, 0, source.getWidth(), source.getHeight() ); + canvas.drawRoundRect( rect, corner, corner, paint ); + paint.setXfermode( new PorterDuffXfermode( PorterDuff.Mode.SRC_IN ) ); + canvas.drawBitmap( source, 0, 0, paint ); + return target; + } + + public static byte[] bitmapToBytes( Bitmap bitmap ) { + if ( bitmap == null ) { + return null; + } + + ByteArrayOutputStream bos = null; + byte[] result = null; + + try { + bos = new ByteArrayOutputStream(); + bitmap.compress( Bitmap.CompressFormat.JPEG, 100, bos ); + result = bos.toByteArray(); + } catch ( Exception e ) { + e.printStackTrace(); + result = null; + } finally { + IOUtils.closeSilently( bos ); + } + + return result; + } + + /** + * Use quality compression to compress bitmap's size to be smaller than a max size, and convert it to bytes thereafter. + * Note that this method will not report compressing ratio related data. + * + * @param bitmap data source + * @param maxSize unit in kb + * @return bytes after compressing bitmap to a size smaller than a specific max size. + */ + public static byte[] bitmapToBytes( Bitmap bitmap, int maxSize ) { + final long start = System.currentTimeMillis(); + + if ( bitmap == null ) { + return null; + } + + final int maxSizeOfBytes = maxSize * ONE_KB; + ByteArrayOutputStream bos = null; + byte[] result = null; + + try { + bos = new ByteArrayOutputStream(); + int quality = 100; + int fullSize = 0; + + do { + bos.reset(); + bitmap.compress( Bitmap.CompressFormat.JPEG, quality, bos ); + if ( quality == 100 ) { + fullSize = bos.size(); + } + Log.i( TAG, "quality<---->size, " + quality + "<---->" + bos.size() / 1024 ); + } + while ( bos.size() > maxSizeOfBytes && ( quality -= ( fullSize > ONE_MB ) ? 10 : 5 ) >= 0 ); + + result = bos.toByteArray(); + + final long end = System.currentTimeMillis(); + Log.i( TAG, + "bitmap to bytes costs " + ( end - start ) + "ms, \n" + + "bitmap full size is " + ( fullSize / 1024 ) + "kb, \n" + + "bitmap final size is " + ( bos.size() / 1024 ) + "kb, \n" + + "bitmap quality is " + quality ); + } catch ( Exception e ) { + e.printStackTrace(); + result = null; + } finally { + IOUtils.closeSilently( bos ); + } + + return result; + } + + /** + * Use quality compression to compress bitmap to be smaller than a specific max size. + * + * @param bitmap data source + * @param maxSize a specific max size which's unit is kb. + * @return a compressed bitmap smaller than the max size. + */ + public static Bitmap compressBitmap( Bitmap bitmap, int maxSize, final OnCompressListener listener ) { + if ( bitmap == null || bitmap.isRecycled() ) { + return null; + } + listener.onBeforeCompress(); + ByteArrayOutputStream bos = null; + Bitmap target = null; + + try { + bos = new ByteArrayOutputStream(); + int quality = 100; + + do { + bos.reset(); + bitmap.compress( Bitmap.CompressFormat.JPEG, quality, bos ); + } + while ( bos.size() / 1024 > maxSize && ( quality -= 5 ) >= 0 ); + + byte[] result = bos.toByteArray(); + target = bytesToBitmap( result ); + if ( listener != null ) { + listener.onCompressSuccess( result ); + } + } catch ( Exception e ) { + e.printStackTrace(); + target = null; + if ( listener != null ) { + listener.onCompressFailed( "压缩失败" ); + } + } finally { + IOUtils.closeSilently( bos ); + } + + return target; + } + + + public static Bitmap compressBitmap( Bitmap bitmap, int maxSize ) { + if ( bitmap == null ) { + return null; + } + + ByteArrayOutputStream bos = null; + Bitmap target = null; + + try { + bos = new ByteArrayOutputStream(); + int quality = 100; + + do { + bos.reset(); + bitmap.compress( Bitmap.CompressFormat.JPEG, quality, bos ); + } + while ( bos.size() / 1024 > maxSize && ( quality -= 5 ) >= 0 ); + + byte[] result = bos.toByteArray(); + target = bytesToBitmap( result ); + } catch ( Exception e ) { + e.printStackTrace(); + target = null; + } finally { + IOUtils.closeSilently( bos ); + } + + return target; + } + + /** + * Decode an immutable bitmap from the specified byte array. + * + * @param b byte array of compressed image data + * @return an immutable bitmap or null in case of exception. + */ + public static Bitmap bytesToBitmap( byte[] b ) { + if ( b != null && b.length != 0 ) { + return BitmapFactory.decodeByteArray( b, 0, b.length ); + } else { + return null; + } + } + + /** + * Decode an immutable bitmap from the specified byte array. + * + * @param b byte array of compressed image data + * @param options Options that control downsampling and whether the + * image should be completely decoded, or just is size returned. + * @return an immutable bitmap or null in case of exception. + */ + public static Bitmap bytesToBitmap( byte[] b, BitmapFactory.Options options ) { + if ( b.length != 0 ) { + return BitmapFactory.decodeByteArray( b, 0, b.length, options ); + } else { + return null; + } + } + + /** + * Get max supported image size which will differ from different devices. + * + * @return max size related to the device. + */ + public static int getMaxSupportedImageSize() { + int textureLimit = getMaxTextureSize(); + if ( textureLimit == 0 ) { + return SIZE_DEFAULT; + } else { + return Math.min( textureLimit, SIZE_LIMIT ); + } + } + + public static int getMaxTextureSize2() { + // The OpenGL texture size is the maximum size that can be drawn in an ImageView + int[] maxSize = new int[1]; + GLES10.glGetIntegerv( GLES10.GL_MAX_TEXTURE_SIZE, maxSize, 0 ); + return maxSize[0]; + } + + /** + * Decode a bitmap's input stream to find a proper inSampleSize according to device's max supported size. + * + * @param is bitmap's data source + * @param close whether to close input stream after work is done. + * @return a proper inSampleSize + */ + public static int findProperInSampleSize( InputStream is, boolean close ) { + // Just decode image size into options + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + + try { + BitmapFactory.decodeStream( is, null, options ); + } catch ( Exception e ) { + e.printStackTrace(); + } finally { + if ( close ) IOUtils.closeSilently( is ); + } + + int maxSize = getMaxSupportedImageSize(); + int sampleSize = 1; + + while ( options.outHeight / sampleSize > maxSize || options.outWidth / sampleSize > maxSize ) { + sampleSize = sampleSize << 1; + } + + Log.i( TAG, "sample size is " + sampleSize ); + return sampleSize; + } + + /** + * Read a picture's degree from a file. + * + * @param file data source of a picture + * @return degrees range from 0 to 360 + */ + public static int readPictureDegree( File file ) { + return readPictureDegree( file.getAbsolutePath() ); + } + + /** + * Read a picture's degree from a file, we use {@link ExifInterface} instead of {@link android.media.ExifInterface} + * to avoid some unexpected bugs. + * + * @param filePath file's absolute path which we can read data source of a picture from. + * @return degrees range from 0 to 360 + */ + public static int readPictureDegree( String filePath ) { + int degree = 0; + + try { + ExifInterface exifInterface = new ExifInterface( filePath ); + int orientation = exifInterface.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL ); + switch ( orientation ) { + case ExifInterface.ORIENTATION_ROTATE_90: + degree = 90; + break; + case ExifInterface.ORIENTATION_ROTATE_180: + degree = 180; + break; + case ExifInterface.ORIENTATION_ROTATE_270: + degree = 270; + break; + } + } catch ( Exception e ) { + e.printStackTrace(); + } + Log.i( TAG, "ExifInterface, degree is " + degree ); + + return degree; + } + + /** + * Rotate an bitmap to a specific angle. + * + * @param angle target angle + * @param bitmap data source + * @return Returns an immutable bitmap from subset of the source bitmap, + * transformed by the optional matrix. The new bitmap may be the + * same object as source, or a copy may have been made. It is + * initialized with the same density as the original bitmap. + *

+ * If the source bitmap is immutable and the requested subset is the + * same as the source bitmap itself, then the source bitmap is + * returned and no new bitmap is created. + */ + public static Bitmap rotateBitmap( int angle, Bitmap bitmap ) { + if ( bitmap == null ) { + return null; + } + + try { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + Matrix matrix = new Matrix(); + matrix.preRotate( angle ); + return Bitmap.createBitmap( bitmap, 0, 0, width, height, matrix, true ); + } catch ( Exception e ) { + e.printStackTrace(); + return bitmap; + } + } + + /** + * Get picture's absolute path according to its uri. + * + * @param context context + * @param uri picture's uri + * @return absolute path of uri. + */ + public static String getRealPathFromUri( Context context, Uri uri ) { + int sdkVersion = Build.VERSION.SDK_INT; + if ( sdkVersion >= 19 ) { + return getRealPathFromUriAboveApi19( context, uri ); + } else { + return getRealPathFromUriBelowAPI19( context, uri ); + } + } + + /** + * Create a default {@link BitmapFactory.Options} . + * Note this options use rgb_565 and a proper inSampleSize in order to save memory. + * + * @param is data source of picture + * @param close whether to close data source + * @return options containing rgb_565 config and a proper inSampleSize. + */ + public static BitmapFactory.Options newDefaultOptions( InputStream is, boolean close ) { + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inPreferredConfig = Bitmap.Config.RGB_565; + options.inSampleSize = BitmapHelper.findProperInSampleSize( is, close ); + + return options; + } + + /** + * Save picture to local file. + * + * @param bitmap data source + * @param file local file to store picture. + */ + public static void savePicture( Bitmap bitmap, File file ) { + final long start = System.currentTimeMillis(); + + if ( bitmap == null || file == null ) { + Log.i( TAG, "保存失败, bitmap or file is null." ); + return; + } + if ( file.getParentFile() != null && !file.getParentFile().exists() ) { + file.getParentFile().mkdirs(); + } + + try { + final FileOutputStream fos = new FileOutputStream( file ); + bitmap.compress( Bitmap.CompressFormat.JPEG, 100, fos ); + fos.flush(); + fos.close(); + + if ( file.exists() ) { + Log.i( TAG, "保存成功" ); + } + } catch ( Exception e ) { + e.printStackTrace(); + } + + Log.i( TAG, "saving picture costs " + ( System.currentTimeMillis() - start ) + "ms" ); + } + + /** + * 适配api19以下(不包括api19),根据uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + private static String getRealPathFromUriBelowAPI19( Context context, Uri uri ) { + return getDataColumn( context, uri, null, null ); + } + + /** + * 适配api19及以上,根据uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + @SuppressLint( "NewApi" ) + private static String getRealPathFromUriAboveApi19( Context context, Uri uri ) { + String filePath = null; + + try { + // 如果是document类型的 uri, 则通过document id来进行处理 + if ( DocumentsContract.isDocumentUri( context, uri ) ) { + String documentId = DocumentsContract.getDocumentId( uri ); + if ( isMediaDocument( uri ) ) { + // 使用':'分割 + String id = documentId.split( ":" )[1]; + String selection = MediaStore.Images.Media._ID + "=?"; + String[] selectionArgs = {id}; + filePath = getDataColumn( context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs ); + } else if ( isDownloadsDocument( uri ) ) { + Uri contentUri = ContentUris.withAppendedId( Uri.parse( "content://downloads/public_downloads" ), Long.valueOf( documentId ) ); + filePath = getDataColumn( context, contentUri, null, null ); + } + } else if ( "content".equalsIgnoreCase( uri.getScheme() ) ) { + filePath = getDataColumn( context, uri, null, null ); + } else if ( "file".equals( uri.getScheme() ) ) { + filePath = uri.getPath(); + } + } catch ( Exception e ) { + e.printStackTrace(); + } + + return filePath; + } + + /** + * 获取数据库表中的 _data 列,即返回Uri对应的文件路径 + */ + private static String getDataColumn( Context context, Uri uri, String selection, String[] selectionArgs ) { + String path = null; + String[] projection = new String[]{MediaStore.Images.Media.DATA}; + Cursor cursor = null; + + try { + cursor = context.getContentResolver().query( uri, projection, selection, selectionArgs, null ); + + if ( cursor != null && cursor.moveToFirst() ) { + int columnIndex = cursor.getColumnIndexOrThrow( projection[0] ); + path = cursor.getString( columnIndex ); + } + } catch ( Exception e ) { + if ( cursor != null ) { + cursor.close(); + cursor = null; + } + } finally { + if ( cursor != null ) { + cursor.close(); + cursor = null; + } + } + + return path; + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is MediaProvider + */ + private static boolean isMediaDocument( Uri uri ) { + return "com.android.providers.media.documents".equals( uri.getAuthority() ); + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is DownloadsProvider + */ + private static boolean isDownloadsDocument( Uri uri ) { + return "com.android.providers.downloads.documents".equals( uri.getAuthority() ); + } + + public static int getMaxTextureSize() { + try { + // Safe minimum default size + final int IMAGE_MAX_BITMAP_DIMENSION = SIZE_DEFAULT; + + // Get EGL Display + EGL10 egl = ( EGL10 ) EGLContext.getEGL(); + EGLDisplay display = egl.eglGetDisplay( EGL10.EGL_DEFAULT_DISPLAY ); + + // Initialise + int[] version = new int[2]; + egl.eglInitialize( display, version ); + + // Query total number of configurations + int[] totalConfigurations = new int[1]; + egl.eglGetConfigs( display, null, 0, totalConfigurations ); + + // Query actual list configurations + EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]]; + egl.eglGetConfigs( display, configurationsList, totalConfigurations[0], totalConfigurations ); + + int[] textureSize = new int[1]; + int maximumTextureSize = 0; + + // Iterate through all the configurations to located the maximum texture size + for ( int i = 0; i < totalConfigurations[0]; i++ ) { + // Only need to check for width since opengl textures are always squared + egl.eglGetConfigAttrib( display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize ); + + // Keep trackCustomEvent of the maximum texture size + if ( maximumTextureSize < textureSize[0] ) + maximumTextureSize = textureSize[0]; + } + + // Release + egl.eglTerminate( display ); + + // Return largest texture size found, or default + return Math.max( maximumTextureSize, IMAGE_MAX_BITMAP_DIMENSION ); + } catch ( Exception e ) { + e.printStackTrace(); + } + + return 0; + } + + public static String bitmapToBase64( Bitmap bitmap ) { + String result = null; + try { + if ( bitmap != null ) { + result = Base64.encodeToString( bitmapToBytes( bitmap ), Base64.DEFAULT ); + } + } catch ( Exception e ) { + e.printStackTrace(); + } + return result; + } + + public static Bitmap base64ToBitmap( String base64Data ) { + byte[] bytes = Base64.decode( base64Data, Base64.DEFAULT ); + return BitmapFactory.decodeByteArray( bytes, 0, bytes.length ); + } + + /** + * 在系统返回的intent中获取图片信息,并转化为uri + * + * @param data + * @return + */ + public static Uri convertUri( Context context, Intent data ) { + if ( data == null || data.getData() == null ) { + return null; + } + Uri localUri = data.getData(); + String scheme = localUri.getScheme(); + String imagePath = ""; + if ( "content".equals( scheme ) ) { + String[] filePathColumns = {MediaStore.Images.Media.DATA}; + Cursor c = context.getContentResolver().query( localUri, filePathColumns, null, null, null ); + if ( c != null ) { + c.moveToFirst(); + int columnIndex = c.getColumnIndex( filePathColumns[0] ); + imagePath = c.getString( columnIndex ); + c.close(); + } + } else if ( "file".equals( scheme ) ) {//小米4选择云相册中的图片是根据此方法获得路径 + imagePath = localUri.getPath(); + } + if ( TextUtils.isEmpty( imagePath ) ) { + return localUri; + } + Uri uri = Uri.fromFile( new File( imagePath ) ); + return uri != null ? uri : localUri; + } + + public static Bitmap colorToBitmap( Context context, int colorResId ) {// drawable 转换成bitmap + Bitmap.Config config = Bitmap.Config.ARGB_8888;// 取drawable的颜色格式 + Bitmap bitmap = Bitmap.createBitmap( 1, 1, config );// 建立对应bitmap + bitmap.eraseColor( context.getResources().getColor( colorResId ) ); + return bitmap; + } + + public static String getAlphaHexValue( float alpha ) { + String color = Integer.toHexString( ( int ) alpha * 255 ); + return TextUtils.isEmpty( color ) ? color : color.toUpperCase(); + } + + /** + * 抓取本地视频缩略图(操作可能耗时,尽量异步进行) + * + * @param filePath + * @return + */ + public static Bitmap getVideoThumbnail( String filePath ) { + Bitmap b = null; + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + try { + retriever.setDataSource( filePath ); + b = retriever.getFrameAtTime(); + } catch ( IllegalArgumentException e ) { + e.printStackTrace(); + } catch ( RuntimeException e ) { + e.printStackTrace(); + + } finally { + try { + retriever.release(); + } catch ( RuntimeException e ) { + e.printStackTrace(); + } + } + return b; + } + + public interface OnCompressListener { + + void onCompressSuccess( byte[] data ); + + void onCompressFailed( String msg ); + + void onBeforeCompress(); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/CheckUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/CheckUtils.java new file mode 100644 index 0000000000..39f653cf3b --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/CheckUtils.java @@ -0,0 +1,199 @@ +package com.mogo.utils; + +import android.content.Context; +import android.graphics.Rect; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.provider.Settings; +import android.text.TextUtils; +import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CheckUtils { + + /** + * 检查任意Object是否为空 + *


+ * shallow check : 不会检查容器内部的元素是否为空 + */ + public static boolean isEmpty( Object obj) { + + if (obj == null) { + return true; + } + + if (obj instanceof Collection ) { + // 检查各种Collection是否为空(List,Queue,Set) + return (( Collection ) obj).isEmpty(); + } else if (obj instanceof Map ) { + // 检查各种Map + return (( Map ) obj).isEmpty(); + } else if (obj instanceof CharSequence ) { + // 检查各种CharSequence + return (( CharSequence ) obj).length() == 0; + } else if (obj.getClass().isArray()) { + // 检查各种base array + // return Array.getLength(obj) == 0; + if (obj instanceof Object[]) { + return (( Object[]) obj).length == 0; + } else if (obj instanceof int[]) { + return ((int[]) obj).length == 0; + } else if (obj instanceof long[]) { + return ((long[]) obj).length == 0; + } else if (obj instanceof short[]) { + return ((short[]) obj).length == 0; + } else if (obj instanceof double[]) { + return ((double[]) obj).length == 0; + } else if (obj instanceof float[]) { + return ((float[]) obj).length == 0; + } else if (obj instanceof boolean[]) { + return ((boolean[]) obj).length == 0; + } else if (obj instanceof char[]) { + return ((char[]) obj).length == 0; + } else if (obj instanceof byte[]) { + return ((byte[]) obj).length == 0; + } + } + + return false; + } + + public static boolean isExist( Object obj) { + return !isEmpty(obj); + } + + public static boolean isContainsEmpty( Object... objs) { + if (isEmpty(objs)) { + return true; + } + for ( Object obj : objs) { + if (isEmpty(obj)) { + return true; + } + } + return false; + } + + /** + * 是否为奇数 + */ + public static boolean isOdd(int i) { + return i % 2 != 0; + } + + /** + * 是否为偶数 + */ + public static boolean isEven(int i) { + return i % 2 == 0; + } + + /** + * 检查枚举组中是否包含指定枚举 + */ + public static boolean isContainsEnum( @Nullable Enum[] group, Enum child) { + + if (isEmpty(group)) { + return false; + } + + for ( Enum enums : group) { + if (enums == child) { + return true; + } + } + return false; + } + + /** + * 快速点击事件 + */ + private static long lastClickTime; + public static boolean isFastDoubleClick() { + long time = System.currentTimeMillis(); + long timeD = time - lastClickTime; + long delayTime = 500L; + if (0L < timeD && timeD < delayTime) { + return true; + } else { + lastClickTime = time; + return false; + } + } + + /** + * 网络是否可用 + */ + public static boolean isNetworkConnected( Context context) { + if ( context == null ) { + return false; + } + ConnectivityManager cm = ( ConnectivityManager ) context.getSystemService( Context.CONNECTIVITY_SERVICE); + NetworkInfo network = null; + if (cm != null) { + network = cm.getActiveNetworkInfo(); + } + return network != null && network.isAvailable() && network.isConnected(); + + } + + /** + * 检查gps开关是否已打开 + */ + public static boolean isGpsOpenStatus( Context context) { + if ( context == null ) { + return false; + } + String gps = Settings.System.getString(context.getContentResolver(), Settings.System.LOCATION_PROVIDERS_ALLOWED); + return !( TextUtils.isEmpty(gps) || !gps.contains("gps")); + } + + /** + * 检查是否模拟位置 + */ + public static boolean isOPenMockLocation( Context context) { + if ( context == null ) { + return false; + } + return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0; + } + + /**当前点击点是否在视图中*/ + public static boolean isInsideView( MotionEvent event, View view) { + if (view != null && event != null) { + float eventX = event.getRawX(); + float eventY = event.getRawY(); + + int[] contentArray = new int[2]; + + Rect contentRect = new Rect(); + view.getLocationOnScreen(contentArray); + view.getDrawingRect(contentRect); + contentRect.offsetTo(contentArray[0], contentArray[1]); + + return contentRect.contains((int) eventX, (int) eventY); + } + + return false; + } + + /** + * 判断扫描内容是否为url格式 + */ + public static boolean isUrl( String url) { + if ( TextUtils.isEmpty(url)) { + return false; + } + String regex = "http(s)?://.*"; + Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); + Matcher m = pattern.matcher(url); + return m.matches(); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/CommonUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/CommonUtils.java new file mode 100644 index 0000000000..e243da13cc --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/CommonUtils.java @@ -0,0 +1,573 @@ +package com.mogo.utils; + +import android.Manifest; +import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Enumeration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class CommonUtils { + + private static String mMacSerial = null; + private static String mCPUSerial = null; + private static boolean isMacSerialNoObtained = false; + private static boolean isCPUSerialNoObtained = false; + private static final Pattern VERSION_NAME_PATTERN = Pattern.compile( "(\\d+\\.\\d+\\.\\d+)\\-*.*" ); + + + public static String getAndroidID( Context context ) { + if ( context == null ) { + return ""; + } + return Settings.Secure.getString( context.getContentResolver(), "android_id" ); + } + + public static String getCPUSerialno() { + if ( !TextUtils.isEmpty( mCPUSerial ) ) { + return mCPUSerial; + } else if ( isCPUSerialNoObtained ) { + mCPUSerial = ""; + return mCPUSerial; + } else { + String str = ""; + + InputStreamReader ir = null; + LineNumberReader input = null; + try { + isCPUSerialNoObtained = true; + Process ex = Runtime.getRuntime().exec( "cat /proc/cpuinfo" ); + if ( ex == null ) { + return null; + } + + ir = new InputStreamReader( ex.getInputStream() ); + input = new LineNumberReader( ir ); + + while ( null != str ) { + str = input.readLine(); + if ( str != null ) { + mCPUSerial = str.trim(); + break; + } + } + } catch ( IOException var4 ) { + var4.printStackTrace(); + } finally { + if ( ir != null ) { + try { + ir.close(); + } catch ( IOException e ) { + e.printStackTrace(); + } + } + if ( input != null ) { + try { + input.close(); + } catch ( IOException e ) { + e.printStackTrace(); + } + } + } + + return mCPUSerial; + } + } + + public static int getVersionCode( Context context ) { + + if ( context == null ) { + return 1; + } + + String pkgName = context.getPackageName(); + + try { + PackageInfo e = context.getPackageManager().getPackageInfo( pkgName, 0 ); + if ( e != null ) { + return e.versionCode; + } + } catch ( Exception var2 ) { + var2.printStackTrace(); + } + + return 1; + } + + + public static String getMacSerialno() { + if ( !TextUtils.isEmpty( mMacSerial ) ) { + return mMacSerial; + } else if ( isMacSerialNoObtained ) { + mMacSerial = ""; + return mMacSerial; + } else { + String str = ""; + + InputStreamReader ir = null; + LineNumberReader input = null; + try { + isMacSerialNoObtained = true; + Process ex = Runtime.getRuntime().exec( "cat /sys/class/net/wlan0/address" ); + if ( ex == null ) { + return null; + } + + ir = new InputStreamReader( ex.getInputStream() ); + input = new LineNumberReader( ir ); + + while ( null != str ) { + str = input.readLine(); + if ( str != null ) { + mMacSerial = str.trim(); + break; + } + } + } catch ( IOException var4 ) { + var4.printStackTrace(); + } finally { + if ( ir != null ) { + try { + ir.close(); + } catch ( IOException e ) { + e.printStackTrace(); + } + } + if ( input != null ) { + try { + input.close(); + } catch ( IOException e ) { + e.printStackTrace(); + } + } + } + + return mMacSerial; + } + } + + /** + * 获取网络类型 + * + * @return + */ + public static String getNetworkType( Context context ) { + + String name = "UNKNOWN"; + + try { + + ConnectivityManager connMgr = ( ConnectivityManager ) context.getSystemService( Context.CONNECTIVITY_SERVICE ); + NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + if ( networkInfo != null ) { + if ( ConnectivityManager.TYPE_WIFI == networkInfo.getType() ) { + return "WIFI"; + } + } + + TelephonyManager tm = ( TelephonyManager ) context.getSystemService( Context.TELEPHONY_SERVICE ); + if ( tm == null ) { + return name; + } + + int type = tm.getNetworkType(); + switch ( type ) { + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_1xRTT: + case TelephonyManager.NETWORK_TYPE_IDEN: + name = "2G"; + break; + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_HSPAP: + name = "3G"; + break; + case TelephonyManager.NETWORK_TYPE_LTE: + name = "4G"; + break; + case TelephonyManager.NETWORK_TYPE_UNKNOWN: + name = "UNKNOWN"; + break; + default: + name = "UNKNOWN"; + break; + } + } catch ( Exception e ) { + + } + return name; + } + + /** + * 得到手机的IMEI号 + * + * @return + */ + public static String getIMEI( Context context ) { + try { + TelephonyManager telephonyManager = ( TelephonyManager ) context.getSystemService( Context.TELEPHONY_SERVICE ); + if ( telephonyManager != null && + ContextCompat.checkSelfPermission( context, Manifest.permission.READ_PHONE_STATE ) == PackageManager.PERMISSION_GRANTED ) { + return telephonyManager.getDeviceId(); + } + } catch ( Exception e ) { + + } + return ""; + } + + /** + * 得到手机的IMSI号 + * + * @return + */ + public static String getIMSI( Context context ) { + + try { + TelephonyManager telephonyManager = ( TelephonyManager ) context.getSystemService( Context.TELEPHONY_SERVICE ); + if ( telephonyManager != null && + ActivityCompat.checkSelfPermission( context, Manifest.permission.READ_PHONE_STATE ) == PackageManager.PERMISSION_GRANTED ) { + return telephonyManager.getSubscriberId(); + } + } catch ( Exception e ) { + + } + return ""; + } + + public static String checkSimState( Context context ) { + + String mString = ""; + + if ( context == null ) { + return mString; + } + + TelephonyManager telephonyManager = ( TelephonyManager ) context.getSystemService( Context.TELEPHONY_SERVICE ); + int simState = 0; + if ( telephonyManager != null ) { + simState = telephonyManager.getSimState(); + } + + switch ( simState ) { + + case TelephonyManager.SIM_STATE_ABSENT: + mString = "无卡"; + // do something + break; + + case TelephonyManager.SIM_STATE_NETWORK_LOCKED: + mString = "需要NetworkPIN解锁"; + // do something + + break; + + case TelephonyManager.SIM_STATE_PIN_REQUIRED: + mString = "需要PIN解锁"; + // do something + break; + + case TelephonyManager.SIM_STATE_PUK_REQUIRED: + mString = "需要PUN解锁"; + // do something + break; + + case TelephonyManager.SIM_STATE_READY: + mString = "良好"; + // do something + break; + + case TelephonyManager.SIM_STATE_UNKNOWN: + mString = "未知状态"; + // do something + break; + } + return mString; + } + + + /** + * 获取路由器Mac + */ + public static String getRouterMac( Context context ) { + + if ( context == null ) { + return ""; + } + + WifiManager wifi = ( WifiManager ) context.getApplicationContext().getSystemService( Context.WIFI_SERVICE ); + if ( wifi != null && wifi.getConnectionInfo() != null ) { + return wifi.getConnectionInfo().getBSSID(); + } + return ""; + } + + /** + * 获取wifi名字 + */ + public static String getWifiName( Context context ) { + if ( context == null ) { + return ""; + } + WifiManager wifi = ( WifiManager ) context.getApplicationContext().getSystemService( Context.WIFI_SERVICE ); + if ( wifi != null && wifi.getConnectionInfo() != null ) { + return wifi.getConnectionInfo().getSSID(); + } + return ""; + } + + + public static String getMobileIP( Context ctx ) { + + if ( ctx == null ) { + return ""; + } + + ConnectivityManager mConnectivityManager = ( ConnectivityManager ) ctx.getSystemService( Context.CONNECTIVITY_SERVICE );// 获取系统的连接服务 + // 实例化mActiveNetInfo对象 + NetworkInfo mActiveNetInfo = null;// 获取网络连接的信息 + if ( mConnectivityManager != null ) { + mActiveNetInfo = mConnectivityManager.getActiveNetworkInfo(); + } + if ( mActiveNetInfo == null ) { + return ""; + } else { + return getIp( mActiveNetInfo ); + } + } + + // 显示IP信息 + private static String getIp( NetworkInfo mActiveNetInfo ) { + + if ( mActiveNetInfo == null ) { + return ""; + } + + // 如果是WIFI网络 + if ( mActiveNetInfo.getType() == ConnectivityManager.TYPE_WIFI ) { + return getLocalIPAddress(); + } + // 如果是手机网络 + else if ( mActiveNetInfo.getType() == ConnectivityManager.TYPE_MOBILE ) { + return getLocalIPAddress(); + } else { + return ""; + } + + } + + // 获取本地IP函数 + private static String getLocalIPAddress() { + try { + Enumeration< NetworkInterface > mEnumeration = NetworkInterface.getNetworkInterfaces(); + if ( mEnumeration != null ) { + while ( mEnumeration.hasMoreElements() ) { + NetworkInterface intf = mEnumeration.nextElement(); + if ( intf != null && intf.getInetAddresses() != null ) { + Enumeration< InetAddress > enumIPAddr = intf.getInetAddresses(); + while ( enumIPAddr.hasMoreElements() ) { + InetAddress inetAddress = enumIPAddr.nextElement(); + // 如果不是回环地址 + if ( inetAddress != null && !inetAddress.isLoopbackAddress() ) { + // 直接返回本地IP地址 + return inetAddress.getHostAddress(); + } + } + } + } + } + } catch ( SocketException ex ) { + ex.printStackTrace(); + } + return ""; + } + + public static String getVersionName( Context context ) { + return getVersionName( context, true ); + } + + public static String getVersionName( Context context, boolean fullVersionName ) { + String appVersion = ""; + + try { + String packageName = context.getApplicationInfo().packageName; + appVersion = context.getPackageManager().getPackageInfo( packageName, 0 ).versionName; + if ( !fullVersionName && appVersion != null && appVersion.length() > 0 ) { + Matcher matcher = VERSION_NAME_PATTERN.matcher( appVersion ); + if ( matcher.matches() ) { + appVersion = matcher.group( 1 ); + } + } + } catch ( Exception e ) { + e.printStackTrace(); + } + + return appVersion; + } + + public static String getAppName( Context context ) { + if ( context == null ) { + return ""; + } + PackageManager pm = context.getPackageManager(); + return context.getApplicationInfo().loadLabel( pm ).toString(); + + } + + public static String getModel() { + String temp = Build.MODEL; + return TextUtils.isEmpty( temp ) ? "" : temp; + } + + @TargetApi( Build.VERSION_CODES.JELLY_BEAN ) + public static long getLeftMemory( Context context ) { + + if ( context == null ) { + return -1; + } + + if ( Build.VERSION.SDK_INT >= 16 ) { + ActivityManager mActivityManager = ( ActivityManager ) context.getSystemService( Context.ACTIVITY_SERVICE ); + ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); + mActivityManager.getMemoryInfo( mi ); + return ( mi.totalMem - mi.availMem ) / 1000; + } + return -1; + } + + + public static String encode( String string ) { + try { + return URLEncoder.encode( string, "UTF-8" ); + } catch ( UnsupportedEncodingException e ) { + e.printStackTrace(); + return ""; + } + } + + public static String decode( String string ) { + try { + return URLDecoder.decode( string, "UTF-8" ); + } catch ( UnsupportedEncodingException e ) { + e.printStackTrace(); + return ""; + } + } + + public static String getProcessName( Context context, int pid ) { + try { + ActivityManager manager = ( ActivityManager ) context.getSystemService( Context.ACTIVITY_SERVICE ); + for ( ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses() ) { + if ( processInfo.pid == pid ) { + return processInfo.processName; + } + } + } catch ( Exception e ) { + + } + return ""; + } + + public static int getStatusBarHeight( Context context ) { + + if ( context == null ) { + return 0; + } + + int statusBarHeight = 0; + try { + Class c = Class.forName( "com.android.internal.R$dimen" ); + Object obj = c.newInstance(); + Field field = c.getField( "status_bar_height" ); + int x = Integer.parseInt( field.get( obj ).toString() ); + statusBarHeight = context.getResources().getDimensionPixelSize( x ); + } catch ( Exception e ) { + } + + if ( statusBarHeight > 0 ) { + return statusBarHeight; + } + + try { + int resourceId = context.getResources().getIdentifier( "status_bar_height", "dimen", "android" ); + if ( resourceId > 0 ) { + statusBarHeight = context.getResources().getDimensionPixelSize( resourceId ); + } + } catch ( Exception e ) { + + } + + return statusBarHeight; + } + + /** + * 获取某个应用的版本名称 + * + * @param context 应用上下文 + * @param packageName 包名,如果为空,将获取 context 本身的版本名称 + * @return + */ + public static String getVersionName( @NonNull Context context, @Nullable String packageName ) { + try { + packageName = TextUtils.isEmpty( packageName ) ? context.getPackageName() : packageName; + PackageManager packageManager = context.getPackageManager(); + PackageInfo packInfo = packageManager.getPackageInfo( packageName, 0 ); + return packInfo.versionName; + } catch ( Exception e ) { + return ""; + } + } + + /** + * 获取某个应用的版本号 + * + * @param context 应用上下文 + * @param packageName 包名,如果为空,将获取 context 本身的版本号 + * @return + */ + public static int getVersionCode( @NonNull Context context, @Nullable String packageName ) { + try { + packageName = TextUtils.isEmpty( packageName ) ? context.getPackageName() : packageName; + PackageManager packageManager = context.getPackageManager(); + PackageInfo packInfo = packageManager.getPackageInfo( packageName, 0 ); + return packInfo.versionCode; + } catch ( Exception e ) { + return 0; + } + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/CountDownTimer.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/CountDownTimer.java new file mode 100644 index 0000000000..fc759cdee9 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/CountDownTimer.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils; + +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; + +/** + * Schedule a countdown until a time in the future, with + * regular notifications on intervals along the way. + * + * Example of showing a 30 second countdown in a text field: + * + *
+ * new CountDownTimer(30000, 1000) {
+ *
+ *     public void onTick(long millisUntilFinished) {
+ *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
+ *     }
+ *
+ *     public void onFinish() {
+ *         mTextField.setText("done!");
+ *     }
+ *  }.start();
+ * 
+ * + * The calls to {@link #onTick(long)} are synchronized to this object so that + * one call to {@link #onTick(long)} won't ever occur before the previous + * callback is complete. This is only relevant when the implementation of + * {@link #onTick(long)} takes an amount of time to execute that is significant + * compared to the countdown interval. + */ +public abstract class CountDownTimer { + + /** + * Millis since epoch when alarm should stop. + */ + private final long mMillisInFuture; + + /** + * The interval in millis that the user receives callbacks + */ + private final long mCountdownInterval; + + private long mStopTimeInFuture; + + /** + * boolean representing if the timer was cancelled + */ + private boolean mCancelled = false; + + /** + * @param millisInFuture The number of millis in the future from the call + * to {@link #start()} until the countdown is done and {@link #onFinish()} + * is called. + * @param countDownInterval The interval along the way to receive + * {@link #onTick(long)} callbacks. + */ + public CountDownTimer(long millisInFuture, long countDownInterval) { + mMillisInFuture = millisInFuture; + mCountdownInterval = countDownInterval; + } + + /** + * Cancel the countdown. + */ + public synchronized final void cancel() { + mCancelled = true; + mHandler.removeMessages(MSG); + } + + /** + * Start the countdown. + */ + public synchronized final CountDownTimer start() { + mCancelled = false; + if (mMillisInFuture <= 0) { + onFinish(); + return this; + } + mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture; + mHandler.sendMessage(mHandler.obtainMessage(MSG)); + return this; + } + + + /** + * Callback fired on regular interval. + * @param millisUntilFinished The amount of time until finished. + */ + public abstract void onTick(long millisUntilFinished); + + /** + * Callback fired when the time is up. + */ + public abstract void onFinish(); + + + private static final int MSG = 1; + + + // handles counting down + private Handler mHandler = new Handler() { + + @Override + public void handleMessage( Message msg) { + + synchronized (CountDownTimer.this) { + if (mCancelled) { + return; + } + + final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime(); + + if (millisLeft <= 0) { + onFinish(); + } else if (millisLeft < mCountdownInterval) { + // no tick, just delay until done + sendMessageDelayed(obtainMessage(MSG), millisLeft); + } else { + long lastTickStart = SystemClock.elapsedRealtime(); + onTick(millisLeft); + + // take into account user's onTick taking time to execute + long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime(); + + // special case: user's onTick took more than interval to + // complete, skip to next interval + while (delay < 0) delay += mCountdownInterval; + + sendMessageDelayed(obtainMessage(MSG), delay); + } + } + } + }; +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/DateTimeUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/DateTimeUtils.java new file mode 100644 index 0000000000..ca9573894a --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/DateTimeUtils.java @@ -0,0 +1,452 @@ +package com.mogo.utils; + +import android.text.TextUtils; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.regex.Pattern; + +public class DateTimeUtils { + + public static final int DATETIME_FIELD_REFERSH = 10; // 刷新时间(分钟), + // + public static final long ONE_SECOND = 1000L; + public static final long ONE_MINUTE = ONE_SECOND * 60L; + public static final long ONE_HOUR = ONE_MINUTE * 60L; + public static final long ONE_DAY = ONE_HOUR * 24L; + public static final long ONE_MONTH = ONE_DAY * 24L; + public static final long ONE_YEAR = ONE_MONTH * 24L; + // 下面的pattern在print和parse时都可以使用 + public static final String MM_Yue_dd_Ri = "MM月dd日"; + public static final String MM_Yue_dd_Ri_HH_mm = "MM月dd日 HH:mm"; + public static final String M_Yue_d_Ri = "M月d日"; + public static final String d_Ri = "d日"; + public static final String yyyyMMdd = "yyyyMMdd"; + public static final String yyyy_MM_dd = "yyyy-MM-dd"; + public static final String yyyy_MM = "yyyy-MM"; + public static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss"; + public static final String yyyy_MM_dd_HH_mm = "yyyy-MM-dd HH:mm"; + public static final String yyyyMMddHHmmss = "yyyyMMddHHmmss"; + public static final String HH_mm = "HH:mm"; + public static final String yyyy_Nian_MM_Yue_dd_Ri = "yyyy年MM月dd日"; + public static final String yyyy_Nian_MM_Yue = "yyyy年MM月"; + public static final String MM_yy = "MM/yy"; + public static final String dd_MM = "dd/MM"; + public static final String MM_dd = "MM-dd"; + private static final String pattern2 = "MM 月 dd"; + // 下面的pattern是print时用,parse时不应使用(只有时间,没有日期) + public static final String HH_mm_ss = "HH:mm:ss"; + private static final String[] PATTERNS = {yyyy_MM_dd_HH_mm_ss, yyyy_MM_dd_HH_mm, yyyy_MM_dd, yyyyMMdd}; + + + public static Calendar cleanCalendarTime( Calendar c) { + c.set( Calendar.HOUR_OF_DAY, 0); + c.set( Calendar.MINUTE, 0); + c.set( Calendar.SECOND, 0); + c.set( Calendar.MILLISECOND, 0); + return c; + } + + /** + * 获得指定日期表示格式转换成Calendar的格式 + * + * @param src + * @param fallback 若无法转换,返回一个默认值 + * @return + */ + public static Calendar getCalendar( T src, Calendar fallback) { + if (src != null) { + try { + return getCalendar(src); + } catch ( Exception e) { + } + } + return ( Calendar ) fallback.clone(); + } + + /** + * 获得日期类型 + * + * @param src 任何可以表示时间的类型,目前支持Calendar,Date,long,String + * @return Calendar类型表示的时间 + * @throws IllegalArgumentException + */ + public static Calendar getCalendar( T src) { + Calendar calendar = Calendar.getInstance(); + calendar.setLenient(false); + if (src == null) { + return null; + } else if (src instanceof Calendar ) { + calendar.setTimeInMillis((( Calendar ) src).getTimeInMillis()); + } else if (src instanceof Date ) { + calendar.setTime(( Date ) src); + } else if (src instanceof Long ) { + calendar.setTimeInMillis(( Long ) src); + } else if (src instanceof String ) { + String nSrc = ( String ) src; + if ( TextUtils.isEmpty(nSrc)) { + return null; + } + try { + // 直接匹配的时候不能匹配到月份或日期不是2位数的情况 + if ( Pattern.compile("\\d{4}年\\d{1,2}月\\d{1,2}日").matcher(nSrc).find()) { + nSrc = fixDateString(nSrc); + return getCalendarByPattern(nSrc, yyyy_MM_dd); + } + return getCalendarByPatterns(nSrc, PATTERNS); + } catch ( Exception e) { + try { + calendar.setTimeInMillis( Long.valueOf(nSrc)); + } catch ( NumberFormatException e1) { + throw new IllegalArgumentException(e1); + } + } + } else { + throw new IllegalArgumentException(); + } + return calendar; + } + + /** + * YYYY年MM月DD日 --> YYYY-MM-DD + */ + private static String fixDateString( String date) { + if ( TextUtils.isEmpty(date)) { + return date; + } + + String[] dateArray = date.split("[年月日]"); + if (dateArray.length == 1) { + dateArray = date.split("-"); + } + for (int i = 0; i < 3; i++) { + if (dateArray[i].length() == 1) { + dateArray[i] = "0" + dateArray[i]; + } + } + return dateArray[0] + "-" + dateArray[1] + "-" + dateArray[2]; + } + + /** + * 匹配pattern获得时间,若无法解析抛出异常 + * + * @param dateTimeStr + * @param patternStr + * @return + * @throws IllegalArgumentException + */ + public static Calendar getCalendarByPattern( String dateTimeStr, String patternStr) { + try { + SimpleDateFormat sdf = new SimpleDateFormat(patternStr, Locale.US); + sdf.setLenient(false); + Date d = sdf.parse(dateTimeStr); + Calendar c = Calendar.getInstance(); + c.setLenient(false); + c.setTimeInMillis(d.getTime()); + return c; + } catch ( Exception e) { + throw new IllegalArgumentException(e); + } + } + + /** + * 匹配pattern数组中的所有pattern解析时间格式,若没有可以解析的方式则抛出异常 + * + * @param dateTimeStr + * @param patternStr + * @return + * @throws IllegalArgumentException + */ + public static Calendar getCalendarByPatterns( String dateTimeStr, String[] patternStr) { + for ( String string : patternStr) { + try { + return getCalendarByPattern(dateTimeStr, string); + } catch ( Exception e) { + } + } + + throw new IllegalArgumentException(); + } + + /** + * 是否有服务器时间 + */ + public static boolean hasServerTime; + /** + * 本地时间和服务器时间的间隔 time server local gap millis + */ + public static long tslgapm; + /** + * 本地时间和服务器时间的间隔 time server string + */ + public static String tss; + + /** + * 获取与服务器时间矫正过的当前时间 + */ + public static Calendar getCurrentDateTime() { + Calendar now = Calendar.getInstance(); + now.setLenient(false); + if (hasServerTime) { + now.setTimeInMillis(now.getTimeInMillis() + tslgapm); + } + return now; + } + + public static Calendar getCurrentDate() { + return cleanCalendarTime(getCurrentDateTime()); + } + + public static long getCurTimeInMillis(){ + return System.currentTimeMillis(); + } + + /** + * login时server的日期 + * + * @return + */ + public static Calendar getLoginServerDate() { + return getCalendar(tss); + } + + /** + * 获得基准日期增加间隔天 + */ + public static Calendar getDateAdd( Calendar start, int interval) { + if (start == null) { + return null; + } + Calendar c = ( Calendar ) start.clone(); + c.add( Calendar.DATE, interval); + return c; + } + + /** + * 获得时间间隔 + * + * @param from + * @param to + * @param unit 时间间隔单位{@link DateTimeUtils#ONE_SECOND},{@link DateTimeUtils#ONE_MINUTE}, + * {@link DateTimeUtils#ONE_HOUR}, {@link DateTimeUtils#ONE_DAY} + * @return + */ + public static long getIntervalTimes( Calendar from, Calendar to, long unit) { + if (from == null || to == null) { + return 0; + } + return Math.abs(from.getTimeInMillis() - to.getTimeInMillis()) / unit; + } + + /** + * 获得日期间隔 忽略小时 + * + * @param startdate + * @param enddate + * @return + */ + + public static int getIntervalDays( String startdate, String enddate, String pattern) { + int betweenDays = 0; + if (startdate == null || enddate == null) { + return betweenDays; + } + + Calendar d1 = getCalendarByPattern(startdate, pattern); + Calendar d2 = getCalendarByPattern(enddate, pattern); + + return getIntervalDays(d1, d2); + } + + public static int getIntervalDays(T from, T to) { + Calendar startdate = getCalendar(from); + Calendar enddate = getCalendar(to); + cleanCalendarTime(startdate); + cleanCalendarTime(enddate); + return (int) getIntervalTimes(startdate, enddate, ONE_DAY); + } + + private static String[] weekdays = {"", "周日", "周一", "周二", "周三", "周四", "周五", "周六",}; + private static String[] weekdays1 = {"", "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六",}; + + /** + * calendar --> 周一~周日 + */ + public static String getWeekDayFromCalendar( Calendar cal) { + if (cal == null) { + throw new IllegalArgumentException(); + } + return weekdays[cal.get( Calendar.DAY_OF_WEEK)]; + } + + /** + * calendar --> 星期日~星期六 + */ + public static String getWeekDayFromCalendar1( Calendar cal) { + if (cal == null) { + throw new IllegalArgumentException(); + } + return weekdays1[cal.get( Calendar.DAY_OF_WEEK)]; + } + + /** + * 判断是否是闰年 这个方法不要改动! + * + * @param date(2009-10-13 || 2009年10月13日 || 2009) + * @return true 是 false 不是 + */ + public static boolean isLeapyear( String date) { + Calendar calendar = getCalendar(date); + if (calendar != null) { + int year = calendar.get( Calendar.YEAR); + return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + } + return false; + } + + // 是否到刷新时间 + public static boolean isRefersh(long beforeTime) { + return isRefersh(DATETIME_FIELD_REFERSH * 1000 * 60, beforeTime); + } + + // 是否到刷新时间 + public static boolean isRefersh(long gap, long beforeTime) { + return new Date().getTime() - beforeTime >= gap; + } + + public static String printCalendarByPattern( Calendar c, String patternStr) { + if (null == c || null == patternStr) { + return null; + } + SimpleDateFormat sdf = new SimpleDateFormat(patternStr, Locale.US); + sdf.setLenient(false); + return sdf.format(c.getTime()); + } + + /** + * 只通过年月日比较两个Calendar + * + * @return c1 < c2 = -1 ; c1 > c2 = 1 ; c1 == c2 = 0 + */ + public static int compareCalendarIgnoreTime( Calendar c1, Calendar c2) { + if (c1.get( Calendar.YEAR) > c2.get( Calendar.YEAR)) { + return 1; + } else if (c1.get( Calendar.YEAR) < c2.get( Calendar.YEAR)) { + return -1; + } else { + if (c1.get( Calendar.MONTH) > c2.get( Calendar.MONTH)) { + return 1; + } else if (c1.get( Calendar.MONTH) < c2.get( Calendar.MONTH)) { + return -1; + } else { + if (c1.get( Calendar.DAY_OF_MONTH) > c2.get( Calendar.DAY_OF_MONTH)) { + return 1; + } else if (c1.get( Calendar.DAY_OF_MONTH) < c2.get( Calendar.DAY_OF_MONTH)) { + return -1; + } else { + return 0; + } + } + } + } + + public static void setTimeWithHHmm( Calendar src, String HH_mm) { + if ( TextUtils.isEmpty(HH_mm) || null == src) { + return; + } + String s[] = HH_mm.split(":"); + if (s.length != 2) { + return; + } + try { + cleanCalendarTime(src); + src.set( Calendar.HOUR_OF_DAY, Integer.valueOf(s[0])); + src.set( Calendar.MINUTE, Integer.valueOf(s[1])); + } catch ( NumberFormatException e) { + } + + } + + public static int getDayDiff(long time1, long time2) { + Date dateA = new Date(time1); + Date dateB = new Date(time2); + Calendar calDateA = Calendar.getInstance(); + calDateA.setTime(dateA); + Calendar calDateB = Calendar.getInstance(); + calDateB.setTime(dateB); + + if (calDateA.get( Calendar.YEAR) == calDateB.get( Calendar.YEAR) + && calDateA.get( Calendar.MONTH) == calDateB.get( Calendar.MONTH)) { + return calDateB.get( Calendar.DAY_OF_MONTH) - calDateA.get( Calendar.DAY_OF_MONTH); + } else if (calDateA.get( Calendar.YEAR) == calDateB.get( Calendar.YEAR) && ((calDateB.get( Calendar.MONTH) - calDateA.get( Calendar.MONTH)) == 1 + || (calDateB.get( Calendar.MONTH) - calDateA.get( Calendar.MONTH)) == -11)) {//处理跨年情况 + return calDateB.get( Calendar.DAY_OF_MONTH) + (getCurrentMonthLastDay() - calDateA.get( Calendar.DAY_OF_MONTH)); + } + return 0; + } + + public static int getCurrentMonthLastDay() { + Calendar a = Calendar.getInstance(); + a.set( Calendar.DATE, 1); + a.roll( Calendar.DATE, -1); + int maxDate = a.get( Calendar.DATE); + return maxDate; + } + + public static String convertToChineseWeekNumber( int number) { + switch (number) { + case 1: + return "一"; + case 2: + return "二"; + case 3: + return "三"; + case 4: + return "四"; + case 5: + return "五"; + case 6: + return "六"; + case 0: + return "日"; + default: + return ""; + } + } + + /** + * 获取分钟数 + */ + private static int getMinutes( String str, int blankCount) { + int ret = 0; + if (str.split(" ").length < (blankCount + 1)) { + return ret; + } + String hh_mm = str.split(" ")[blankCount]; + + String s = ""; + if (!TextUtils.isEmpty(hh_mm) && hh_mm.length() >= 4) { + s = hh_mm.substring(3); + } + try { + ret = Integer.parseInt(s); + } catch ( NumberFormatException e) { + e.printStackTrace(); + } + return ret; + } + + /** + * 获取 06月07 格式的日期 + * @param timestamp 时间戳 + * @return + */ + public static String getTimeText( long timestamp) { + SimpleDateFormat format = new SimpleDateFormat(yyyy_Nian_MM_Yue_dd_Ri, Locale.US); + String strStart = format.format(new Date(timestamp)); + return strStart; + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/DeviceIdUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/DeviceIdUtils.java new file mode 100644 index 0000000000..3e561c9530 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/DeviceIdUtils.java @@ -0,0 +1,118 @@ +package com.mogo.utils; + +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Build; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import androidx.core.content.ContextCompat; + + +import com.mogo.utils.storage.SharedPrefsMgr; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public final class DeviceIdUtils { + + public static final String KEY_DEVICE_ID = "deviceId"; + + private DeviceIdUtils() {} + + private static void saveDeviceId( Context context, String deviceId){ + SharedPrefsMgr.getInstance(context).putString(KEY_DEVICE_ID, deviceId); + } + + public static String getDeviceId( Context context) { + if(context == null){ + throw new NullPointerException("context must not be null."); + } + + final Context appContext = context.getApplicationContext(); + String deviceId = SharedPrefsMgr.getInstance( context ).getString( KEY_DEVICE_ID ); + + if ( TextUtils.isEmpty( deviceId )) { + deviceId = getDeviceIdInternal(appContext); + if (!TextUtils.isEmpty(deviceId)) { + saveDeviceId(appContext,deviceId); + } else { + if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) { + if ( ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE ) == PackageManager.PERMISSION_GRANTED ) { + deviceId = (( TelephonyManager ) appContext.getSystemService( Context.TELEPHONY_SERVICE)).getSimSerialNumber(); + } + }else{ + deviceId = (( TelephonyManager ) appContext.getSystemService( Context.TELEPHONY_SERVICE)).getSimSerialNumber(); + } + if (!TextUtils.isEmpty(deviceId)) { + saveDeviceId(appContext,deviceId); + } else { + deviceId = getDeviceSerial(); + if (!TextUtils.isEmpty(deviceId) && !deviceId.equalsIgnoreCase("unknown")) { + saveDeviceId(appContext,deviceId); + } else { + deviceId = getAndroidId(appContext); + if (!TextUtils.isEmpty(deviceId)) { + saveDeviceId(appContext,deviceId); + } else { + deviceId = String.valueOf( System.currentTimeMillis()); + saveDeviceId(appContext,deviceId); + } + } + } + } + } + + return deviceId; + } + + private static String getDeviceIdInternal( Context context) { + String id = ""; + + if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) { + if ( ContextCompat.checkSelfPermission( context, Manifest.permission.READ_PHONE_STATE ) != PackageManager.PERMISSION_GRANTED ) { + return id; + } + } + + TelephonyManager telephonymanager = ( TelephonyManager ) context.getSystemService( Context.TELEPHONY_SERVICE); + if (telephonymanager != null) { + id = telephonymanager.getDeviceId(); + if ( TextUtils.isEmpty(id)) + id = ""; + } + return id; + } + + private static String getAndroidId( Context context) { + String s = ""; + s = Settings.Secure.getString(context.getContentResolver(), "android_id"); + if ( TextUtils.isEmpty(s)) + s = ""; + return s; + } + + private static String getDeviceSerial() { + String serial = "unknown"; + try { + Class clazz = Class.forName("android.os.Build"); + Class paraTypes = Class.forName("java.lang.String"); + Method method = clazz.getDeclaredMethod("getString", paraTypes); + if (!method.isAccessible()) { + method.setAccessible(true); + } + serial = ( String ) method.invoke(new Build(), "ro.serialno"); + } catch ( ClassNotFoundException e) { + e.printStackTrace(); + } catch ( NoSuchMethodException e) { + e.printStackTrace(); + } catch ( InvocationTargetException e) { + e.printStackTrace(); + } catch ( IllegalAccessException e) { + e.printStackTrace(); + } + return serial; + } +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/FileUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/FileUtils.java new file mode 100644 index 0000000000..e7067de16b --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/FileUtils.java @@ -0,0 +1,250 @@ +package com.mogo.utils; + +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.text.TextUtils; +import android.util.Base64; + +import androidx.core.content.FileProvider; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; + + +public class FileUtils { + public static boolean createFileDir( String fileDir ) { + + if ( TextUtils.isEmpty( fileDir ) ) { + return false; + } + + try { + File dir = new File( fileDir ); + return dir.exists() || dir.mkdir(); + } catch ( Exception e ) { + return false; + } + } + + public static boolean createFileDir( File dir ) { + if ( dir == null ) { + return false; + } + try { + return dir.exists() || dir.mkdir(); + } catch ( Exception e ) { + return false; + } + } + + public static void writeToFile( String fileDir, String fileName, String content ) { + if ( fileDir == null || fileName == null || content == null ) { + return; + } + if ( !createFileDir( fileDir ) ) { + return; + } + FileOutputStream fos = null; + OutputStreamWriter osw = null; + try { + fos = new FileOutputStream( fileDir + fileName, true ); + osw = new OutputStreamWriter( fos ); + osw.write( content ); + osw.flush(); + } catch ( IOException e ) { + e.printStackTrace(); + } finally { + IOUtils.closeSilently( fos ); + IOUtils.closeSilently( osw ); + } + } + + /** + * Read a text file into a String, optionally limiting the length. + */ + public static String readTextFile( File file ) { + InputStream is = null; + BufferedInputStream bis = null; + ByteArrayOutputStream bos = null; + String text = null; + try { + is = new FileInputStream( file ); + bis = new BufferedInputStream( is ); + bos = new ByteArrayOutputStream(); + int len; + byte[] data = new byte[1024]; + do { + len = bis.read( data ); + if ( len > 0 ) bos.write( data, 0, len ); + } while ( len == data.length ); + text = bos.toString(); + } catch ( Exception e ) { + e.printStackTrace(); + } finally { + IOUtils.closeSilently( is ); + IOUtils.closeSilently( bis ); + IOUtils.closeSilently( bos ); + } + return text; + } + + public static String fileToBase64( File file ) { + String base64 = null; + InputStream in = null; + try { + in = new FileInputStream( file ); + byte[] bytes = new byte[in.available()]; + int length = in.read( bytes ); + base64 = Base64.encodeToString( bytes, 0, length, Base64.DEFAULT ); + } catch ( FileNotFoundException e ) { + e.printStackTrace(); + } catch ( IOException e ) { + e.printStackTrace(); + } finally { + IOUtils.closeSilently( in ); + } + return base64; + } + + /** + * Writes string to file. Basically same as "echo -n $string > $filename" + */ + public static void stringToFile( String filename, String string ) { + FileWriter out = null; + try { + out = new FileWriter( filename ); + out.write( string ); + } catch ( Exception e ) { + e.printStackTrace(); + } finally { + IOUtils.closeSilently( out ); + } + } + + public static InputStream stringToStream( String content ) { + InputStream inputStream = null; + try { + inputStream = new ByteArrayInputStream( content.getBytes() ); + } catch ( Exception e ) { + e.printStackTrace(); + } + return inputStream; + } + + public static String streamToString( InputStream is ) throws IOException { + String content = null; + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + int i = -1; + while ( ( i = is.read() ) != -1 ) { + bos.write( i ); + } + content = bos.toString(); + } catch ( Exception e ) { + e.printStackTrace(); + } + return content; + } + + public static String getStringFromFile( Context context, String fileName ) { + FileInputStream fis = null; + ByteArrayOutputStream os = null; + String content = null; + try { + fis = context.openFileInput( fileName ); + os = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length = -1; + while ( ( length = fis.read( buffer ) ) != -1 ) { + os.write( buffer, 0, length ); + } + content = os.toString(); + } catch ( Exception e ) { + e.printStackTrace(); + } finally { + IOUtils.closeSilently( fis ); + IOUtils.closeSilently( os ); + } + return content; + } + + public static InputStream getStreamFromFile( Context context, String fileName ) { + FileInputStream fis = null; + try { + fis = context.openFileInput( fileName ); + } catch ( Exception e ) { + e.printStackTrace(); + } + return fis; + } + + public static void saveStringToFile( Context context, String content, String fileName ) { + try { + FileOutputStream fos = context.openFileOutput( fileName, Context.MODE_PRIVATE ); + fos.write( content.getBytes() ); + IOUtils.closeSilently( fos ); + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + /** + * 将scheme为file的uri转成FileProvider 提供的content uri + */ + public static Uri convertFileUriToFileProviderUri( Context context, Uri uri ) { + if ( uri == null ) return null; + if ( ContentResolver.SCHEME_FILE.equals( uri.getScheme() ) ) { + return getUriForFile( context, new File( uri.getPath() ) ); + } + return uri; + + } + + /** + * 创建一个用于拍照图片输出路径的Uri (FileProvider) + */ + public static Uri getUriForFile( Context context, File file ) { + return FileProvider.getUriForFile( context, getFileProviderName( context ), file ); + } + + public static String getFileProviderName( Context context ) { + return context.getPackageName() + ".fileprovider"; + } + + /** + * 把Uri 解析出文件绝对路径 + */ + public static String parseOwnUri( Context context, Uri uri ) { + if ( uri == null ) return null; + String path; + if ( TextUtils.equals( uri.getAuthority(), getFileProviderName( context ) ) ) { + path = new File( uri.getPath() ).getAbsolutePath(); + } else { + path = uri.getPath(); + } + return path; + } + + public static String getFileStreamPath( Context context, String name ) { + String absFileName = null; + try { + File file = context.getFileStreamPath( name ); + if ( file != null && file.exists() ) { + absFileName = file.getAbsolutePath(); + } + } catch ( Exception e ) { + e.printStackTrace(); + } + return absFileName; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/IOUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/IOUtils.java new file mode 100644 index 0000000000..26c7936c2b --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/IOUtils.java @@ -0,0 +1,49 @@ +package com.mogo.utils; + + +import androidx.annotation.Nullable; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.InputStream; + +public class IOUtils { + + public static byte[] inputToBytes( InputStream is) { + if(is == null){ + return null; + } + + ByteArrayOutputStream bos = null; + byte[] result = null; + + try{ + bos = new ByteArrayOutputStream(); + byte[] buff = new byte[100]; + int rc = 0; + while ((rc = is.read(buff, 0, 100)) > 0) { + bos.write(buff, 0, rc); + } + + result = bos.toByteArray(); + }catch ( Exception e){ + e.printStackTrace(); + result = null; + }finally { + closeSilently(bos); + } + + return result; + } + + public static void closeSilently(@Nullable Closeable c) { + if (c == null) return; + try { + c.close(); + c = null; + } catch ( Throwable t) { + t.printStackTrace(); + } + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/IntentUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/IntentUtils.java new file mode 100644 index 0000000000..f36c5d3557 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/IntentUtils.java @@ -0,0 +1,361 @@ +package com.mogo.utils; + +import android.net.Uri; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.TreeMap; + +public class IntentUtils { + + /** + * urlDecode的时候会把'+'转换成' ' + * + * @param uri + * @return + */ + public static TreeMap< String, String > splitParams2( Uri uri) { + if (uri == null) { + return new TreeMap< String, String >(); + } + Set< String > keys = getQueryParameterNames(uri); + TreeMap< String, String > map = new TreeMap< String, String >(); + + for ( String key : keys) { + map.put(key, getQueryParameter(uri, key)); + } + return map; + } + + /** + * urlDecode时
+ * 4.0版本以后 '+' ->' '
+ * 2.3版本之前 '+' -> '+'
+ * + * @param uri + * @return + */ + public static HashMap< String, String > splitParams1( Uri uri) { + if (uri == null) { + return new HashMap< String, String >(); + } + Set< String > keys = getQueryParameterNames(uri); + HashMap< String, String > map = new HashMap< String, String >(keys.size()); + + for ( String key : keys) { + map.put(key, uri.getQueryParameter(key)); + } + return map; + } + + /** + * Searches the query string for the first value with the given key. + * + *

+ * Warning: this decoded the '+' character as ' '. + * + * @param key which will be encoded + * @throws UnsupportedOperationException if this isn't a hierarchical URI + * @throws NullPointerException if key is null + * @return the decoded value or null if no parameter is found + */ + public static String getQueryParameter( Uri uri, String key) { + if (uri.isOpaque()) { + throw new UnsupportedOperationException("This isn't a hierarchical URI."); + } + if (key == null) { + throw new NullPointerException("key"); + } + + final String query = uri.getEncodedQuery(); + if (query == null) { + return null; + } + + final String encodedKey = Uri.encode(key, null); + final int length = query.length(); + int start = 0; + do { + int nextAmpersand = query.indexOf('&', start); + int end = nextAmpersand != -1 ? nextAmpersand : length; + + int separator = query.indexOf('=', start); + if (separator > end || separator == -1) { + separator = end; + } + + if (separator - start == encodedKey.length() + && query.regionMatches(start, encodedKey, 0, encodedKey.length())) { + if (separator == end) { + return ""; + } else { + String encodedValue = query.substring(separator + 1, end); + return UriCodec.decode(encodedValue, true, UriCodec.UTF_8, false); + } + } + + // Move start to end of name. + if (nextAmpersand != -1) { + start = nextAmpersand + 1; + } else { + break; + } + } while (true); + return null; + } + + public static Set< String > getQueryParameterNames( Uri uri) { + if (uri.isOpaque()) { + throw new UnsupportedOperationException("This isn't a hierarchical URI."); + } + + String query = uri.getEncodedQuery(); + if (query == null) { + return Collections.emptySet(); + } + + Set< String > names = new LinkedHashSet< String >(); + int start = 0; + do { + int next = query.indexOf('&', start); + int end = next == -1 ? query.length() : next; + + int separator = query.indexOf('=', start); + if (separator > end || separator == -1) { + separator = end; + } + + String name = query.substring(start, separator); + names.add( Uri.decode(name)); + + // Move start to end of name. + start = end + 1; + } while (start < query.length()); + + return Collections.unmodifiableSet(names); + } + + /** + * Encodes and decodes {@code application/x-www-form-urlencoded} content. Subclasses define exactly which characters + * are legal. + * + *

+ * By default, UTF-8 is used to encode escaped characters. A single input character like "\u0080" may be encoded to + * multiple octets like %C2%80. + */ + abstract static class UriCodec { + + /** + * Returns true if {@code c} does not need to be escaped. + */ + protected abstract boolean isRetained(char c); + + /** + * Throws if {@code s} is invalid according to this encoder. + */ + public final String validate( String uri, int start, int end, String name) throws URISyntaxException { + for (int i = start; i < end;) { + char ch = uri.charAt(i); + if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || isRetained(ch)) { + i++; + } else if (ch == '%') { + if (i + 2 >= end) { + throw new URISyntaxException(uri, "Incomplete % sequence in " + name, i); + } + int d1 = hexToInt(uri.charAt(i + 1)); + int d2 = hexToInt(uri.charAt(i + 2)); + if (d1 == -1 || d2 == -1) { + throw new URISyntaxException(uri, "Invalid % sequence: " + uri.substring(i, i + 3) + " in " + + name, i); + } + i += 3; + } else { + throw new URISyntaxException(uri, "Illegal character in " + name, i); + } + } + return uri.substring(start, end); + } + + /** + * Throws if {@code s} contains characters that are not letters, digits or in {@code legal}. + */ + public static void validateSimple( String s, String legal) throws URISyntaxException { + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if (!(ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || legal.indexOf(ch) > -1)) { + throw new URISyntaxException(s, "Illegal character", i); + } + } + } + + /** + * Encodes {@code s} and appends the orderList to {@code builder}. + * + * @param isPartiallyEncoded true to fix input that has already been partially or fully encoded. For example, + * input of "hello%20world" is unchanged with isPartiallyEncoded=true but would be double-escaped to + * "hello%2520world" otherwise. + * @throws UnsupportedEncodingException + */ + private void appendEncoded( StringBuilder builder, String s, Charset charset, boolean isPartiallyEncoded) + throws UnsupportedEncodingException { + if (s == null) { + throw new NullPointerException(); + } + + int escapeStart = -1; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || isRetained(c) || c == '%' + && isPartiallyEncoded) { + if (escapeStart != -1) { + appendHex(builder, s.substring(escapeStart, i), charset); + escapeStart = -1; + } + if (c == '%' && isPartiallyEncoded) { + // this is an encoded 3-character sequence like "%20" + builder.append(s, i, i + 3); + i += 2; + } else if (c == ' ') { + builder.append('+'); + } else { + builder.append(c); + } + } else if (escapeStart == -1) { + escapeStart = i; + } + } + if (escapeStart != -1) { + appendHex(builder, s.substring(escapeStart, s.length()), charset); + } + } + + public final String encode( String s, Charset charset) throws UnsupportedEncodingException { + // Guess a bit larger for encoded form + StringBuilder builder = new StringBuilder(s.length() + 16); + appendEncoded(builder, s, charset, false); + return builder.toString(); + } + + static final Charset UTF_8 = Charset.forName("UTF-8"); + + public final void appendEncoded( StringBuilder builder, String s) throws UnsupportedEncodingException { + appendEncoded(builder, s, UTF_8, false); + } + + public final void appendPartiallyEncoded( StringBuilder builder, String s) throws UnsupportedEncodingException { + appendEncoded(builder, s, UTF_8, true); + } + + /** + * @param convertPlus true to convert '+' to ' '. + * @param throwOnFailure true to throw an IllegalArgumentException on invalid escape sequences; false to replace + * them with the replacement character (U+fffd). + * @throws UnsupportedEncodingException + */ + public static String decode( String s, boolean convertPlus, Charset charset, boolean throwOnFailure) { + if (s.indexOf('%') == -1 && (!convertPlus || s.indexOf('+') == -1)) { + return s; + } + + StringBuilder result = new StringBuilder(s.length()); + ByteArrayOutputStream out = new ByteArrayOutputStream();; + try { + for (int i = 0; i < s.length();) { + char c = s.charAt(i); + if (c == '%') { + do { + int d1, d2; + if (i + 2 < s.length() && (d1 = hexToInt(s.charAt(i + 1))) != -1 + && (d2 = hexToInt(s.charAt(i + 2))) != -1) { + out.write((byte) ((d1 << 4) + d2)); + } else if (throwOnFailure) { + throw new IllegalArgumentException("Invalid % sequence at " + i + ": " + s); + } else { + byte[] replacement = "\ufffd".getBytes(charset.name()); + out.write(replacement, 0, replacement.length); + } + i += 3; + } while (i < s.length() && s.charAt(i) == '%'); + result.append(new String(out.toByteArray(), charset.name())); + out.reset(); + } else { + if (convertPlus && c == '+') { + c = ' '; + } + result.append(c); + i++; + } + } + } catch ( UnsupportedEncodingException e) { + Log.e("IntentUtils",e.getMessage()); + }finally { + try { + out.close(); + } catch ( IOException e) { + Log.e("IntentUtils",e.getMessage()); + } + } + return result.toString(); + } + + /** + * Like {@link Character#digit}, but without support for non-ASCII characters. + */ + private static int hexToInt(char c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('a' <= c && c <= 'f') { + return 10 + c - 'a'; + } else if ('A' <= c && c <= 'F') { + return 10 + c - 'A'; + } else { + return -1; + } + } + + public static String decode( String s) throws UnsupportedEncodingException { + return decode(s, false, UTF_8, true); + } + + private static void appendHex( StringBuilder builder, String s, Charset charset) + throws UnsupportedEncodingException { + for (byte b : s.getBytes(charset.name())) { + appendHex(builder, b); + } + } + + private static void appendHex( StringBuilder sb, byte b) { + sb.append('%'); + sb.append(byteToHexString(b, true)); + } + + /** + * The digits for every supported radix. + */ + private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z' }; + + private static final char[] UPPER_CASE_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z' }; + + public static String byteToHexString( byte b, boolean upperCase) { + char[] digits = upperCase ? UPPER_CASE_DIGITS : DIGITS; + char[] buf = new char[2]; // We always want two digits. + buf[0] = digits[b >> 4 & 0xf]; + buf[1] = digits[b & 0xf]; + return new String(buf, 0, 2); + } + + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/MapUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/MapUtils.java new file mode 100644 index 0000000000..e2deae92c4 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/MapUtils.java @@ -0,0 +1,25 @@ +package com.mogo.utils; + +import java.util.Map; +import java.util.Set; + +/** + * Created by congtaowang on 2018/11/20. + */ +public class MapUtils { + + public static void putNotAllowNull( final Map< String, Object > target, final String key, final Object value ) { + if ( target != null && key != null && value != null ) { + target.put( key, value ); + } + } + + public static void putAllNotAllowNull( final Map< String, Object > target, final Map< String, Object > source ) { + if ( target != null && source != null && !source.isEmpty() ) { + final Set< String > keys = source.keySet(); + for ( String key : keys ) { + putNotAllowNull( target, key, source.get( key ) ); + } + } + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/MatcherUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/MatcherUtils.java new file mode 100644 index 0000000000..25c5bf568a --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/MatcherUtils.java @@ -0,0 +1,398 @@ +/* + * 创建日期:2012-10-9 + */ +package com.mogo.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MatcherUtils { + /** + * 匹配:Email地址 + */ + public static final String REGEX_EMAIL = "^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)+$"; + /** + * 匹配:手机号码 + */ + public static final String REGEX_MOBILE_NUMBER = "^[1][3-9]\\d{9}$"; + + /** + * 正则表达式中使用的特殊的计算符号,如:'$', '*', ... + */ + public static final char[] PATTERN_REGEX_SPECIAL_CHARACTERS = { '$', // 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。 + '(', ')', // 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 \( 和 \)。 + '*', // 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。 + '+', // 匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。 + '.', // 匹配除换行符 \n之外的任何单字符。要匹配 .,请使用 \。 + '[', // 标记一个中括号表达式的开始。要匹配 [,请使用 \[。 + '?', // 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?。 + '\\', // 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。 + '^', // 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。要匹配 ^ 字符本身,请使用 \^。 + '{', // 标记限定符表达式的开始。要匹配 {,请使用 \{。 + '|', // 指明两项之间的一个选择。要匹配 |,请使用 \|。 + }; + + /** 英文标点符号 */ + private static final int TYPE_PUNCT = 1; + /** 数字 */ + private static final int TYPE_DIGIT = 2; + /** 小写字母字符 */ + private static final int TYPE_LOWER_LETTER = 3; + /** 大写字母字符 */ + private static final int TYPE_UPPER_LETTER = 4; + /** 汉字字符串包括了中文标点符号 */ + private static final int TYPE_CJK = 5; + /** 汉字字符串剔除了中文标点符号 */ + private static final int TYPE_NO_PUNCTUATION_CJK = 6; + /** 是否是空白字符 */ + private static final int TYPE_WHITESPACE = 7; + /** 不是字母 数字 英文标点符号 */ + private static final int TYPE_NOT_PUNCT_DIGIT_LETTER = 8; + + private static boolean isMatchesChar(int type, char ch) { + switch (type) { + case TYPE_PUNCT: + return isPunct(ch); + case TYPE_DIGIT: + return Character.isDigit(ch); + case TYPE_LOWER_LETTER: + return isLowerLetter(ch); + case TYPE_UPPER_LETTER: + return isUpperLetter(ch); + case TYPE_CJK: + return isCJK(ch); + case TYPE_NO_PUNCTUATION_CJK: + return isNoPunctuationCJK(ch); + case TYPE_WHITESPACE: + return Character.isWhitespace(ch); + case TYPE_NOT_PUNCT_DIGIT_LETTER: + return !( Character.isDigit(ch) || isLowerLetter(ch) || isUpperLetter(ch) || isPunct(ch)); + default: + return false; + } + } + + private static boolean isMatches(int type, String input) { + if (input != null && input.length() > 0) { + for (int i = input.length() - 1; i >= 0; i--) { + if (!isMatchesChar(type, input.charAt(i))) { + return false; + } else if (i == 0) { + return true; + } + } + } + return false; + } + + private static boolean isMatchesInclude(int type, String input) { + if (input != null && input.length() > 0) { + for (int i = input.length() - 1; i >= 0; i--) { + if (isMatchesChar(type, input.charAt(i))) { + return true; + } + } + } + return false; + } + + /** + * 匹配正则表达式 + * + * @param regex + * @param input + * @return + */ + public static boolean isMatches( String regex, String input) { + return input == null ? false : Pattern.compile(regex).matcher(input).find(); + } + + /** + * 匹配正则表达式(忽略大小写) + * + * @param regex + * @param input + * @return + */ + public static boolean isMatchesIgnoreCase( String regex, String input) { + return input == null ? false : Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(input).find(); + } + + /** + * 是否是正则表达式中使用的特殊的计算符号,如:'$', '*', ... + * + * @param ch + * @return + */ + public static boolean isPatternRegexSpecialCharacter(char ch) { + for (int i = PATTERN_REGEX_SPECIAL_CHARACTERS.length - 1; i >= 0; i--) { + if (ch == PATTERN_REGEX_SPECIAL_CHARACTERS[i]) { + return true; + } + } + return false; + } + + /** + * 是否是标点符号,(POSIX 字符类(仅 US-ASCII) \p{Punct} 标点符号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~) + * + * @param ch + * @return + */ + public static boolean isPunct(char ch) { + return (ch >= 0x21 && ch <= 0x2F) // !"#$%&'()*+,-./ + || (ch >= 0x3A && ch <= 0x40) // :;<=>?@ + || (ch >= 0x5B && ch <= 0x60) // [\]^_` + || (ch >= 0x7B && ch <= 0x7E);// {|}~ + } + + /** + * 是否是标点符号字符串 中文标点识别不了 + * + * @param input + * @return + */ + public static boolean isPunct( String input) { + return isMatches(TYPE_PUNCT, input); + } + + /** + * 是否包含标点符号字符串 + * + * @param input + * @return + */ + public static boolean isIncludePunct( String input) { + return isMatchesInclude(TYPE_PUNCT, input); + } + + /** + * 是否包含非字母,数字以及Punct规定的字符串 + * + * @param input + * @return 含有则返回true,否则 返回false + */ + public static boolean isIncludeInvalidChar( String input) { + return isMatchesInclude(TYPE_NOT_PUNCT_DIGIT_LETTER, input); + } + + /** + * 是否是数字字符串 + * + * @param input + * @return + */ + public static boolean isDigit( String input) { + return isMatches(TYPE_DIGIT, input); + } + + /** + * 是否包含数字字符串 + * + * @param input + * @return + */ + public static boolean isIncludeDigit( String input) { + return isMatchesInclude(TYPE_DIGIT, input); + } + + /** + * 是否是小写字母字符([a-z]) + * + * @param ch + * @return + */ + public static boolean isLowerLetter(char ch) { + return ch >= 0x61 && ch <= 0x7A; + } + + /** + * 是否是小写字母字符串([a-z]) + * + * @param input + * @return + */ + public static boolean isLowerLetter( String input) { + return isMatches(TYPE_LOWER_LETTER, input); + } + + /** + * 是否包含小写字母字符串([a-z]) + * + * @param input + * @return + */ + public static boolean isIncludeLowerLetter( String input) { + return isMatchesInclude(TYPE_LOWER_LETTER, input); + } + + /** + * 是否是大写字母字符([A-Z]) + * + * @param ch + * @return + */ + public static boolean isUpperLetter(char ch) { + return ch >= 0x41 && ch <= 0x5A; + } + + /** + * 是否是大写字母字符串([A-Z]) + * + * @param + * @return + */ + public static boolean isUpperLetter( String input) { + return isMatches(TYPE_UPPER_LETTER, input); + } + + /** + * 是否包含大写字母字符串([A-Z]) + * + * @param input + * @return + */ + public static boolean isIncludeUpperLetter( String input) { + return isMatchesInclude(TYPE_UPPER_LETTER, input); + } + + /** + * 是否是字母字符串([A-Za-z]) + * + * @param ch + * @return + */ + public static boolean isLetter(char ch) { + return isLowerLetter(ch) || isUpperLetter(ch); + } + + /** + * 是否是汉字字符(广义上的汉字字符,很多都无法用输入法输出的汉字) + * + * @param c + * @return + */ + public static boolean isCJK(char c) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); + if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS // CJK统一汉字 \\u4E00-\\u9fAF + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A // CJK统一汉字扩展-A \\u3400-\\u4dBF + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS // CJK兼容汉字 \\uF900-\\uFAFF + || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION // CJK符号和标点 \\u3000-\\u303F + || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION // 广义标点 \\u2000-\\u206F + || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { // 半形及全形字符 \\uFF00-\\uFFEF + return true; + } + return false; + } + + /** + * 是否是汉字字符(不包含标点) + * + * @param c + * @return + */ + public static boolean isNoPunctuationCJK(char c) { + Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); + if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS // CJK统一汉字 \\u4E00-\\u9fAF + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A // CJK统一汉字扩展-A \\u3400-\\u4dBF + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS) { // CJK兼容汉字 \\uF900-\\uFAFF + return true; + } + return false; + } + + /** + * 是否是汉字字符(不包含标点) + * + * @param input + * @return + */ + public static boolean isNoPunctuationCJK( String input) { + return isMatches(TYPE_NO_PUNCTUATION_CJK, input); + } + + /** + * 是否是汉字字符串(广义上的汉字字符,很多都无法用输入法输出的汉字) + * + * @param input + * @return + */ + public static boolean isCJK( String input) { + return isMatches(TYPE_CJK, input); + } + + /** + * 是否包含汉字字符串(广义上的汉字字符,很多都无法用输入法输出的汉字) + * + * @param input + * @return + */ + public static boolean isIncludeCJK( String input) { + return isMatchesInclude(TYPE_CJK, input); + } + + /** + * 是否是空白字符 + * + * @param input + * @return + */ + public static boolean isWhitespace( String input) { + return isMatches(TYPE_WHITESPACE, input); + } + + /** + * 是否包含空白字符 + * + * @param input + * @return + */ + public static boolean isIncludeWhitespace( String input) { + return isMatchesInclude(TYPE_WHITESPACE, input); + } + + /** + * 是否符合密码规则 + * + * @param input + * @return + */ + public static boolean isFetionPassword( String input) { + // 密码必须含有字母和数字,符号可选,但是必须符合Punct标准,否则返回false + // 首先判断是否含有无效字符,含有,则返回false + if (isIncludeInvalidChar(input)) { + return false; + } + // 长度不符合要求,返回 false + if (input.length() < 6 || input.length() > 16) { + return false; + } + int tmpValue = 0; + if (isMatches("[A-Za-z]{1,15}", input)) { + tmpValue++; + } + if (isMatches("[0-9]{1,15}", input)) { + tmpValue++; + } + if (tmpValue >= 2) { + return true; + } else { + return false; + } + } + + /** + * 1个或者多个0-9组成的数字 + * + * @param str + * @return + */ + public static boolean isNumeric( String str) { + Pattern pattern = Pattern.compile("[0-9]+"); + Matcher isNum = pattern.matcher(str); + if (!isNum.matches()) { + return false; + } + return true; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ProcessUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ProcessUtils.java new file mode 100644 index 0000000000..3b9d54eaae --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ProcessUtils.java @@ -0,0 +1,86 @@ +package com.mogo.utils; + +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; +import android.content.Context; +import android.os.Process; +import android.text.TextUtils; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.List; + +public class ProcessUtils { + + public static boolean isMainProcess( Context context ) { + try { + ActivityManager activityManager = ( ( ActivityManager ) context.getSystemService( Context.ACTIVITY_SERVICE ) ); + if ( activityManager == null ) { + return false; + } + List< RunningAppProcessInfo > raps = activityManager.getRunningAppProcesses(); + if ( raps == null || raps.size() <= 0 ) { + return false; + } + int pid = Process.myPid(); + String packageName = context.getPackageName(); + for ( RunningAppProcessInfo info : raps ) { + if ( packageName.equals( info.processName ) ) { + return pid == info.pid; + } + } + return false; + } catch ( Exception e ) { + e.printStackTrace(); + } + + return false; + } + + public static String getPackageName() { + String packageName = null; + BufferedReader reader = null; + + try { + reader = new BufferedReader( new InputStreamReader( new FileInputStream( "/proc/" + Process.myPid() + "/cmdline" ) ) ); + packageName = reader.readLine().trim(); + } catch ( Exception e ) { + e.printStackTrace(); + packageName = String.valueOf( Process.myPid() ); + } finally { + if ( reader != null ) try { + reader.close(); + } catch ( IOException e ) { + e.printStackTrace(); + } + } + + return packageName; + } + + /** + * 获取进程号对应的进程名 + * + * @param pid 进程号 + * @return 进程名 + */ + private static String getProcessName( int pid ) { + BufferedReader reader = null; + try { + reader = new BufferedReader( new FileReader( "/proc/" + pid + "/cmdline" ) ); + String processName = reader.readLine(); + if ( !TextUtils.isEmpty( processName ) ) { + processName = processName.trim(); + } + return processName; + } catch ( Throwable t ) { + t.printStackTrace(); + } finally { + IOUtils.closeSilently( reader ); + } + return null; + } +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ReflectUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ReflectUtils.java new file mode 100644 index 0000000000..f3ee5ec450 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ReflectUtils.java @@ -0,0 +1,73 @@ +package com.mogo.utils; + +import android.util.Log; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + + +@SuppressWarnings("rawtypes") +public class ReflectUtils { + + public static Object getField( Class clazz, Object target, String name) throws Exception { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field.get(target); + } + + public static void setField( Class clazz, Object target, String name, Object value) throws Exception { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + field.set(target, value); + } + + public static void setFieldNoException( Class clazz, Object target, String name, Object value) { + try { + ReflectUtils.setField(clazz, target, name, value); + } catch ( Exception e) { + Log.e("ReflectUtil",e.getMessage(),e); + } + } + + @SuppressWarnings("unchecked") + public static Object invoke( Class clazz, Object target, String name, Object... args) + throws Exception { + Class[] parameterTypes = null; + if (args != null) { + parameterTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + parameterTypes[i] = args[i].getClass(); + } + } + + Method method = clazz.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method.invoke(target, args); + } + + @SuppressWarnings("unchecked") + public static Object invoke( Class clazz, Object target, String name, Class[] parameterTypes, Object... args) + throws Exception { + Method method = clazz.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method.invoke(target, args); + } + + @SuppressWarnings("unchecked") + public static T invokeConstructor( Class clazz, Class[] parameterTypes, Object... args) + throws Exception { + Constructor constructor = clazz.getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return (T) constructor.newInstance(args); + } + + @SuppressWarnings("unchecked") + public static T invokeConstructor( Class clazz) + throws Exception { + Constructor constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + return (T) constructor.newInstance(); + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ResourcesHelper.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ResourcesHelper.java new file mode 100644 index 0000000000..c46161df83 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ResourcesHelper.java @@ -0,0 +1,72 @@ +package com.mogo.utils; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.util.DisplayMetrics; + +public class ResourcesHelper { + + public static Resources getResources( Context context){ + return context.getResources(); + } + + public static int getColor( Context context, int rid) { + return getResources(context).getColor(rid); + } + + public static ColorStateList getColorStateList( Context context, int rid) { + return getResources(context).getColorStateList(rid); + } + + public static String getString( Context context, int rid) { + return getResources(context).getString(rid); + } + + public static String getString( Context context, int rid, int param1, int param2) { + return getResources(context).getString(rid, param1, param2); + } + + public static String getString( Context context, int rid, String str) { + return getResources(context).getString(rid, str); + } + + public static String[] getStringArray( Context context, int rid) { + return getResources(context).getStringArray(rid); + } + + public static Drawable getDrawable( Context context, int rid) { + return getResources(context).getDrawable(rid); + } + + public static float getDimension( Context context, int rid) { + return getResources(context).getDimension(rid); + } + + public static DisplayMetrics getDisplayMetrics( Context context) { + return getResources(context).getDisplayMetrics(); + } + + public static int getDisplayMetrics( Context context, int x) { + return getResources(context).getDimensionPixelSize(x); + } + + public static int getDimensionPixelSize( Context context, int x) { + return getResources(context).getDimensionPixelSize(x); + } + + public static int getInteger( Context context, int rid) { + return getResources(context).getInteger(rid); + } + + public static XmlResourceParser getXml( Context context, int rid) { + return getResources(context).getXml(rid); + } + + public static Configuration getConfiguration( Context context) { + return getResources(context).getConfiguration(); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/SoftKeyboardUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/SoftKeyboardUtils.java new file mode 100644 index 0000000000..ed4cf52ce4 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/SoftKeyboardUtils.java @@ -0,0 +1,33 @@ +package com.mogo.utils; + +import android.content.Context; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +/** + * Created by congtaowang on 2018/12/20. + */ +public class SoftKeyboardUtils { + + public static void show( Context context ) { + try { + InputMethodManager imm = ( InputMethodManager ) context.getSystemService( Context.INPUT_METHOD_SERVICE ); + if ( imm != null ) { + imm.toggleSoftInput( 0, InputMethodManager.HIDE_NOT_ALWAYS ); + } + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + public static void hidden( Context context, EditText editText ) { + try { + InputMethodManager imm = ( InputMethodManager ) context.getSystemService( Context.INPUT_METHOD_SERVICE ); + if ( imm != null ) { + imm.hideSoftInputFromWindow( editText.getWindowToken(), 0 ); + } + } catch ( Exception e ) { + e.printStackTrace(); + } + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ThreadPoolService.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ThreadPoolService.java new file mode 100644 index 0000000000..3fb2341761 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ThreadPoolService.java @@ -0,0 +1,16 @@ +package com.mogo.utils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ThreadPoolService { + + private static final ExecutorService SERVICE = Executors.newScheduledThreadPool( 1 ); + + private ThreadPoolService() { + } + + public static void execute( Runnable task ) { + SERVICE.execute( task ); + } +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/TipToast.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/TipToast.java new file mode 100644 index 0000000000..93af03bbae --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/TipToast.java @@ -0,0 +1,176 @@ +package com.mogo.utils; + +/** + * 2016/1/1 by congtaowang + * + * @Version 1.0 + */ + +import android.content.Context; +import android.os.Handler; +import android.text.TextUtils; +import android.view.View; +import android.widget.Toast; + +import com.mogo.utils.logger.Logger; + + +/** + * Init TipToast in your Application + */ +public final class TipToast { + + private static final String TAG = "TipToast"; + + private static Toast sToast = null; + private static final Object sSyncObject = new Object(); + private static Handler sHandler = null; + private static Context sContext; + private static ToastViewGenerator sGenerator; + + public static void init( Context context, ToastViewGenerator generator ) { + TipToast.sContext = context; + sHandler = new Handler( context.getMainLooper() ); + sGenerator = generator; + } + + public static void recycle() { + sContext = null; + sHandler = null; + if ( sToast != null ) { + sToast.cancel(); + sToast = null; + } + sGenerator = null; + } + + private static void tip( final String message, int duration ) { + if ( !checkParams() ) { + return; + } + if ( TextUtils.isEmpty( message ) ) { + return; + } + new ToastThread( new StringToastRunnable( sContext, message, duration ) ).start(); + } + + private static void tip( final int msgId, int duration ) { + if ( !checkParams() ) { + return; + } + try { + if ( TextUtils.isEmpty( ResourcesHelper.getString( sContext, msgId ) ) ) { + return; + } + } catch ( Exception e ) { + return; + } + tip( ResourcesHelper.getString( sContext, msgId ), duration ); + } + + private static boolean checkParams() { + if ( sContext == null ) { + Logger.e( TAG, "context can't be null." ); + return false; + } + if ( sHandler == null ) { + Logger.e( TAG, "sHandler can't be null." ); + return false; + } + return true; + } + + public static void tip( final String message ) { + tip( message, Toast.LENGTH_SHORT ); + } + + public static void tip( final int msgId ) { + tip( msgId, Toast.LENGTH_SHORT ); + } + + public static void longTip( String message ) { + tip( message, Toast.LENGTH_LONG ); + } + + public static void longTip( int msgId ) { + tip( msgId, Toast.LENGTH_LONG ); + } + + public static void shortTip( String message ) { + tip( message, Toast.LENGTH_SHORT ); + } + + public static void shortTip( int msgId ) { + tip( msgId, Toast.LENGTH_SHORT ); + } + + static class ToastThread extends Thread { + public ToastThread( Runnable runnable ) { + super( runnable ); + } + + } + + static class StringToastRunnable implements Runnable { + + Context context; + String msg; + int duration; + + public StringToastRunnable( Context context, String msg, int duration ) { + this.context = context; + this.msg = msg; + this.duration = duration; + } + + @Override + public void run() { + + if ( sHandler == null ) { + return; + } + + sHandler.post( new Runnable() { + + @Override + public void run() { + synchronized ( sSyncObject ) { + + if ( context == null ) { + return; + } + + if ( sToast != null ) { + sToast.cancel(); + } + + if ( sGenerator == null ) { + sToast = Toast.makeText( context, msg, duration ); + } + + if ( sGenerator == null ) { + sToast = Toast.makeText( context, msg, duration ); + } else { + sToast = new Toast( context ); + final View view = sGenerator.make( context, msg ); + if ( view != null ) { + sToast.setView( view ); + sToast.setDuration( duration ); + } else { + sToast = Toast.makeText( context, msg, duration ); + } + } + if ( sToast != null ) { + sToast.show(); + } + } + } + } ); + } + } + + public interface ToastViewGenerator { + View make( Context context, String message ); + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/UiThreadHandler.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/UiThreadHandler.java new file mode 100644 index 0000000000..7225548d91 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/UiThreadHandler.java @@ -0,0 +1,41 @@ +package com.mogo.utils; + +import android.os.Handler; +import android.os.Looper; + +public class UiThreadHandler { + + private static Handler sUiHandler = new Handler( Looper.getMainLooper() ); + + private static Object sToken = new Object(); + + public UiThreadHandler() { + } + + public static boolean post( Runnable r ) { + return sUiHandler != null && sUiHandler.post( r ); + } + + public static boolean postDelayed( Runnable r, long delayMillis ) { + return sUiHandler != null && sUiHandler.postDelayed( r, delayMillis ); + } + + public static Handler getsUiHandler() { + return sUiHandler; + } + + public static boolean postOnceDelayed( Runnable r, long delayMillis ) { + if ( sUiHandler == null ) { + return false; + } else { + sUiHandler.removeCallbacks( r, sToken ); + return sUiHandler.postDelayed( r, delayMillis ); + } + } + + public static void removeCallbacks( Runnable runnable ) { + if ( sUiHandler != null ) { + sUiHandler.removeCallbacks( runnable ); + } + } +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/ValidateUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/ValidateUtils.java new file mode 100644 index 0000000000..2bf7c332c6 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/ValidateUtils.java @@ -0,0 +1,10 @@ +package com.mogo.utils; + +/** + * @author congtaowang + * @since 2019-06-11 + *

+ * 验证格式工具类 + */ +public class ValidateUtils { +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/WindowUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/WindowUtils.java new file mode 100644 index 0000000000..65416700e8 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/WindowUtils.java @@ -0,0 +1,71 @@ +package com.mogo.utils; + +import android.content.Context; + +public class WindowUtils { + + public static int getStatusBarHeight( Context context ) { + if ( context == null ) { + return 0; + } else { + int result = 0; + int resourceId = context.getResources().getIdentifier( "status_bar_height", "dimen", "android" ); + if ( resourceId > 0 ) { + result = context.getResources().getDimensionPixelSize( resourceId ); + } + + return result; + } + } + + /** + * 根据手机的分辨率从 dp 的单位 转成为 px(像素) + */ + public static int dip2px( Context context, float dpValue ) { + if ( context == null ) { + return 0; + } + final float scale = context.getResources().getDisplayMetrics().density; + return ( int ) ( dpValue * scale + 0.5f ); + } + + /** + * 根据手机的分辨率从 px(像素) 的单位 转成为 dp + */ + public static int px2dip( Context context, float pxValue ) { + if ( context == null ) { + return 0; + } + final float scale = context.getResources().getDisplayMetrics().density; + return ( int ) ( pxValue / scale + 0.5f ); + } + + + public static String getScreenPixels( Context context ) { + if ( context == null ) { + return ""; + } + return getScreenWidth( context ) + "*" + getScreenHeight( context ); + } + + public static int getScreenWidth( Context context ) { + if ( context == null ) { + return 0; + } + return context.getResources().getDisplayMetrics().widthPixels; + } + + public static int getScreenHeight( Context context ) { + if ( context == null ) { + return 0; + } + return context.getResources().getDisplayMetrics().heightPixels; + } + + public static int getScreenDpi( Context context ) { + if ( context == null ) { + return 0; + } + return context.getResources().getDisplayMetrics().densityDpi; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/BinaryDecoder.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/BinaryDecoder.java new file mode 100644 index 0000000000..9e4cf08970 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/BinaryDecoder.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Defines common decoding methods for byte array decoders. + * + * @version $Id: BinaryDecoder.java 1379145 2012-08-30 21:02:52Z tn $ + */ +public interface BinaryDecoder extends Decoder { + + /** + * Decodes a byte array and returns the results as a byte array. + * + * @param source + * A byte array which has been encoded with the appropriate encoder + * @return a byte array that contains decoded content + * @throws DecoderException + * A decoder exception is thrown if a Decoder encounters a failure condition during the decode process. + */ + byte[] decode( byte[] source ) throws DecoderException; +} + diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/BinaryEncoder.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/BinaryEncoder.java new file mode 100644 index 0000000000..5d40f71c5e --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/BinaryEncoder.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Defines common encoding methods for byte array encoders. + * + * @version $Id: BinaryEncoder.java 1379145 2012-08-30 21:02:52Z tn $ + */ +public interface BinaryEncoder extends Encoder { + + /** + * Encodes a byte array and return the encoded data as a byte array. + * + * @param source + * Data to be encoded + * @return A byte array containing the encoded data + * @throws EncoderException + * thrown if the Encoder encounters a failure condition during the encoding process. + */ + byte[] encode( byte[] source ) throws EncoderException; +} + diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/CharEncoding.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/CharEncoding.java new file mode 100644 index 0000000000..80a0081204 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/CharEncoding.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Character encoding names required of every implementation of the Java platform. + * + * From the Java documentation Standard charsets: + *

+ * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. Consult the release + * documentation for your implementation to see if any other encodings are supported. + *

+ * + *
    + *
  • US-ASCII
    + * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
  • + *
  • ISO-8859-1
    + * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
  • + *
  • UTF-8
    + * Eight-bit Unicode Transformation Format.
  • + *
  • UTF-16BE
    + * Sixteen-bit Unicode Transformation Format, big-endian byte order.
  • + *
  • UTF-16LE
    + * Sixteen-bit Unicode Transformation Format, little-endian byte order.
  • + *
  • UTF-16
    + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order + * accepted on input, big-endian used on output.)
  • + *
+ * + * This perhaps would best belong in the [lang] project. Even if a similar interface is defined in [lang], it is not + * foreseen that [codec] would be made to depend on [lang]. + * + *

+ * This class is immutable and thread-safe. + *

+ * + * @see Standard charsets + * @since 1.4 + * @version $Id: CharEncoding.java 1563226 2014-01-31 19:38:06Z ggregory $ + */ +public class CharEncoding { + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + */ + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + */ + public static final String US_ASCII = "US-ASCII"; + + /** + * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark + * (either order accepted on input, big-endian used on output) + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + */ + public static final String UTF_16 = "UTF-16"; + + /** + * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + */ + public static final String UTF_16BE = "UTF-16BE"; + + /** + * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + */ + public static final String UTF_16LE = "UTF-16LE"; + + /** + * Eight-bit Unicode Transformation Format. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + */ + public static final String UTF_8 = "UTF-8"; +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/CharSequenceUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/CharSequenceUtils.java new file mode 100644 index 0000000000..bf5d9c3f3d --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/CharSequenceUtils.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mogo.utils.digest; + +/** + *

+ * Operations on {@link CharSequence} that are null safe. + *

+ *

+ * Copied from Apache Commons Lang r1586295 on April 10, 2014 (day of 3.3.2 release). + *

+ * + * @see CharSequence + * @since 1.10 + */ +public class CharSequenceUtils { + + /** + * Green implementation of regionMatches. + * + * @param cs + * the CharSequence to be processed + * @param ignoreCase + * whether or not to be case insensitive + * @param thisStart + * the index to start on the cs CharSequence + * @param substring + * the CharSequence to be looked for + * @param start + * the index to start on the substring CharSequence + * @param length + * character length of the region + * @return whether the region matched + */ + static boolean regionMatches( final CharSequence cs, final boolean ignoreCase, final int thisStart, + final CharSequence substring, final int start, final int length) { + if (cs instanceof String && substring instanceof String ) { + return (( String ) cs).regionMatches(ignoreCase, thisStart, ( String ) substring, start, length); + } + int index1 = thisStart; + int index2 = start; + int tmpLen = length; + + while (tmpLen-- > 0) { + char c1 = cs.charAt(index1++); + char c2 = substring.charAt(index2++); + + if (c1 == c2) { + continue; + } + + if (!ignoreCase) { + return false; + } + + // The same check as in String.regionMatches(): + if ( Character.toUpperCase(c1) != Character.toUpperCase(c2) && + Character.toLowerCase(c1) != Character.toLowerCase(c2)) { + return false; + } + } + + return true; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Charsets.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Charsets.java new file mode 100644 index 0000000000..83104fb837 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Charsets.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mogo.utils.digest; + +import java.nio.charset.Charset; + +/** + * Charsets required of every implementation of the Java platform. + * + * From the Java documentation Standard + * charsets: + *

+ * Every implementation of the Java platform is required to support the following character encodings. Consult the + * release documentation for your implementation to see if any other encodings are supported. Consult the release + * documentation for your implementation to see if any other encodings are supported. + *

+ * + *
    + *
  • US-ASCII
    + * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the Unicode character set.
  • + *
  • ISO-8859-1
    + * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
  • + *
  • UTF-8
    + * Eight-bit Unicode Transformation Format.
  • + *
  • UTF-16BE
    + * Sixteen-bit Unicode Transformation Format, big-endian byte order.
  • + *
  • UTF-16LE
    + * Sixteen-bit Unicode Transformation Format, little-endian byte order.
  • + *
  • UTF-16
    + * Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial byte-order mark (either order + * accepted on input, big-endian used on output.)
  • + *
+ * + * This perhaps would best belong in the Commons Lang project. Even if a similar class is defined in Commons Lang, it is + * not foreseen that Commons Codec would be made to depend on Commons Lang. + * + *

+ * This class is immutable and thread-safe. + *

+ * + * @see Standard charsets + * @since 1.7 + * @version $Id: CharEncoding.java 1173287 2011-09-20 18:16:19Z ggregory $ + */ +public class Charsets { + + // + // This class should only contain Charset instances for required encodings. This guarantees that it will load + // correctly and without delay on all Java platforms. + // + + /** + * Returns the given Charset or the default Charset if the given Charset is null. + * + * @param charset + * A charset or null. + * @return the given Charset or the default Charset if the given Charset is null + */ + public static Charset toCharset( final Charset charset) { + return charset == null ? Charset.defaultCharset() : charset; + } + + /** + * Returns a Charset for the named charset. If the name is null, return the default Charset. + * + * @param charset + * The name of the requested charset, may be null. + * @return a Charset for the named charset + * @throws java.nio.charset.UnsupportedCharsetException + * If the named charset is unavailable + */ + public static Charset toCharset( final String charset) { + return charset == null ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset ISO_8859_1 = Charset.forName(CharEncoding.ISO_8859_1); + + /** + * Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block of the Unicode character set. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset US_ASCII = Charset.forName(CharEncoding.US_ASCII); + + /** + * Sixteen-bit Unicode Transformation Format, The byte order specified by a mandatory initial byte-order mark + * (either order accepted on input, big-endian used on output) + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_16 = Charset.forName(CharEncoding.UTF_16); + + /** + * Sixteen-bit Unicode Transformation Format, big-endian byte order. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_16BE = Charset.forName(CharEncoding.UTF_16BE); + + /** + * Sixteen-bit Unicode Transformation Format, little-endian byte order. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_16LE = Charset.forName(CharEncoding.UTF_16LE); + + /** + * Eight-bit Unicode Transformation Format. + *

+ * Every implementation of the Java platform is required to support this character encoding. + * + * @see Standard charsets + * @deprecated Use Java 7's {@link java.nio.charset.StandardCharsets} + */ + @Deprecated + public static final Charset UTF_8 = Charset.forName(CharEncoding.UTF_8); +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Decoder.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Decoder.java new file mode 100644 index 0000000000..fbb68cacfa --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Decoder.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Provides the highest level of abstraction for Decoders. + *

+ * This is the sister interface of {@link Encoder}. All Decoders implement this common generic interface. + * Allows a user to pass a generic Object to any Decoder implementation in the codec package. + *

+ * One of the two interfaces at the center of the codec package. + * + * @version $Id: Decoder.java 1379145 2012-08-30 21:02:52Z tn $ + */ +public interface Decoder { + + /** + * Decodes an "encoded" Object and returns a "decoded" Object. Note that the implementation of this interface will + * try to cast the Object parameter to the specific type expected by a particular Decoder implementation. If a + * {@link ClassCastException} occurs this decode method will throw a DecoderException. + * + * @param source + * the object to decode + * @return a 'decoded" object + * @throws DecoderException + * a decoder exception can be thrown for any number of reasons. Some good candidates are that the + * parameter passed to this method is null, a param cannot be cast to the appropriate type for a + * specific encoder. + */ + Object decode( Object source ) throws DecoderException; +} + diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/DecoderException.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/DecoderException.java new file mode 100644 index 0000000000..4bbfb808ed --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/DecoderException.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Thrown when there is a failure condition during the decoding process. This exception is thrown when a {@link Decoder} + * encounters a decoding specific exception such as invalid data, or characters outside of the expected range. + * + * @version $Id: DecoderException.java 1619948 2014-08-22 22:53:55Z ggregory $ + */ +public class DecoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public DecoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + */ + public DecoderException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException( final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public DecoderException(final Throwable cause) { + super(cause); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/DigestUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/DigestUtils.java new file mode 100644 index 0000000000..5ee2a590f1 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/DigestUtils.java @@ -0,0 +1,817 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + + +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * Operations to simplify common {@link MessageDigest} tasks. + * This class is immutable and thread-safe. + * + * @version $Id: DigestUtils.java 1634433 2014-10-27 01:10:47Z ggregory $ + */ +public class DigestUtils { + + private static final int STREAM_BUFFER_LENGTH = 1024; + + /** + * Read through an InputStream and returns the digest for the data + * + * @param digest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + */ + private static byte[] digest( final MessageDigest digest, final InputStream data) throws IOException { + return updateDigest(digest, data).digest(); + } + + /** + * Returns a MessageDigest for the given algorithm. + * + * @param algorithm + * the name of the algorithm requested. See Appendix A in the Java Cryptography Architecture Reference Guide for information about standard + * algorithm names. + * @return A digest instance. + * @see MessageDigest#getInstance(String) + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught. + */ + public static MessageDigest getDigest( final String algorithm) { + try { + return MessageDigest.getInstance(algorithm); + } catch (final NoSuchAlgorithmException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * Returns an MD2 MessageDigest. + * + * @return An MD2 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because MD2 is a + * built-in algorithm + * @see MessageDigestAlgorithms#MD2 + * @since 1.7 + */ + public static MessageDigest getMd2Digest() { + return getDigest(MessageDigestAlgorithms.MD2); + } + + /** + * Returns an MD5 MessageDigest. + * + * @return An MD5 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because MD5 is a + * built-in algorithm + * @see MessageDigestAlgorithms#MD5 + */ + public static MessageDigest getMd5Digest() { + return getDigest(MessageDigestAlgorithms.MD5); + } + + /** + * Returns an SHA-1 digest. + * + * @return An SHA-1 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because SHA-1 is a + * built-in algorithm + * @see MessageDigestAlgorithms#SHA_1 + * @since 1.7 + */ + public static MessageDigest getSha1Digest() { + return getDigest(MessageDigestAlgorithms.SHA_1); + } + + /** + * Returns an SHA-256 digest. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @return An SHA-256 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because SHA-256 is a + * built-in algorithm + * @see MessageDigestAlgorithms#SHA_256 + */ + public static MessageDigest getSha256Digest() { + return getDigest(MessageDigestAlgorithms.SHA_256); + } + + /** + * Returns an SHA-384 digest. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @return An SHA-384 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because SHA-384 is a + * built-in algorithm + * @see MessageDigestAlgorithms#SHA_384 + */ + public static MessageDigest getSha384Digest() { + return getDigest(MessageDigestAlgorithms.SHA_384); + } + + /** + * Returns an SHA-512 digest. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @return An SHA-512 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught, which should never happen because SHA-512 is a + * built-in algorithm + * @see MessageDigestAlgorithms#SHA_512 + */ + public static MessageDigest getSha512Digest() { + return getDigest(MessageDigestAlgorithms.SHA_512); + } + + /** + * Returns an SHA-1 digest. + * + * @return An SHA-1 digest instance. + * @throws IllegalArgumentException + * when a {@link NoSuchAlgorithmException} is caught + * @deprecated Use {@link #getSha1Digest()} + */ + @Deprecated + public static MessageDigest getShaDigest() { + return getSha1Digest(); + } + + /** + * Calculates the MD2 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest + * @return MD2 digest + * @since 1.7 + */ + public static byte[] md2(final byte[] data) { + return getMd2Digest().digest(data); + } + + /** + * Calculates the MD2 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest + * @return MD2 digest + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static byte[] md2(final InputStream data) throws IOException { + return digest(getMd2Digest(), data); + } + + /** + * Calculates the MD2 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return MD2 digest + * @since 1.7 + */ + public static byte[] md2(final String data) { + return md2(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the MD2 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD2 digest as a hex string + * @since 1.7 + */ + public static String md2Hex( final byte[] data) { + return Hex.encodeHexString(md2(data)); + } + + /** + * Calculates the MD2 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD2 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static String md2Hex( final InputStream data) throws IOException { + return Hex.encodeHexString(md2(data)); + } + + /** + * Calculates the MD2 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD2 digest as a hex string + * @since 1.7 + */ + public static String md2Hex( final String data) { + return Hex.encodeHexString(md2(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest + * @return MD5 digest + */ + public static byte[] md5(final byte[] data) { + return getMd5Digest().digest(data); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest + * @return MD5 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] md5(final InputStream data) throws IOException { + return digest(getMd5Digest(), data); + } + + /** + * Calculates the MD5 digest and returns the value as a 16 element byte[]. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return MD5 digest + */ + public static byte[] md5(final String data) { + return md5(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + */ + public static String md5Hex( final byte[] data) { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String md5Hex( final InputStream data) throws IOException { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the MD5 digest and returns the value as a 32 character hex string. + * + * @param data + * Data to digest + * @return MD5 digest as a hex string + */ + public static String md5Hex( final String data) { + return Hex.encodeHexString(md5(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @deprecated Use {@link #sha1(byte[])} + */ + @Deprecated + public static byte[] sha(final byte[] data) { + return sha1(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + * @deprecated Use {@link #sha1(InputStream)} + */ + @Deprecated + public static byte[] sha(final InputStream data) throws IOException { + return sha1(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @deprecated Use {@link #sha1(String)} + */ + @Deprecated + public static byte[] sha(final String data) { + return sha1(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @since 1.7 + */ + public static byte[] sha1(final byte[] data) { + return getSha1Digest().digest(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest + * @return SHA-1 digest + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static byte[] sha1(final InputStream data) throws IOException { + return digest(getSha1Digest(), data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a byte[]. + * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-1 digest + */ + public static byte[] sha1(final String data) { + return sha1(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @since 1.7 + */ + public static String sha1Hex( final byte[] data) { + return Hex.encodeHexString(sha1(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.7 + */ + public static String sha1Hex( final InputStream data) throws IOException { + return Hex.encodeHexString(sha1(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @since 1.7 + */ + public static String sha1Hex( final String data) { + return Hex.encodeHexString(sha1(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-256 digest + * @since 1.4 + */ + public static byte[] sha256(final byte[] data) { + return getSha256Digest().digest(data); + } + + /** + * Calculates the SHA-256 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-256 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha256(final InputStream data) throws IOException { + return digest(getSha256Digest(), data); + } + + /** + * Calculates the SHA-256 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-256 digest + * @since 1.4 + */ + public static byte[] sha256(final String data) { + return sha256(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @since 1.4 + */ + public static String sha256Hex( final byte[] data) { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha256Hex( final InputStream data) throws IOException { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-256 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-256 digest as a hex string + * @since 1.4 + */ + public static String sha256Hex( final String data) { + return Hex.encodeHexString(sha256(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-384 digest + * @since 1.4 + */ + public static byte[] sha384(final byte[] data) { + return getSha384Digest().digest(data); + } + + /** + * Calculates the SHA-384 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-384 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha384(final InputStream data) throws IOException { + return digest(getSha384Digest(), data); + } + + /** + * Calculates the SHA-384 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-384 digest + * @since 1.4 + */ + public static byte[] sha384(final String data) { + return sha384(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @since 1.4 + */ + public static String sha384Hex( final byte[] data) { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha384Hex( final InputStream data) throws IOException { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-384 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-384 digest as a hex string + * @since 1.4 + */ + public static String sha384Hex( final String data) { + return Hex.encodeHexString(sha384(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-512 digest + * @since 1.4 + */ + public static byte[] sha512(final byte[] data) { + return getSha512Digest().digest(data); + } + + /** + * Calculates the SHA-512 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-512 digest + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static byte[] sha512(final InputStream data) throws IOException { + return digest(getSha512Digest(), data); + } + + /** + * Calculates the SHA-512 digest and returns the value as a byte[]. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return SHA-512 digest + * @since 1.4 + */ + public static byte[] sha512(final String data) { + return sha512(StringUtils.getBytesUtf8(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @since 1.4 + */ + public static String sha512Hex( final byte[] data) { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + */ + public static String sha512Hex( final InputStream data) throws IOException { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-512 digest and returns the value as a hex string. + *

+ * Throws a RuntimeException on JRE versions prior to 1.4.0. + *

+ * + * @param data + * Data to digest + * @return SHA-512 digest as a hex string + * @since 1.4 + */ + public static String sha512Hex( final String data) { + return Hex.encodeHexString(sha512(data)); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @deprecated Use {@link #sha1Hex(byte[])} + */ + @Deprecated + public static String shaHex( final byte[] data) { + return sha1Hex(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @throws IOException + * On error reading from the stream + * @since 1.4 + * @deprecated Use {@link #sha1Hex(InputStream)} + */ + @Deprecated + public static String shaHex( final InputStream data) throws IOException { + return sha1Hex(data); + } + + /** + * Calculates the SHA-1 digest and returns the value as a hex string. + * + * @param data + * Data to digest + * @return SHA-1 digest as a hex string + * @deprecated Use {@link #sha1Hex(String)} + */ + @Deprecated + public static String shaHex( final String data) { + return sha1Hex(data); + } + + /** + * Updates the given {@link MessageDigest}. + * + * @param messageDigest + * the {@link MessageDigest} to update + * @param valueToDigest + * the value to update the {@link MessageDigest} with + * @return the updated {@link MessageDigest} + * @since 1.7 + */ + public static MessageDigest updateDigest( final MessageDigest messageDigest, final byte[] valueToDigest) { + messageDigest.update(valueToDigest); + return messageDigest; + } + + /** + * Reads through an InputStream and updates the digest for the data + * + * @param digest + * The MessageDigest to use (e.g. MD5) + * @param data + * Data to digest + * @return the digest + * @throws IOException + * On error reading from the stream + * @since 1.8 + */ + public static MessageDigest updateDigest( final MessageDigest digest, final InputStream data) throws IOException { + final byte[] buffer = new byte[STREAM_BUFFER_LENGTH]; + int read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); + + while (read > -1) { + digest.update(buffer, 0, read); + read = data.read(buffer, 0, STREAM_BUFFER_LENGTH); + } + + return digest; + } + + /** + * Updates the given {@link MessageDigest}. + * + * @param messageDigest + * the {@link MessageDigest} to update + * @param valueToDigest + * the value to update the {@link MessageDigest} with; + * converted to bytes using {@link StringUtils#getBytesUtf8(String)} + * @return the updated {@link MessageDigest} + * @since 1.7 + */ + public static MessageDigest updateDigest( final MessageDigest messageDigest, final String valueToDigest) { + messageDigest.update(StringUtils.getBytesUtf8(valueToDigest)); + return messageDigest; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Encoder.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Encoder.java new file mode 100644 index 0000000000..115d49c620 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Encoder.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Provides the highest level of abstraction for Encoders. + *

+ * This is the sister interface of {@link Decoder}. Every implementation of Encoder provides this + * common generic interface which allows a user to pass a generic Object to any Encoder implementation + * in the codec package. + * + * @version $Id: Encoder.java 1379145 2012-08-30 21:02:52Z tn $ + */ +public interface Encoder { + + /** + * Encodes an "Object" and returns the encoded content as an Object. The Objects here may just be + * byte[] or Strings depending on the implementation used. + * + * @param source + * An object to encode + * @return An "encoded" Object + * @throws EncoderException + * An encoder exception is thrown if the encoder experiences a failure condition during the encoding + * process. + */ + Object encode( Object source ) throws EncoderException; +} + diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/EncoderException.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/EncoderException.java new file mode 100644 index 0000000000..babc0bcc23 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/EncoderException.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Thrown when there is a failure condition during the encoding process. This exception is thrown when an + * {@link Encoder} encounters a encoding specific exception such as invalid data, inability to calculate a checksum, + * characters outside of the expected range. + * + * @version $Id: EncoderException.java 1619948 2014-08-22 22:53:55Z ggregory $ + */ +public class EncoderException extends Exception { + + /** + * Declares the Serial Version Uid. + * + * @see Always Declare Serial Version Uid + */ + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with null as its detail message. The cause is not initialized, and may + * subsequently be initialized by a call to {@link #initCause}. + * + * @since 1.4 + */ + public EncoderException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause is not initialized, and may subsequently + * be initialized by a call to {@link #initCause}. + * + * @param message + * a useful message relating to the encoder specific error. + */ + public EncoderException(final String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * + *

+ * Note that the detail message associated with cause is not automatically incorporated into this + * exception's detail message. + *

+ * + * @param message + * The detail message which is saved for later retrieval by the {@link #getMessage()} method. + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException( final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception with the specified cause and a detail message of (cause==null ? + * null : cause.toString()) (which typically contains the class and detail message of cause). + * This constructor is useful for exceptions that are little more than wrappers for other throwables. + * + * @param cause + * The cause which is saved for later retrieval by the {@link #getCause()} method. A null + * value is permitted, and indicates that the cause is nonexistent or unknown. + * @since 1.4 + */ + public EncoderException(final Throwable cause) { + super(cause); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Hex.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Hex.java new file mode 100644 index 0000000000..9879ed8f93 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/Hex.java @@ -0,0 +1,328 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + + +import java.nio.charset.Charset; + +/** + * Converts hexadecimal Strings. The charset used for certain operation can be set, the default is set in + * {@link #DEFAULT_CHARSET_NAME} + * + * This class is thread-safe. + * + * @since 1.1 + * @version $Id: Hex.java 1619948 2014-08-22 22:53:55Z ggregory $ + */ +public class Hex implements BinaryEncoder, BinaryDecoder { + + /** + * Default charset name is {@link Charsets#UTF_8} + * + * @since 1.7 + */ + public static final Charset DEFAULT_CHARSET = Charsets.UTF_8; + + /** + * Default charset name is {@link CharEncoding#UTF_8} + * + * @since 1.4 + */ + public static final String DEFAULT_CHARSET_NAME = CharEncoding.UTF_8; + + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_LOWER = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_UPPER = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + /** + * Converts an array of characters representing hexadecimal values into an array of bytes of those same values. The + * returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param data + * An array of characters containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied char array. + * @throws DecoderException + * Thrown if an odd number or illegal of characters is supplied + */ + public static byte[] decodeHex(final char[] data) throws DecoderException { + + final int len = data.length; + + if ((len & 0x01) != 0) { + throw new DecoderException("Odd number of characters."); + } + + final byte[] out = new byte[len >> 1]; + + // two characters form the hex value. + for (int i = 0, j = 0; j < len; i++) { + int f = toDigit(data[j], j) << 4; + j++; + f = f | toDigit(data[j], j); + j++; + out[i] = (byte) (f & 0xFF); + } + + return out; + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @return A char[] containing hexadecimal characters + */ + public static char[] encodeHex(final byte[] data) { + return encodeHex(data, true); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @param toLowerCase + * true converts to lowercase, false to uppercase + * @return A char[] containing hexadecimal characters + * @since 1.4 + */ + public static char[] encodeHex(final byte[] data, final boolean toLowerCase) { + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * Converts an array of bytes into an array of characters representing the hexadecimal values of each byte in order. + * The returned array will be double the length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @param toDigits + * the output alphabet + * @return A char[] containing hexadecimal characters + * @since 1.4 + */ + protected static char[] encodeHex(final byte[] data, final char[] toDigits) { + final int l = data.length; + final char[] out = new char[l << 1]; + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + return out; + } + + /** + * Converts an array of bytes into a String representing the hexadecimal values of each byte in order. The returned + * String will be double the length of the passed array, as it takes two characters to represent any given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @return A String containing hexadecimal characters + * @since 1.4 + */ + public static String encodeHexString( final byte[] data) { + return new String(encodeHex(data)); + } + + /** + * Converts a hexadecimal character to an integer. + * + * @param ch + * A character to convert to an integer digit + * @param index + * The index of the character in the source + * @return An integer + * @throws DecoderException + * Thrown if ch is an illegal hex character + */ + protected static int toDigit(final char ch, final int index) throws DecoderException { + final int digit = Character.digit(ch, 16); + if (digit == -1) { + throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index); + } + return digit; + } + + private final Charset charset; + + /** + * Creates a new codec with the default charset name {@link #DEFAULT_CHARSET} + */ + public Hex() { + // use default encoding + this.charset = DEFAULT_CHARSET; + } + + /** + * Creates a new codec with the given Charset. + * + * @param charset + * the charset. + * @since 1.7 + */ + public Hex(final Charset charset) { + this.charset = charset; + } + + /** + * Creates a new codec with the given charset name. + * + * @param charsetName + * the charset name. + * @throws java.nio.charset.UnsupportedCharsetException + * If the named charset is unavailable + * @since 1.4 + * @since 1.7 throws UnsupportedCharsetException if the named charset is unavailable + */ + public Hex(final String charsetName) { + this( Charset.forName(charsetName)); + } + + /** + * Converts an array of character bytes representing hexadecimal values into an array of bytes of those same values. + * The returned array will be half the length of the passed array, as it takes two characters to represent any given + * byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param array + * An array of character bytes containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied byte array (representing characters). + * @throws DecoderException + * Thrown if an odd number of characters is supplied to this function + * @see #decodeHex(char[]) + */ + @Override + public byte[] decode(final byte[] array) throws DecoderException { + return decodeHex(new String(array, getCharset()).toCharArray()); + } + + /** + * Converts a String or an array of character bytes representing hexadecimal values into an array of bytes of those + * same values. The returned array will be half the length of the passed String or array, as it takes two characters + * to represent any given byte. An exception is thrown if the passed char array has an odd number of elements. + * + * @param object + * A String or, an array of character bytes containing hexadecimal digits + * @return A byte array containing binary data decoded from the supplied byte array (representing characters). + * @throws DecoderException + * Thrown if an odd number of characters is supplied to this function or the object is not a String or + * char[] + * @see #decodeHex(char[]) + */ + @Override + public Object decode( final Object object) throws DecoderException { + try { + final char[] charArray = object instanceof String ? (( String ) object).toCharArray() : (char[]) object; + return decodeHex(charArray); + } catch (final ClassCastException e) { + throw new DecoderException(e.getMessage(), e); + } + } + + /** + * Converts an array of bytes into an array of bytes for the characters representing the hexadecimal values of each + * byte in order. The returned array will be double the length of the passed array, as it takes two characters to + * represent any given byte. + *

+ * The conversion from hexadecimal characters to the returned bytes is performed with the charset named by + * {@link #getCharset()}. + *

+ * + * @param array + * a byte[] to convert to Hex characters + * @return A byte[] containing the bytes of the hexadecimal characters + * @since 1.7 No longer throws IllegalStateException if the charsetName is invalid. + * @see #encodeHex(byte[]) + */ + @Override + public byte[] encode(final byte[] array) { + return encodeHexString(array).getBytes(this.getCharset()); + } + + /** + * Converts a String or an array of bytes into an array of characters representing the hexadecimal values of each + * byte in order. The returned array will be double the length of the passed String or array, as it takes two + * characters to represent any given byte. + *

+ * The conversion from hexadecimal characters to bytes to be encoded to performed with the charset named by + * {@link #getCharset()}. + *

+ * + * @param object + * a String, or byte[] to convert to Hex characters + * @return A char[] containing hexadecimal characters + * @throws EncoderException + * Thrown if the given object is not a String or byte[] + * @see #encodeHex(byte[]) + */ + @Override + public Object encode( final Object object) throws EncoderException { + try { + final byte[] byteArray = object instanceof String ? + (( String ) object).getBytes(this.getCharset()) : (byte[]) object; + return encodeHex(byteArray); + } catch (final ClassCastException e) { + throw new EncoderException(e.getMessage(), e); + } + } + + /** + * Gets the charset. + * + * @return the charset. + * @since 1.7 + */ + public Charset getCharset() { + return this.charset; + } + + /** + * Gets the charset name. + * + * @return the charset name. + * @since 1.4 + */ + public String getCharsetName() { + return this.charset.name(); + } + + /** + * Returns a string representation of the object, which includes the charset name. + * + * @return a string representation of the object. + */ + @Override + public String toString() { + return super.toString() + "[charsetName=" + this.charset + "]"; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/MessageDigestAlgorithms.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/MessageDigestAlgorithms.java new file mode 100644 index 0000000000..9ecd4e4f9b --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/MessageDigestAlgorithms.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +import java.security.MessageDigest; + +/** + * Standard {@link MessageDigest} algorithm names from the Java Cryptography Architecture Standard Algorithm Name + * Documentation. + *

+ * This class is immutable and thread-safe. + *

+ * + * @see Java Cryptography + * Architecture Standard Algorithm Name Documentation + * @since 1.7 + * @version $Id: MessageDigestAlgorithms.java 1585867 2014-04-09 00:12:36Z ggregory $ + */ +public class MessageDigestAlgorithms { + + private MessageDigestAlgorithms() { + // cannot be instantiated. + } + + /** + * The MD2 message digest algorithm defined in RFC 1319. + */ + public static final String MD2 = "MD2"; + + /** + * The MD5 message digest algorithm defined in RFC 1321. + */ + public static final String MD5 = "MD5"; + + /** + * The SHA-1 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_1 = "SHA-1"; + + /** + * The SHA-256 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_256 = "SHA-256"; + + /** + * The SHA-384 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_384 = "SHA-384"; + + /** + * The SHA-512 hash algorithm defined in the FIPS PUB 180-2. + */ + public static final String SHA_512 = "SHA-512"; + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/StringEncoder.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/StringEncoder.java new file mode 100644 index 0000000000..91dfa24b4f --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/StringEncoder.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + +/** + * Defines common encoding methods for String encoders. + * + * @version $Id: StringEncoder.java 1379145 2012-08-30 21:02:52Z tn $ + */ +public interface StringEncoder extends Encoder { + + /** + * Encodes a String and returns a String. + * + * @param source + * the String to encode + * @return the encoded String + * @throws EncoderException + * thrown if there is an error condition during the encoding process. + */ + String encode( String source ) throws EncoderException; +} + diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/StringUtils.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/StringUtils.java new file mode 100644 index 0000000000..c847ec094c --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/digest/StringUtils.java @@ -0,0 +1,384 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.mogo.utils.digest; + + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + * Converts String to and from bytes using the encodings required by the Java specification. These encodings are + * specified in + * Standard charsets. + * + *

This class is immutable and thread-safe.

+ * + * @see CharEncoding + * @see Standard charsets + * @version $Id: StringUtils.java 1634456 2014-10-27 05:26:56Z ggregory $ + * @since 1.4 + */ +public class StringUtils { + + /** + *

+ * Compares two CharSequences, returning true if they represent equal sequences of characters. + *

+ * + *

+ * nulls are handled without exceptions. Two null references are considered to be equal. + * The comparison is case sensitive. + *

+ * + *
+     * StringUtils.equals(null, null)   = true
+     * StringUtils.equals(null, "abc")  = false
+     * StringUtils.equals("abc", null)  = false
+     * StringUtils.equals("abc", "abc") = true
+     * StringUtils.equals("abc", "ABC") = false
+     * 
+ * + *

+ * Copied from Apache Commons Lang r1583482 on April 10, 2014 (day of 3.3.2 release). + *

+ * + * @see Object#equals(Object) + * @param cs1 + * the first CharSequence, may be null + * @param cs2 + * the second CharSequence, may be null + * @return true if the CharSequences are equal (case-sensitive), or both null + * @since 1.10 + */ + public static boolean equals( final CharSequence cs1, final CharSequence cs2) { + if (cs1 == cs2) { + return true; + } + if (cs1 == null || cs2 == null) { + return false; + } + if (cs1 instanceof String && cs2 instanceof String ) { + return cs1.equals(cs2); + } + return CharSequenceUtils.regionMatches(cs1, false, 0, cs2, 0, Math.max(cs1.length(), cs2.length())); + } + + /** + * Calls {@link String#getBytes(Charset)} + * + * @param string + * The string to encode (if null, return null). + * @param charset + * The {@link Charset} to encode the String + * @return the encoded bytes + */ + private static byte[] getBytes( final String string, final Charset charset) { + if (string == null) { + return null; + } + return string.getBytes(charset); + } + + /** + * Encodes the given string into a sequence of bytes using the ISO-8859-1 charset, storing the result into a new + * byte array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#ISO_8859_1} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesIso8859_1(final String string) { + return getBytes(string, Charsets.ISO_8859_1); + } + + + /** + * Encodes the given string into a sequence of bytes using the named charset, storing the result into a new byte + * array. + *

+ * This method catches {@link UnsupportedEncodingException} and rethrows it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param string + * the String to encode, may be null + * @param charsetName + * The name of a required {@link Charset} + * @return encoded bytes, or null if the input string was null + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#getBytes(String) + */ + public static byte[] getBytesUnchecked( final String string, final String charsetName) { + if (string == null) { + return null; + } + try { + return string.getBytes(charsetName); + } catch (final UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Encodes the given string into a sequence of bytes using the US-ASCII charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#US_ASCII} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUsAscii(final String string) { + return getBytes(string, Charsets.US_ASCII); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16(final String string) { + return getBytes(string, Charsets.UTF_16); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16BE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16BE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Be(final String string) { + return getBytes(string, Charsets.UTF_16BE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-16LE charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16LE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf16Le(final String string) { + return getBytes(string, Charsets.UTF_16LE); + } + + /** + * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte + * array. + * + * @param string + * the String to encode, may be null + * @return encoded bytes, or null if the input string was null + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * @see Standard charsets + * @see #getBytesUnchecked(String, String) + */ + public static byte[] getBytesUtf8(final String string) { + return getBytes(string, Charsets.UTF_8); + } + + private static IllegalStateException newIllegalStateException( final String charsetName, + final UnsupportedEncodingException e) { + return new IllegalStateException(charsetName + ": " + e); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the given charset. + * + * @param bytes + * The bytes to be decoded into characters + * @param charset + * The {@link Charset} to encode the String + * @return A new String decoded from the specified array of bytes using the given charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + */ + private static String newString( final byte[] bytes, final Charset charset) { + return bytes == null ? null : new String(bytes, charset); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the given charset. + *

+ * This method catches {@link UnsupportedEncodingException} and re-throws it as {@link IllegalStateException}, which + * should never happen for a required charset name. Use this method when the encoding is required to be in the JRE. + *

+ * + * @param bytes + * The bytes to be decoded into characters, may be null + * @param charsetName + * The name of a required {@link Charset} + * @return A new String decoded from the specified array of bytes using the given charset, + * or null if the input byte array was null. + * @throws IllegalStateException + * Thrown when a {@link UnsupportedEncodingException} is caught, which should never happen for a + * required charset name. + * @see CharEncoding + * @see String#String(byte[], String) + */ + public static String newString( final byte[] bytes, final String charsetName) { + if (bytes == null) { + return null; + } + try { + return new String(bytes, charsetName); + } catch (final UnsupportedEncodingException e) { + throw StringUtils.newIllegalStateException(charsetName, e); + } + } + + /** + * Constructs a new String by decoding the specified array of bytes using the ISO-8859-1 charset. + * + * @param bytes + * The bytes to be decoded into characters, may be null + * @return A new String decoded from the specified array of bytes using the ISO-8859-1 charset, or + * null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#ISO_8859_1} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringIso8859_1( final byte[] bytes) { + return new String(bytes, Charsets.ISO_8859_1); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the US-ASCII charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the US-ASCII charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#US_ASCII} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUsAscii( final byte[] bytes) { + return new String(bytes, Charsets.US_ASCII); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16 charset + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16( final byte[] bytes) { + return new String(bytes, Charsets.UTF_16); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16BE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16BE charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16BE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16Be( final byte[] bytes) { + return new String(bytes, Charsets.UTF_16BE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-16LE charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-16LE charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_16LE} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf16Le( final byte[] bytes) { + return new String(bytes, Charsets.UTF_16LE); + } + + /** + * Constructs a new String by decoding the specified array of bytes using the UTF-8 charset. + * + * @param bytes + * The bytes to be decoded into characters + * @return A new String decoded from the specified array of bytes using the UTF-8 charset, + * or null if the input byte array was null. + * @throws NullPointerException + * Thrown if {@link Charsets#UTF_8} is not initialized, which should never happen since it is + * required by the Java platform specification. + * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + */ + public static String newStringUtf8( final byte[] bytes) { + return newString(bytes, Charsets.UTF_8); + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/glide/BaseGlideModule.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/glide/BaseGlideModule.java new file mode 100644 index 0000000000..b77e27d37f --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/glide/BaseGlideModule.java @@ -0,0 +1,38 @@ +package com.mogo.utils.glide; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.GlideBuilder; +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.load.Key; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.engine.cache.ExternalPreferredCacheDiskCacheFactory; +import com.bumptech.glide.load.engine.cache.LruResourceCache; +import com.bumptech.glide.load.engine.cache.MemoryCache; +import com.bumptech.glide.module.AppGlideModule; + +/** + * Created by congtaowang on 2018/12/17. + */ +@GlideModule +public class BaseGlideModule extends AppGlideModule { + + public static final int MEMORY_CACHE_SIZE = 5 * 1024 * 1024; + public static final int DISK_CACHE_SIZE = 50 * 1024 * 1024; + public static final String DISK_CACHE_NAME = "glide"; + + @Override + public void applyOptions( Context context, GlideBuilder builder ) { + super.applyOptions( context, builder ); + /** + * 更改缓存最总文件夹名称 + * + * 是在sdcard/Android/data/包名/cache/DISK_CACHE_NAME目录当中 + */ + builder.setMemoryCache( new LruResourceCache( MEMORY_CACHE_SIZE ) ); + builder.setDiskCache( new ExternalPreferredCacheDiskCacheFactory( context, DISK_CACHE_NAME, DISK_CACHE_SIZE ) ); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/LogLevel.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/LogLevel.java new file mode 100644 index 0000000000..1296d3a052 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/LogLevel.java @@ -0,0 +1,24 @@ + +package com.mogo.utils.logger; + +public enum LogLevel { + + OFF( Integer.MAX_VALUE), + + VERBOSE(1), + + DEBUG(2), + + INFO(3), + + WARN(4), + + ERROR(5); + + public final int level; + + private LogLevel(final int level) { + this.level = level; + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Logger.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Logger.java new file mode 100644 index 0000000000..152779902b --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Logger.java @@ -0,0 +1,57 @@ + +package com.mogo.utils.logger; + +public final class Logger { + private static final Printer sPrinter = new LoggerPrinter(); + + private Logger() { + } + + public static Settings init() { + return sPrinter.init(LogLevel.DEBUG); + } + + public static Settings init(LogLevel logLevel) { + return sPrinter.init(logLevel); + } + + public static void d( String tag, String message, Object... args) { + if(isLoggable(LogLevel.DEBUG)) sPrinter.d(tag, message, args); + } + + public static void e( String tag, String message, Object... args) { + if(isLoggable(LogLevel.ERROR)) sPrinter.e(tag, null, message, args); + } + + public static void e( String tag, Throwable throwable, String message, Object... args) { + if(isLoggable(LogLevel.ERROR)) sPrinter.e(tag, throwable, message, args); + } + + public static void i( String tag, String message, Object... args) { + if(isLoggable(LogLevel.INFO)) sPrinter.i(tag, message, args); + } + + public static void v( String tag, String message, Object... args) { + if(isLoggable(LogLevel.VERBOSE)) sPrinter.v(tag, message, args); + } + + public static void w( String tag, String message, Object... args) { + if(isLoggable(LogLevel.WARN)) sPrinter.w(tag, message, args); + } + + public static void easyLog( String tag, String message) { + if(isLoggable(LogLevel.DEBUG)) sPrinter.d(tag, message); + } + + public static void json( String tag, String json) { + if(isLoggable(LogLevel.DEBUG)) sPrinter.json(tag, json); + } + + public static void xml( String tag, String xml) { + if(isLoggable(LogLevel.DEBUG)) sPrinter.xml(tag, xml); + } + + private static boolean isLoggable(LogLevel logLevel){ + return sPrinter.getSettings().getLogLevel().level <= logLevel.level; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/LoggerPrinter.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/LoggerPrinter.java new file mode 100644 index 0000000000..d4b65ba28c --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/LoggerPrinter.java @@ -0,0 +1,261 @@ + +package com.mogo.utils.logger; + +import android.text.TextUtils; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.StringReader; +import java.io.StringWriter; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +final class LoggerPrinter implements Printer { + private static final String TAG = "LoggerPrinter"; + + private static final int CHUNK_SIZE = 4000; + private static final int JSON_INDENT = 4; + private static final int MIN_STACK_OFFSET = 3; + private static final char TOP_LEFT_CORNER = '╔'; + private static final char BOTTOM_LEFT_CORNER = '╚'; + private static final char MIDDLE_CORNER = '╟'; + private static final char HORIZONTAL_DOUBLE_LINE = '║'; + private static final String DOUBLE_DIVIDER = "════════════════════════════════════════════"; + private static final String SINGLE_DIVIDER = "────────────────────────────────────────────"; + private static final String TOP_BORDER = "╔════════════════════════════════════════════════════════════════════════════════════════"; + private static final String BOTTOM_BORDER = "╚════════════════════════════════════════════════════════════════════════════════════════"; + private static final String MIDDLE_BORDER = "╟────────────────────────────────────────────────────────────────────────────────────────"; + + private final Settings mSettings = new Settings(); + + LoggerPrinter() { + } + + public Settings init(LogLevel logLevel) { + return mSettings.setLogLevel(logLevel); + } + + public Settings getSettings() { + return mSettings; + } + + public void d( String tag, String message, Object... args) { + this.log(tag, LogLevel.DEBUG, message, args); + } + + public void e( String tag, String message, Object... args) { + this.e(tag, null, message, args); + } + + public void e( String tag, Throwable throwable, String message, Object... args) { + if (throwable != null && message != null) { + message = message + " : " + throwable.toString(); + } + + if (throwable != null && message == null) { + message = throwable.toString(); + } + + if (message == null) { + message = "No message/exception is set"; + } + + this.log(tag, LogLevel.ERROR, message, args); + } + + public void w( String tag, String message, Object... args) { + this.log(tag, LogLevel.WARN, message, args); + } + + public void i( String tag, String message, Object... args) { + this.log(tag, LogLevel.INFO, message, args); + } + + public void v( String tag, String message, Object... args) { + this.log(tag, LogLevel.VERBOSE, message, args); + } + + public void json( String tag, String json) { + if ( TextUtils.isEmpty(json)) { + this.d(tag, "Empty/Null json content"); + } else { + try { + String message; + if (json.startsWith("{")) { + JSONObject e1 = new JSONObject(json); + message = e1.toString(4); + this.d(tag, message); + return; + } + + if (json.startsWith("[")) { + JSONArray e = new JSONArray(json); + message = e.toString(4); + this.d(tag, message); + } + } catch ( JSONException var4) { + this.e(tag, var4.getCause().getMessage() + "\n" + json); + } + + } + } + + public void xml( String tag, String xml) { + if ( TextUtils.isEmpty(xml)) { + this.d(tag, "Empty/Null xml content"); + } else { + try { + StreamSource e = new StreamSource(new StringReader(xml)); + StreamResult xmlOutput = new StreamResult(new StringWriter()); + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty("indent", "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.transform(e, xmlOutput); + this.d(tag, xmlOutput.getWriter().toString().replaceFirst(">", ">\n")); + } catch ( TransformerException var5) { + this.e(tag, var5.getCause().getMessage() + "\n" + xml); + } + + } + } + + public void normalLog( String tag, String message) { + if (!TextUtils.isEmpty(message)) { + this.logChunk(LogLevel.DEBUG, tag, message); + } + } + + private synchronized void log( String tag, LogLevel logLevel, String msg, Object... args) { + String message = this.createMessage(msg, args); + int methodCount = this.getMethodCount(); + this.logTopBorder(logLevel, tag); + this.logHeaderContent(logLevel, tag, methodCount); + byte[] bytes = message.getBytes(); + int length = bytes.length; + if (length <= 4000) { + if (methodCount > 0) { + this.logDivider(logLevel, tag); + } + + this.logContent(logLevel, tag, message); + this.logBottomBorder(logLevel, tag); + } else { + if (methodCount > 0) { + this.logDivider(logLevel, tag); + } + + for (int i = 0; i < length; i += 4000) { + int count = Math.min(length - i, 4000); + this.logContent(logLevel, tag, new String(bytes, i, count)); + } + + this.logBottomBorder(logLevel, tag); + } + } + + private void logTopBorder(LogLevel logLevel, String tag) { + this.logChunk(logLevel, tag, "╔════════════════════════════════════════════════════════════════════════════════════════"); + } + + private void logHeaderContent( LogLevel logLevel, String tag, int methodCount) { + StackTraceElement[] trace = Thread.currentThread().getStackTrace(); + if (mSettings.isShowThreadInfo()) { + this.logChunk(logLevel, tag, "║ Thread: " + Thread.currentThread().getName()); + this.logDivider(logLevel, tag); + } + + String level = ""; + int stackOffset = this.getStackOffset(trace) + mSettings.getMethodOffset(); + if (methodCount + stackOffset > trace.length) { + methodCount = trace.length - stackOffset - 1; + } + + for (int i = methodCount; i > 0; --i) { + int stackIndex = i + stackOffset; + if (stackIndex < trace.length) { + StringBuilder builder = new StringBuilder(); + builder.append("║ ").append(level).append(this.getSimpleClassName(trace[stackIndex].getClassName())).append(".").append(trace[stackIndex].getMethodName()).append(" ").append(" (").append(trace[stackIndex].getFileName()).append(":").append(trace[stackIndex].getLineNumber()).append(")"); + level = level + " "; + this.logChunk(logLevel, tag, builder.toString()); + } + } + + } + + private void logBottomBorder(LogLevel logLevel, String tag) { + this.logChunk(logLevel, tag, "╚════════════════════════════════════════════════════════════════════════════════════════"); + } + + private void logDivider(LogLevel logLevel, String tag) { + this.logChunk(logLevel, tag, "╟────────────────────────────────────────────────────────────────────────────────────────"); + } + + private void logContent( LogLevel logLevel, String tag, String chunk) { + String[] lines = chunk.split( System.getProperty("line.separator")); + + for ( String line : lines) { + this.logChunk(logLevel, tag, "║ " + line); + } + } + + private void logChunk( LogLevel logLevel, String tag, String chunk) { + String finalTag = this.checkTag(tag); + switch (logLevel) { + case VERBOSE: + Log.v(finalTag, chunk); + break; + case INFO: + Log.i(finalTag, chunk); + break; + case DEBUG: + Log.d(finalTag, chunk); + break; + case WARN: + Log.w(finalTag, chunk); + break; + case ERROR: + Log.e(finalTag, chunk); + break; + case OFF: + break; + } + + } + + private String getSimpleClassName( String name) { + int lastIndex = name.lastIndexOf("."); + return name.substring(lastIndex + 1); + } + + private String checkTag( String tag) { + return TextUtils.isEmpty(tag) ? TAG : tag; + } + + private String createMessage( String message, Object... args) { + return (args == null || args.length == 0) ? message : String.format(message, args); + } + + private int getMethodCount() { + return mSettings.getMethodCount(); + } + + private int getStackOffset( StackTraceElement[] trace) { + for (int i = 3; i < trace.length; ++i) { + StackTraceElement e = trace[i]; + String name = e.getClassName(); + if (!name.equals(LoggerPrinter.class.getName()) && !name.equals(Logger.class.getName())) { + --i; + return i; + } + } + + return -1; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Printer.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Printer.java new file mode 100644 index 0000000000..57def529a6 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Printer.java @@ -0,0 +1,26 @@ + +package com.mogo.utils.logger; + +public interface Printer { + Settings init( LogLevel logLevel ); + + Settings getSettings(); + + void d( String tag, String message, Object... args ); + + void e( String tag, String message, Object... args ); + + void e( String tag, Throwable throwable, String message, Object... args ); + + void w( String tag, String message, Object... args ); + + void i( String tag, String message, Object... args ); + + void v( String tag, String message, Object... args ); + + void json( String tag, String json ); + + void xml( String tag, String xml ); + + void normalLog( String tag, String message ); +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Settings.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Settings.java new file mode 100644 index 0000000000..af6ec2213b --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/logger/Settings.java @@ -0,0 +1,52 @@ + +package com.mogo.utils.logger; + +public final class Settings { + private int methodCount = 2; + private boolean showThreadInfo = true; + private int methodOffset = 0; + private LogLevel logLevel = LogLevel.DEBUG; + + public Settings() { + } + + public Settings hideThreadInfo() { + this.showThreadInfo = false; + return this; + } + + public Settings setMethodCount(int methodCount) { + if(methodCount < 0) { + methodCount = 0; + } + + this.methodCount = methodCount; + return this; + } + + public Settings setLogLevel(LogLevel logLevel) { + this.logLevel = logLevel; + return this; + } + + public Settings setMethodOffset(int offset) { + this.methodOffset = offset; + return this; + } + + public int getMethodCount() { + return this.methodCount; + } + + public boolean isShowThreadInfo() { + return this.showThreadInfo; + } + + public LogLevel getLogLevel() { + return this.logLevel; + } + + public int getMethodOffset() { + return this.methodOffset; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/BaseParams.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/BaseParams.java new file mode 100644 index 0000000000..349fce9496 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/BaseParams.java @@ -0,0 +1,179 @@ +package com.mogo.utils.network; + +import android.text.TextUtils; + +import com.mogo.utils.logger.Logger; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +public abstract class BaseParams { + + private static final String TAG = "BaseParams"; + + protected ConcurrentHashMap< String, Object > urlParams; + + protected BaseParams() { + this.init(); + } + + public Object get( String key ) { + return this.urlParams != null && key != null ? this.urlParams.get( key ) : ""; + } + + protected BaseParams( Map< String, Object > source ) { + this.init(); + Iterator iterator = source.entrySet().iterator(); + + while ( iterator.hasNext() ) { + Map.Entry entry = ( Map.Entry ) iterator.next(); + this.put( ( String ) entry.getKey(), entry.getValue() ); + } + + } + + protected BaseParams( String key, Object value ) { + this.init(); + this.put( key, value ); + } + + protected BaseParams( Object... keysAndValues ) { + this.init(); + int len = keysAndValues.length; + if ( len % 2 != 0 ) { + throw new IllegalArgumentException( "Supplied arguments must be even" ); + } else { + for ( int i = 0; i < len; i += 2 ) { + String key = String.valueOf( keysAndValues[i] ); + Object value = keysAndValues[i + 1]; + this.put( key, value ); + } + + } + } + + private boolean checkValue( final Object value ) { + if ( value instanceof CharSequence ) { + return !TextUtils.isEmpty( ( CharSequence ) value ); + } + + if ( value == null || value.getClass() == null ) { + return false; + } + + final Class< ? > clazz = value.getClass(); + return clazz.isPrimitive() || + clazz.isAssignableFrom( Boolean.class ) || + clazz.isAssignableFrom( Character.class ) || + clazz.isAssignableFrom( Byte.class ) || + clazz.isAssignableFrom( Short.class ) || + clazz.isAssignableFrom( Integer.class ) || + clazz.isAssignableFrom( Long.class ) || + clazz.isAssignableFrom( Float.class ) || + clazz.isAssignableFrom( Double.class ); + } + + private boolean checkKey( String key ) { + return !TextUtils.isEmpty( key ); + } + + public BaseParams put( String key, Object value ) { + if ( checkKey( key ) && checkValue( value ) ) { + urlParams.put( key, value ); + } else { + Logger.e( TAG, "parameter key is illegal or parameter value is illegal" ); + } + return this; + } + + public Object remove( String key ) { + return this.urlParams.remove( key ); + } + + public String toString() { + StringBuilder result = new StringBuilder(); + Iterator< Map.Entry< String, Object > > iterator = this.urlParams.entrySet().iterator(); + while ( iterator.hasNext() ) { + Map.Entry< String, Object > entry = iterator.next(); + if ( result.length() > 0 ) { + result.append( "&" ); + } + + result.append( entry.getKey() ); + result.append( "=" ); + result.append( entry.getValue() ); + } + return result.toString(); + } + + private void init() { + this.urlParams = new ConcurrentHashMap< String, Object >(); + } + + public List< BasicNameValuePair > getParamsList() { + LinkedList< BasicNameValuePair > pairs = new LinkedList< BasicNameValuePair >(); + Iterator< Map.Entry< String, Object > > iterator = this.urlParams.entrySet().iterator(); + + while ( iterator.hasNext() ) { + Map.Entry< String, Object > entry = iterator.next(); + pairs.add( new BasicNameValuePair( entry.getKey(), entry.getValue() ) ); + } + + return pairs; + } + + public Map< String, Object > getOriginMap() { + return urlParams; + } + + public String getParamString() { + StringBuilder result = new StringBuilder(); + Iterator< BasicNameValuePair > iterator = getParamsList().iterator(); + + while ( iterator.hasNext() ) { + BasicNameValuePair parameter = iterator.next(); + String key = parameter.getName(); + Object value = parameter.getValue(); + if ( result.length() > 0 ) { + result.append( "&" ); + } + + result.append( key ); + result.append( "=" ); + result.append( value ); + } + + return result.toString(); + } + + public static class BasicNameValuePair { + private String name; + private Object value; + + public BasicNameValuePair( String name, Object value ) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } + + public Object getValue() { + return value; + } + + public void setValue( Object value ) { + this.value = value; + } + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerNotAliveException.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerNotAliveException.java new file mode 100644 index 0000000000..57d61834c7 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerNotAliveException.java @@ -0,0 +1,10 @@ +package com.mogo.utils.network; + + + +public class CallerNotAliveException extends Exception { + + public CallerNotAliveException( String message){ + super(message); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerRestrictTo.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerRestrictTo.java new file mode 100644 index 0000000000..1818e103ae --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerRestrictTo.java @@ -0,0 +1,13 @@ +package com.mogo.utils.network; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Retention( RetentionPolicy.SOURCE) +@Target( ElementType.ANNOTATION_TYPE) +public @interface CallerRestrictTo { + Class[] value(); +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerType.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerType.java new file mode 100644 index 0000000000..facb8aea86 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/CallerType.java @@ -0,0 +1,29 @@ +package com.mogo.utils.network; + +import android.app.Activity; +import android.app.Dialog; +import android.app.Fragment; +import android.content.ContextWrapper; +import android.view.View; +import android.widget.PopupWindow; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Retention( RetentionPolicy.SOURCE ) +@Target( ElementType.PARAMETER ) +@CallerRestrictTo( { + Activity.class, + Fragment.class, + androidx.fragment.app.Fragment.class, + View.class, + Dialog.class, + PopupWindow.class, + ContextWrapper.class, + Object.class +} ) +public @interface CallerType { +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/HttpParams.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/HttpParams.java new file mode 100644 index 0000000000..0937b36c8c --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/HttpParams.java @@ -0,0 +1,70 @@ +package com.mogo.utils.network; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class HttpParams extends BaseParams { + public HttpParams() { + } + + public HttpParams( Map< String, Object > source) { + super(source); + } + + public HttpParams( Object... keysAndValues) { + super(keysAndValues); + } + + public HttpParams( String key, Object value) { + super(key, value); + } + + public List getParamList() { + List list = getParamsList(); + Collections.sort(list, new KVPairComparator()); + return list; + } + + public String getSortedParamsString() { + List list = getParamList(); + StringBuilder sb = new StringBuilder(NetConfig.instance().getSignaturePrefix()); + for (BasicNameValuePair basicNameValuePair : list) { + sb.append(basicNameValuePair.getName()); + sb.append(basicNameValuePair.getValue()); + } + return sb.toString(); + } + + public Map< String, Object > getParamsMap() { + List list = getParamList(); + HashMap< String, Object > map = new HashMap< String, Object >(); + for (BasicNameValuePair basicNameValuePair : list) { + map.put(basicNameValuePair.getName(), basicNameValuePair.getValue()); + } + return map; + } + + public String getSortedUrlParamsString() { + List list = getParamList(); + StringBuilder sb = new StringBuilder(); + for (BasicNameValuePair basicNameValuePair : list) { + String key = basicNameValuePair.getName(); + Object value = basicNameValuePair.getValue(); + if (sb.length() > 0) + sb.append("&"); + sb.append(key); + sb.append("="); + sb.append(value); + } + return sb.toString(); + } + + private class KVPairComparator implements Comparator { + public int compare(BasicNameValuePair pairA, BasicNameValuePair pairB) { + return pairA.getName().compareTo(pairB.getName()); + } + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetConfig.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetConfig.java new file mode 100644 index 0000000000..a0fe0af85f --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetConfig.java @@ -0,0 +1,160 @@ +package com.mogo.utils.network; + +import android.app.Application; +import android.content.Context; + +import androidx.collection.ArraySet; + +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; + +import okhttp3.Interceptor; +import okhttp3.internal.tls.OkHostnameVerifier; + +/** + *

+ * This is a configuration class provided for clients.For better extension and customization,client can set customized parameters of network + * based on their own application. However you should set these parameters before you get the global instance + * of {@link okhttp3.OkHttpClient} by {@link OkHttpFactory} for the first time.

+ *

+ * Some of the parameters have default values, so there is not a must to use this class.

+ */ + +public final class NetConfig { + private long readTimeout = NetConstants.READ_TIMEOUT; + private long writeTimeout = NetConstants.WRITE_TIMEOUT; + private long connectTimeout = NetConstants.CONNECT_TIMEOUT; + + private final Set< Interceptor > interceptors = new ArraySet<>(); + private final Set< Interceptor > networkInterceptors = new ArraySet<>(); + private final HostnameVerifier allowAllHostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify( String hostname, SSLSession session ) { + return true; + } + }; + + private String signaturePrefix = "com.foundation.network"; + private HostnameVerifier hostnameVerifier = OkHostnameVerifier.INSTANCE; + private Map< String, Object > publicParams; + private boolean isLoggable; + private Context appContext; + + private SSLContext sslContext; + + private NetConfig() { + } + + private static final class SingletonHolder { + private static final NetConfig INSTANCE = new NetConfig(); + } + + public static NetConfig instance() { + return SingletonHolder.INSTANCE; + } + + public synchronized long getReadTimeout() { + return readTimeout; + } + + public synchronized NetConfig setReadTimeout( int readTimeout ) { + this.readTimeout = readTimeout; + return this; + } + + public synchronized long getWriteTimeout() { + return writeTimeout; + } + + public synchronized NetConfig setWriteTimeout( int writeTimeout ) { + this.writeTimeout = writeTimeout; + return this; + } + + public synchronized long getConnectTimeout() { + return connectTimeout; + } + + public synchronized NetConfig setConnectTimeout( int connectTimeout ) { + this.connectTimeout = connectTimeout; + return this; + } + + public synchronized HostnameVerifier getHostnameVerifier() { + return hostnameVerifier; + } + + public synchronized NetConfig setHostnameVerifier( HostnameVerifier hostnameVerifier ) { + this.hostnameVerifier = hostnameVerifier; + return this; + } + + public synchronized NetConfig setSignaturePrefix( String prefix ) { + this.signaturePrefix = prefix; + return this; + } + + public synchronized String getSignaturePrefix() { + return signaturePrefix; + } + + public synchronized Set< Interceptor > getInterceptors() { + return interceptors; + } + + public synchronized NetConfig addInterceptor( Interceptor interceptor ) { + interceptors.add( interceptor ); + return this; + } + + public synchronized Set< Interceptor > getNetworkInterceptors() { + return networkInterceptors; + } + + public synchronized NetConfig addNetworkInterceptor( Interceptor networkInterceptor ) { + networkInterceptors.add( networkInterceptor ); + return this; + } + + public synchronized NetConfig setPublicParams( Map< String, Object > publicParams ) { + this.publicParams = publicParams; + return this; + } + + public synchronized Map< String, Object > getPublicParams() { + return publicParams; + } + + public synchronized boolean isLoggable() { + return isLoggable; + } + + public synchronized void setLoggable( boolean loggable ) { + isLoggable = loggable; + } + + public synchronized NetConfig setAppContext( Context appContext ) { + if ( appContext instanceof Application ) { + this.appContext = appContext; + } else { + this.appContext = appContext.getApplicationContext(); + } + return this; + } + + public synchronized Context getAppContext() { + return appContext; + } + + public synchronized SSLContext getSslContext() { + return sslContext; + } + + public synchronized void setSslContext( SSLContext sslContext ) { + this.sslContext = sslContext; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetConstants.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetConstants.java new file mode 100644 index 0000000000..2ef1cd3a02 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetConstants.java @@ -0,0 +1,13 @@ +package com.mogo.utils.network; + + +public class NetConstants { + /** 无数据 */ + public final static int NO_DATA = -800; + /** 数据返回正常 */ + public final static int OK = 0; + + public static final long READ_TIMEOUT = 20_000L; + public static final long WRITE_TIMEOUT = 20_000L; + public static final long CONNECT_TIMEOUT = 15_000L; +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetworkMonitorInterceptor.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetworkMonitorInterceptor.java new file mode 100644 index 0000000000..a014b352b4 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetworkMonitorInterceptor.java @@ -0,0 +1,24 @@ +package com.mogo.utils.network; + +import android.content.Context; + +import com.mogo.utils.CheckUtils; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.Response; + + +final class NetworkMonitorInterceptor implements Interceptor { + private static final String TAG = "NetworkMonitorInterceptor"; + + @Override + public Response intercept(Chain chain) throws IOException { + final Context appContext = NetConfig.instance().getAppContext(); + if(appContext != null && !CheckUtils.isNetworkConnected(appContext)){ + throw new NetworkUnavailableException("Network is unavailable"); + } + return chain.proceed(chain.request()); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetworkUnavailableException.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetworkUnavailableException.java new file mode 100644 index 0000000000..425d14c6e4 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/NetworkUnavailableException.java @@ -0,0 +1,18 @@ +package com.mogo.utils.network; + +import java.io.IOException; + +import okhttp3.Interceptor; + +/** + * An exception occurs whenever network is not reachable, it's threw by {@link NetworkMonitorInterceptor} + * during {@link NetworkMonitorInterceptor#intercept(Interceptor.Chain)} method. + * + */ + +public class NetworkUnavailableException extends IOException { + + public NetworkUnavailableException( String message) { + super(message); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/OkHttpFactory.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/OkHttpFactory.java new file mode 100644 index 0000000000..02e05d5592 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/OkHttpFactory.java @@ -0,0 +1,55 @@ +package com.mogo.utils.network; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import okhttp3.Interceptor; +import okhttp3.OkHttpClient; + +/** + *

+ * Before getting an global instance of {@link OkHttpClient}, you can use {@link NetConfig} to set some + * parameters of network. Note that network configuration must be first set before you get an instance of {@link OkHttpClient}, + * for the first time, otherwise configuration is invalid

+ * + */ +public final class OkHttpFactory { + + private static volatile OkHttpClient sInstance; + + private OkHttpFactory() { + } + + public static OkHttpClient getInstance() { + if (sInstance == null) { + synchronized (OkHttpClient.class) { + if (sInstance == null) { + final OkHttpClient.Builder builder = new OkHttpClient.Builder(); + builder.addInterceptor(new NetworkMonitorInterceptor()); + builder.addInterceptor(new RequestLogInterceptor()); + builder.addInterceptor(new ResponseLogInterceptor()); + builder.hostnameVerifier(NetConfig.instance().getHostnameVerifier()); + if ( NetConfig.instance().getSslContext() != null ) { + builder.sslSocketFactory( NetConfig.instance().getSslContext().getSocketFactory()); + } + builder.connectTimeout(NetConfig.instance().getConnectTimeout(), TimeUnit.MILLISECONDS); + builder.readTimeout(NetConfig.instance().getReadTimeout(), TimeUnit.MILLISECONDS); + builder.writeTimeout(NetConfig.instance().getWriteTimeout(), TimeUnit.MILLISECONDS); + final Set interceptors = NetConfig.instance().getInterceptors(); + for(Interceptor interceptor : interceptors){ + builder.addInterceptor(interceptor); + } + + final Set networkInterceptors = NetConfig.instance().getNetworkInterceptors(); + for(Interceptor networkInterceptor : networkInterceptors){ + builder.addNetworkInterceptor(networkInterceptor); + } + + sInstance = builder.build(); + } + } + } + + return sInstance; + } +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RequestLogInterceptor.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RequestLogInterceptor.java new file mode 100644 index 0000000000..5af287c703 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RequestLogInterceptor.java @@ -0,0 +1,46 @@ +package com.mogo.utils.network; + +import com.mogo.utils.logger.Logger; + +import java.io.IOException; + +import okhttp3.Interceptor; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; + + +final class RequestLogInterceptor implements Interceptor { + private static final String TAG = "RequestLogInterceptor"; + + @Override + public Response intercept(Chain chain) throws IOException { + Request request = chain.request(); + RequestBody requestBody = request.body(); + boolean hasRequestBody = requestBody != null; + + String protocol = Protocol.HTTP_1_1.toString(); + if(chain.connection() != null && chain.connection().protocol() != null){ + protocol = chain.connection().protocol().toString(); + } + + StringBuilder logMsg = new StringBuilder(); + logMsg.append("--> "); + logMsg.append(protocol).append(", "); + logMsg.append(request.method()).append(", "); + logMsg.append("Request Headers: ").append(request.headers()).append("\r\n"); + logMsg.append(request.url()).append("\r\n"); + if(hasRequestBody){ + logMsg.append("Content-Type: ").append(requestBody.contentType()).append(", "); + logMsg.append("Content-Length: ").append(requestBody.contentLength()); + } + logMsg.append(" <-- end http request"); + + if(NetConfig.instance().isLoggable()){ + Logger.d(TAG, logMsg.toString()); + } + + return chain.proceed(request); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RequestOptions.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RequestOptions.java new file mode 100644 index 0000000000..d0bae21535 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RequestOptions.java @@ -0,0 +1,127 @@ +package com.mogo.utils.network; + +import android.content.Context; +import android.view.KeyEvent; + + +import com.mogo.utils.network.utils.Util; + +import java.util.Collections; +import java.util.Map; + + +public class RequestOptions { + private static final String DEFAULT_LOADING_MESSAGE = "正在加载中..."; + + private Object caller; + private Context context; + private Map< String, Object > parameter; + private CharSequence loadingMessage; + private boolean loading; + private boolean cancelable; + private boolean cancelableOnTouchOutside; + + /** + * Private constructor with some default initialization. + */ + private RequestOptions( @CallerType Object caller ) { + this.caller = caller; + this.context = Util.getContext( caller ); + this.parameter = Collections.emptyMap(); + this.loadingMessage = DEFAULT_LOADING_MESSAGE; + this.loading = false; + this.cancelable = true; + this.cancelableOnTouchOutside = false; + } + + /** + * Factory method to create an instance of {@link RequestOptions} + * + *

+ * Request caller can be any type of object. HOWEVER, if you want to show loading message + * when request is ongoing or if you want to monitor caller's lifecycle in process of requesting + * in order to avoid unexpected async exceptions, please use one of {@link android.app.Activity}, + * {@link android.app.Fragment},{@link androidx.fragment.app.Fragment},{@link android.view.View}, + * {@link android.app.Dialog} and {@link android.widget.PopupWindow} as caller

+ * + *

+ * Caller can be other types if loading message is not intended to be shown.

+ * + *

+ * It's strongly suggested that caller should have an context for better control of caller's lifecycle.

+ */ + public static RequestOptions create( Object caller ) { + return new RequestOptions( caller ); + } + + /** + * Mutator for request parameters + */ + public RequestOptions parameter( Map< String, Object > parameter ) { + this.parameter = parameter; + return this; + } + + /** + * Mutator for indicating whether loading message should be displayed while request is ongoing + */ + public RequestOptions loading( boolean loading ) { + this.loading = loading; + return this; + } + + /** + * Mutator for loading message + */ + public RequestOptions loadingMessage( CharSequence loadingMessage ) { + this.loading = true; + this.loadingMessage = loadingMessage; + return this; + } + + /** + * Mutator for indicating that loading dialog is cancelable with the {@link KeyEvent#KEYCODE_BACK BACK} key. + * Default value is true. + */ + public RequestOptions cancelable( boolean cancelable ) { + this.cancelable = cancelable; + return this; + } + + /** + * Mutator for indicating that loading dialog is cancelable with touching outside of the dialog. + * Default value is false. + */ + public RequestOptions cancelableOnTouchOutside( boolean cancelableOnTouchOutside ) { + this.cancelableOnTouchOutside = cancelableOnTouchOutside; + return this; + } + + public Object getCaller() { + return caller; + } + + public Context getContext() { + return context; + } + + public Map< String, Object > getParameter() { + return parameter; + } + + public CharSequence getLoadingMessage() { + return loadingMessage; + } + + public boolean isLoading() { + return loading; + } + + public boolean isCancelable() { + return cancelable; + } + + public boolean isCancelableOnTouchOutside() { + return cancelableOnTouchOutside; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ResponseLogInterceptor.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ResponseLogInterceptor.java new file mode 100644 index 0000000000..0199ebc717 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ResponseLogInterceptor.java @@ -0,0 +1,59 @@ +package com.mogo.utils.network; + + +import com.mogo.utils.logger.Logger; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.Response; +import okhttp3.ResponseBody; + + +final class ResponseLogInterceptor implements Interceptor { + private static final String TAG = "ResponseLogInterceptor"; + + @Override + public Response intercept( Chain chain ) throws IOException { + long startTime = System.nanoTime(); + Response response = chain.proceed( chain.request() ); + long endTime = TimeUnit.NANOSECONDS.toMillis( System.nanoTime() - startTime ); + + ResponseBody responseBody = response.body(); + String responseContent = null; + String bodySize = null; + MediaType contentType = null; + boolean consumedResponse = false; + + StringBuilder logMsg = new StringBuilder(); + + if ( responseBody != null ) { + long contentLength = responseBody.contentLength(); + bodySize = contentLength != -1L ? contentLength + "-byte" : "unknown-length"; + contentType = responseBody.contentType(); + responseContent = responseBody.string(); + consumedResponse = true; + } + + logMsg.append( "--> " ); + logMsg.append( response.code() ).append( " " ); + logMsg.append( response.message() ).append( " " ); + logMsg.append( response.protocol() ).append( " " ); + logMsg.append( response.request().url() ).append( "\r\n" ); + logMsg.append( "Response Content: " ).append( responseContent ).append( "\r\n" ); + logMsg.append( "Content-Type: " ).append( contentType ).append( ", " ); + logMsg.append( "Content-Length: " ).append( bodySize ).append( ", " ); + logMsg.append( " (" ).append( endTime ).append( "ms)" ); + logMsg.append( " <-- end http response" ); + + if ( NetConfig.instance().isLoggable() ) { + Logger.d( TAG, logMsg.toString() ); + } + + return consumedResponse ? + response.newBuilder().body( ResponseBody.create( contentType, responseContent ) ).build() : + response; + } +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RetrofitFactory.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RetrofitFactory.java new file mode 100644 index 0000000000..f26c1b1d74 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/RetrofitFactory.java @@ -0,0 +1,34 @@ +package com.mogo.utils.network; + + +import androidx.collection.ArrayMap; + +import java.util.Map; + +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import retrofit2.converter.gson.GsonConverterFactory; + +public final class RetrofitFactory { + + private static final Map< String, Retrofit> sRpcServiceMap = new ArrayMap< String,Retrofit>(); + + private RetrofitFactory(){} + + public static synchronized Retrofit getInstance( String baseUrl) { + Retrofit target = sRpcServiceMap.get(baseUrl); + + if(target == null){ + target = new Retrofit.Builder(). + client(OkHttpFactory.getInstance()). + baseUrl(baseUrl). + addCallAdapterFactory(RxJavaCallAdapterFactory.create()). + addConverterFactory(GsonConverterFactory.create()). + build(); + sRpcServiceMap.put(baseUrl,target); + } + + return target; + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ServerParam.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ServerParam.java new file mode 100644 index 0000000000..87a7d768d1 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ServerParam.java @@ -0,0 +1,5 @@ +package com.mogo.utils.network; + +public class ServerParam { + public static final String SIGNATURE = "sig"; +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/SubscriberEx.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/SubscriberEx.java new file mode 100644 index 0000000000..158c431852 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/SubscriberEx.java @@ -0,0 +1,95 @@ +package com.mogo.utils.network; + + +import androidx.annotation.CallSuper; + + +import com.mogo.utils.network.ui.ProgressDialog; +import com.mogo.utils.network.utils.Util; + +import rx.Subscriber; + + +/** + *

+ * Extension of {@link Subscriber}. For better extension and customization, client can extend this + * class to override the default behaviours such as loading appearance on each lifecycle method + * of network callback.

+ */ + +public abstract class SubscriberEx< T > extends Subscriber< T > { + protected final RequestOptions mRequestOptions; + private ProgressDialog mProgressDialog; + + public SubscriberEx( RequestOptions requestOptions ) { + if ( requestOptions == null ) { + throw new IllegalArgumentException( "RequestOptions cannot be null" ); + } + this.mRequestOptions = requestOptions; + if ( mRequestOptions.isLoading() ) { + this.mProgressDialog = new ProgressDialog(); + } + } + + /** + * This method must be called if you want to use the default loading dialog in case of override. + * Otherwise you can ignore it. + */ + @Override + @CallSuper + public void onStart() { + super.onStart(); + if ( !Util.checkAlive( mRequestOptions.getCaller() ) ) { + unsubscribe(); + return; + } + if ( mRequestOptions.isLoading() && mProgressDialog != null ) { + mProgressDialog.showLoadingDialog( mRequestOptions.getContext(), mRequestOptions.getLoadingMessage(), mRequestOptions.isCancelable(), mRequestOptions.isCancelableOnTouchOutside() ); + } + } + + /** + * This method must be called if you want to use the default loading dialog in case of override. + * Otherwise you can ignore it. + */ + private void onFinish() { + if ( mRequestOptions.isLoading() && mProgressDialog != null ) { + mProgressDialog.removeLoadingDialog(); + mProgressDialog = null; + } + if ( !Util.checkAlive( mRequestOptions.getCaller() ) ) { + unsubscribe(); + } + } + + /** + * This method must be called if you want to use the default loading dialog in case of override. + * Otherwise you can ignore it. + */ + @Override + @CallSuper + public void onCompleted() { + onFinish(); + } + + /** + * This method must be called if you want to use the default loading dialog in case of override. + * Otherwise you can ignore it. + */ + @Override + @CallSuper + public void onError( Throwable e ) { + onFinish(); + } + + /** + * This method must be override if you care about the result of request. + * + * @param o The result of network request + */ + @Override + @CallSuper + public void onNext( T o ) { + } + +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ui/ProgressDialog.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ui/ProgressDialog.java new file mode 100644 index 0000000000..ee5b507132 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ui/ProgressDialog.java @@ -0,0 +1,66 @@ +package com.mogo.utils.network.ui; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + +import com.mogo.utils.logger.Logger; + + +public class ProgressDialog { + + private static final String TAG = "ProgressDialog"; + + public static final String LOADING_DIALOG_TAG = "loading_dialog_tag"; + + private ProgressDialogFragment mProgressDialogFragment; + private FragmentManager mFragmentManager; + + public void showLoadingDialog( @NonNull Context context, CharSequence loadingMassage) { + showLoadingDialog(context, loadingMassage, true); + } + + public void showLoadingDialog( @NonNull Context context, CharSequence loadingMassage, boolean cancelable) { + showLoadingDialog(context, loadingMassage, cancelable, false); + } + + public void showLoadingDialog( Context context, CharSequence loadingMassage, boolean cancelable, boolean cancelableOnTouchOutSide) { + if (context != null && context instanceof FragmentActivity ) + mFragmentManager = ((FragmentActivity) context).getSupportFragmentManager(); + + if (mFragmentManager == null) + return; + + mProgressDialogFragment = new ProgressDialogFragment(); + mProgressDialogFragment.setContent(loadingMassage, cancelable, cancelableOnTouchOutSide); + + try { + FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); + fragmentTransaction.add( mProgressDialogFragment, LOADING_DIALOG_TAG ); + fragmentTransaction.add(mProgressDialogFragment, LOADING_DIALOG_TAG); + fragmentTransaction.commitAllowingStateLoss(); + } catch ( IllegalStateException e) { + Logger.e(TAG, e, e.getMessage()); + mFragmentManager = null; + mProgressDialogFragment = null; + } + } + + public void removeLoadingDialog() { + if (mProgressDialogFragment != null) { + try { + mProgressDialogFragment.dismissAllowingStateLoss(); + } catch ( Exception e) { + Logger.e(TAG, "dialog 取消异常"); + } finally { + mProgressDialogFragment = null; + mFragmentManager = null; + } + } + } + +} + diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ui/ProgressDialogFragment.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ui/ProgressDialogFragment.java new file mode 100644 index 0000000000..527957bf17 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/ui/ProgressDialogFragment.java @@ -0,0 +1,40 @@ +package com.mogo.utils.network.ui; + +import android.app.Dialog; +import android.app.ProgressDialog; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; + + +public class ProgressDialogFragment extends DialogFragment { + + /** + * Progress显示的文字 + */ + private CharSequence mMessage; + + private boolean mCancelableOnTouchOutSide; + + /** + * 设置dialog内容 + * + * @param message 显示的文字 + * @param cancelable 是否可以被取消 例如back键取消 + */ + public void setContent( CharSequence message, boolean cancelable, boolean cancelableOnTouchOutSide) { + mMessage = message; + mCancelableOnTouchOutSide = cancelableOnTouchOutSide; + setCancelable(cancelable); + } + + @NonNull + @Override + public Dialog onCreateDialog( Bundle savedInstanceState) { + ProgressDialog dialog = new ProgressDialog(getActivity()); + dialog.setMessage(mMessage); + dialog.setCanceledOnTouchOutside(mCancelableOnTouchOutSide); + return dialog; + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/GsonUtil.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/GsonUtil.java new file mode 100644 index 0000000000..ea1cfad71d --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/GsonUtil.java @@ -0,0 +1,77 @@ +package com.mogo.utils.network.utils; + +import android.text.TextUtils; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + + +public class GsonUtil { + + private static volatile Gson gson; + + private GsonUtil() {} + + public static Gson getGson() { + if(gson == null){ + synchronized (GsonUtil.class) { + if (gson == null) { + GsonBuilder builder = new GsonBuilder(); + gson = builder.create(); + } + } + } + return gson; + } + + public static String jsonFromObject( Object object) { + if (object == null) { + return null; + } else { + try { + return getGson().toJson(object); + } catch ( Exception var2) { + return null; + } + } + } + + public static T objectFromJson( String json, Class klass) { + if (json == null) { + return null; + } else { + try { + return getGson().fromJson(json, klass); + } catch ( Exception var3) { + return null; + } + } + } + + + public static List arrayFromJson( String json, Class clazz) { + List list = new ArrayList(); + if ( TextUtils.isEmpty(json)) { + return null; + } + try { + org.json.JSONArray array = new org.json.JSONArray(json); + for (int i = 0; i < array.length(); i++) { + JSONObject object = array.getJSONObject(i); + String js = object.toString(); + T t = GsonUtil.objectFromJson(js, clazz); + list.add(t); + } + return list; + } catch ( Exception e) { + e.printStackTrace(); + } + return null; + + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/SignUtil.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/SignUtil.java new file mode 100644 index 0000000000..282bb3664e --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/SignUtil.java @@ -0,0 +1,120 @@ +package com.mogo.utils.network.utils; + + +import com.mogo.utils.digest.DigestUtils; +import com.mogo.utils.network.HttpParams; + +import org.json.JSONObject; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + + +public class SignUtil { + + private static class StrSortTreeMap extends TreeMap< String, Object > { + private static final long serialVersionUID = 1L; + + public StrSortTreeMap() { + super(new Comparator< String >() { + + public int compare( String str1, String str2) { + return str1.compareTo(str2); + } + }); + } + } + + public static String createSign( Map< String, Object > map, String key) { + if (map == null || map.isEmpty()) { + return ""; + } + StrSortTreeMap treeMap = new StrSortTreeMap(); + for ( String k : map.keySet()) { + treeMap.put(k, map.get(k)); + } + return createSign(treeMap, key); + } + + private static String createSign( StrSortTreeMap map, String key) { + try{ + StringBuilder queryString = new StringBuilder(); + for ( Map.Entry< String, Object > entry : map.entrySet()) { + queryString.append(entry.getKey()) + .append("=") + .append(entry.getValue()) + .append("&"); + } + queryString.append("key=").append(DigestUtils.shaHex(key)); + return DigestUtils.shaHex(queryString.toString()).toUpperCase(); + }catch ( Exception e){ + e.printStackTrace(); + return ""; + } + } + + public static String encode( String string) { + try { + return URLEncoder.encode(string, "UTF-8"); + } catch ( UnsupportedEncodingException e) { + e.printStackTrace(); + return ""; + } + } + public static String decode( String string) { + try { + return URLDecoder.decode(string, "UTF-8"); + } catch ( UnsupportedEncodingException e) { + e.printStackTrace(); + return ""; + } + } + + /** + * 生成请求参数的签名 + * + * @param params HTTP请求中的所有POST或GET参数的Key-Value集合 + * @return 签名字符串,用于赋值给sig参数 + */ + public static String generateSignature( HttpParams params) { + String paramString = params.getSortedParamsString(); + try { + return DigestUtils.shaHex(paramString); + } catch ( Exception e) { + return ""; + } + } + + public static String generateSignature( Map< String, String > params) { + HttpParams httpParams = new HttpParams(params); + return generateSignature(httpParams); + } + + public static Map< String, Object > convertToMap( JSONObject jsonObject) { + final TreeMap< String, Object > sortedMap = new TreeMap< String, Object >(); + + if (jsonObject != null) { + Iterator< String > keys = jsonObject.keys(); + while (keys.hasNext()) { + String key = keys.next(); + Object value = jsonObject.opt(key); + sortedMap.put(key,value); + } + } + + return sortedMap; + } + + public static String signString( String value) { + try { + return DigestUtils.shaHex(value); + } catch ( Exception e) { + return ""; + } + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/Util.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/Util.java new file mode 100644 index 0000000000..9caf3d16cb --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/network/utils/Util.java @@ -0,0 +1,94 @@ +package com.mogo.utils.network.utils; + +import android.app.Activity; +import android.app.Dialog; +import android.app.Fragment; +import android.content.Context; +import android.content.ContextWrapper; +import android.os.Build; +import android.os.Looper; +import android.view.View; +import android.widget.PopupWindow; + +import com.mogo.utils.network.CallerNotAliveException; + +public class Util { + + public static boolean checkAlive( Object caller ) { + if ( caller instanceof Activity ) { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 ? !( ( Activity ) caller ).isDestroyed() : !( ( Activity ) caller ).isFinishing(); + } else if ( caller instanceof Fragment ) { + return ( ( Fragment ) caller ).isAdded(); + } else if ( caller instanceof androidx.fragment.app.Fragment ) { + return ( ( androidx.fragment.app.Fragment ) caller ).isAdded(); + } else if ( caller instanceof View ) { + return true; + } else if ( caller instanceof Dialog ) { + return ( ( Dialog ) caller ).getWindow() != null; + } else if ( caller instanceof PopupWindow ) { + return ( ( PopupWindow ) caller ).getContentView() != null; + } + return caller != null; + } + + public static void assertCallerAlive( Object caller ) throws CallerNotAliveException { + if ( !checkAlive( caller ) ) { + throw new CallerNotAliveException( "Caller is not alive any more" ); + } + } + + public static Context getContext( Object object ) { + if ( object instanceof Activity ) { + return ( Activity ) object; + } else if ( object instanceof Fragment ) { + return ( ( Fragment ) object ).getActivity(); + } else if ( object instanceof androidx.fragment.app.Fragment ) { + return ( ( androidx.fragment.app.Fragment ) object ).getActivity(); + } else if ( object instanceof View ) { + return ( ( View ) object ).getContext(); + } else if ( object instanceof Dialog ) { + return ( ( Dialog ) object ).getContext(); + } else if ( object instanceof PopupWindow ) { + if ( ( ( PopupWindow ) object ).getContentView() != null ) + return ( ( PopupWindow ) object ).getContentView().getContext(); + else return null; + } else if ( object instanceof ContextWrapper ) { + return ( ( ContextWrapper ) object ).getBaseContext(); + } else { + return null; + } + } + + /** + * Throws an {@link IllegalArgumentException} if called on a thread other than the main + * thread. + */ + public static void assertMainThread() { + if ( !isOnMainThread() ) { + throw new IllegalArgumentException( "You must call this method on the main thread" ); + } + } + + /** + * Throws an {@link IllegalArgumentException} if called on the main thread. + */ + public static void assertBackgroundThread() { + if ( !isOnBackgroundThread() ) { + throw new IllegalArgumentException( "You must call this method on a background thread" ); + } + } + + /** + * Returns {@code true} if called on the main thread, {@code false} otherwise. + */ + public static boolean isOnMainThread() { + return Looper.myLooper() == Looper.getMainLooper(); + } + + /** + * Returns {@code true} if called on a background thread, {@code false} otherwise. + */ + public static boolean isOnBackgroundThread() { + return !isOnMainThread(); + } +} diff --git a/foudations/mogo-utils/src/main/java/com/mogo/utils/storage/SharedPrefsMgr.java b/foudations/mogo-utils/src/main/java/com/mogo/utils/storage/SharedPrefsMgr.java new file mode 100644 index 0000000000..3acfd14fa3 --- /dev/null +++ b/foudations/mogo-utils/src/main/java/com/mogo/utils/storage/SharedPrefsMgr.java @@ -0,0 +1,131 @@ +package com.mogo.utils.storage; + +import android.content.Context; +import android.content.SharedPreferences; + +import androidx.annotation.NonNull; + +public class SharedPrefsMgr { + + private static final String File_Name = "app_shared_pref"; + private static SharedPrefsMgr sInstance; + private static SharedPreferences sSharedPrefs; + + public synchronized static SharedPrefsMgr getInstance( @NonNull Context context ) { + if ( sInstance == null ) { + try { + sInstance = new SharedPrefsMgr( context.getApplicationContext() ); + } catch ( Exception e ) { + sInstance = new SharedPrefsMgr(); + } + } + return sInstance; + } + + private SharedPrefsMgr() { + + } + + private SharedPrefsMgr( Context context ) { + try { + sSharedPrefs = context.getSharedPreferences( File_Name, Context.MODE_PRIVATE ); + } catch ( Exception e ) { + e.printStackTrace(); + } + } + + public void putString( String key, String value ) { + try { + SharedPreferences.Editor editor = sSharedPrefs.edit(); + editor.putString( key, value ); + editor.apply(); + } catch ( Exception e ) { + } + } + + public String getString( String tag ) { + try { + return sSharedPrefs.getString( tag, "" ); + } catch ( Exception e ) { + return ""; + } + } + + public boolean getBoolean( String key, boolean defaultValue ) { + try { + return sSharedPrefs.getBoolean( key, defaultValue ); + } catch ( Exception e ) { + return defaultValue; + } + } + + public long getLong( String key, long defaultValue ) { + try { + return sSharedPrefs.getLong( key, defaultValue ); + } catch ( Exception e ) { + return defaultValue; + } + } + + public float getFloat( String key, float defaultValue ) { + try { + return sSharedPrefs.getFloat( key, defaultValue ); + } catch ( Exception e ) { + return defaultValue; + } + } + + public int getInt( String key, int value ) { + try { + return sSharedPrefs.getInt( key, value ); + } catch ( Exception e ) { + return value; + } + } + + public void putBoolean( String key, boolean value ) { + try { + SharedPreferences.Editor editor = sSharedPrefs.edit(); + editor.putBoolean( key, value ); + editor.apply(); + } catch ( Exception e ) { + } + } + + public void putLong( String key, long value ) { + try { + SharedPreferences.Editor editor = sSharedPrefs.edit(); + editor.putLong( key, value ); + editor.apply(); + } catch ( Exception e ) { + } + } + + public void putInt( String key, int value ) { + try { + SharedPreferences.Editor editor = sSharedPrefs.edit(); + editor.putInt( key, value ); + editor.apply(); + } catch ( Exception e ) { + } + } + + public void putFloat( String key, float value ) { + try { + SharedPreferences.Editor editor = sSharedPrefs.edit(); + editor.putFloat( key, value ); + editor.apply(); + } catch ( Exception e ) { + } + } + + public void remove( String key ) { + try { + SharedPreferences.Editor editor = sSharedPrefs.edit(); + editor.remove( key ); + editor.apply(); + } catch ( Exception e ) { + } + } + +} \ No newline at end of file diff --git a/foudations/mogo-utils/src/main/res/values/strings.xml b/foudations/mogo-utils/src/main/res/values/strings.xml new file mode 100644 index 0000000000..b136d6c475 --- /dev/null +++ b/foudations/mogo-utils/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + mogo-utils + diff --git a/foudations/mogo-utils/src/test/java/com/mogo/utils/ExampleUnitTest.java b/foudations/mogo-utils/src/test/java/com/mogo/utils/ExampleUnitTest.java new file mode 100644 index 0000000000..f20b6cb3c3 --- /dev/null +++ b/foudations/mogo-utils/src/test/java/com/mogo/utils/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.mogo.utils; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals( 4, 2 + 2 ); + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000..199d16ede3 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,20 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..f6b961fd5a Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..cdc2ce1117 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Dec 18 10:03:53 CST 2019 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..42417e852f --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not mQuery maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..e95643d6a2 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libraries/mogo-map/.gitignore b/libraries/mogo-map/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/libraries/mogo-map/.gitignore @@ -0,0 +1 @@ +/build diff --git a/libraries/mogo-map/build.gradle b/libraries/mogo-map/build.gradle new file mode 100644 index 0000000000..545771335c --- /dev/null +++ b/libraries/mogo-map/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation rootProject.ext.dependencies.androidxappcompat + implementation rootProject.ext.dependencies.amapnavi3dmap + implementation rootProject.ext.dependencies.amapsearch + implementation rootProject.ext.dependencies.amaplocation + + implementation project(':foudations:mogo-utils') +} diff --git a/libraries/mogo-map/consumer-rules.pro b/libraries/mogo-map/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libraries/mogo-map/proguard-rules.pro b/libraries/mogo-map/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/libraries/mogo-map/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/libraries/mogo-map/src/main/AndroidManifest.xml b/libraries/mogo-map/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..cfaf88eee3 --- /dev/null +++ b/libraries/mogo-map/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/ILifeCycle.java b/libraries/mogo-map/src/main/java/com/mogo/map/ILifeCycle.java new file mode 100644 index 0000000000..2752e1d112 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/ILifeCycle.java @@ -0,0 +1,25 @@ +package com.mogo.map; + +import android.os.Bundle; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 生命周期 + */ +public interface ILifeCycle { + + void onCreate( Bundle bundle ); + + void onResume(); + + void onPause(); + + void onDestroy(); + + void onSaveInstanceState( Bundle outState ); + + // mapview only. + void onLowMemory(); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/IMogoMap.java b/libraries/mogo-map/src/main/java/com/mogo/map/IMogoMap.java new file mode 100644 index 0000000000..5ff709f78b --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/IMogoMap.java @@ -0,0 +1,99 @@ +package com.mogo.map; + +import com.mogo.map.marker.IMogoMarker; +import com.mogo.map.marker.MogoMarkerOptions; + +import java.util.ArrayList; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 地图控制接口 + */ +public interface IMogoMap { + + /** + * 获取地图ui控制器,可以控制内置ui(缩放按钮、指北针等)是否显示及部分手势(滑动、双指缩放等)是否可用。 + * + * @return + */ + IUiSettings getUiSettings(); + + /** + * 在地图上添一个图片标记(marker)对象。 + * + * @param options + * @return + */ + IMogoMarker addMarker( MogoMarkerOptions options ); + + /** + * 在地图上添一组图片标记(marker)对象,并设置是否改变地图状态以至于所有的marker对象都在当前地图可视区域范围内显示。 + * + * @param options + * @param moveToCenter + * @return + */ + ArrayList< IMogoMarker > addMarkers( ArrayList< MogoMarkerOptions > options, boolean moveToCenter ); + + /** + * 从地图上删除所有的overlay(marker,circle,polyline 等对象)。 + */ + void clear(); + + /** + * 从地图上删除所有的覆盖物(marker,circle,polyline 等对象),但myLocationOverlay(内置定位覆盖物)除外。 + * + * @param isKeepMyLocationOverlay + */ + void clear( boolean isKeepMyLocationOverlay ); + + /** + * 设置屏幕上的某个像素点为地图中心点。 + * + * @param x + * @param y + */ + void setPointToCenter( int x, int y ); + + /** + * 设置地图POI是否允许点击。 + * + * @param touchPoiEnable + */ + void setTouchPoiEnable( boolean touchPoiEnable ); + + /** + * 设置是否打开交通路况图层。 + * + * @param enable + */ + void setTrafficEnable( boolean enable ); + + /** + * 设置是否显示3D建筑物,默认显示。 + * + * @param enabled + */ + void showBuildings( boolean enabled ); + + /** + * 设置是否显示室内地图,默认不显示。 + * + * @param enable + */ + void showIndoorMap( boolean enable ); + + /** + * 设置是否显示底图文字标注,默认显示。 + * + * @param enable + */ + void showMapText( boolean enable ); + + /** + * 停止当前执行的改变地图状态的动画。 + */ + void stopAnimation(); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/IMogoMapView.java b/libraries/mogo-map/src/main/java/com/mogo/map/IMogoMapView.java new file mode 100644 index 0000000000..59c174e296 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/IMogoMapView.java @@ -0,0 +1,21 @@ +package com.mogo.map; + +import android.view.View; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 地图抽象 + */ +public interface IMogoMapView extends ILifeCycle { + + /** + * 获取地图实例 + * + * @return 地图实例 + */ + View getMapView(); + + IMogoMap getMap(); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/IUiSettings.java b/libraries/mogo-map/src/main/java/com/mogo/map/IUiSettings.java new file mode 100644 index 0000000000..85d1895297 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/IUiSettings.java @@ -0,0 +1,68 @@ +package com.mogo.map; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 图层控制 + */ +public interface IUiSettings { + + /** + * 设置比例尺功能是否可用。 + */ + void setScaleControlsEnabled( boolean enabled ); + + /** + * 这个方法设置了地图是否允许显示缩放按钮。如果允许,则在地图上显示。 + * + * @param enabled + */ + void setZoomControlsEnabled( boolean enabled ); + + /** + * 这个方法设置了地图是否允许显示指南针 + * + * @param enabled + */ + void setCompassEnabled( boolean enabled ); + + /** + * 设置定位按钮是否显示 + * + * @param enabled + */ + void setMyLocationButtonEnabled( boolean enabled ); + + /** + * 这个方法设置了地图是否允许通过手势来移动。如果允许,则用户可以通过按住地图移动来改变可视区域。 + * 如果禁止,则不支持此功能。 这个设置不会影响用户在程序里对地图的移动。 默认移动手势为可用。 + * + * @param enabled + */ + void setScrollGesturesEnabled( boolean enabled ); + + /** + * 这个方法设置了地图是否允许通过手势来缩放。 + * 如果允许,则用户可以通过双击地图或双指在地图上捏合来绽放地图。 + * 这个设置不会影响缩放按钮的功能, 也不会影响程序对地图的操控。 默认允许通过手势缩放地图。 + * + * @param enabled + */ + void setZoomGesturesEnabled( boolean enabled ); + + void setTiltGesturesEnabled( boolean enabled ); + + void setRotateGesturesEnabled( boolean enabled ); + + /** + * 设置当前地图是否支持所有手势。这个设置不影响用户在点击屏幕上的按钮(如缩放按钮)的效果,也不影响用户在程序里对地图的操作。 + * + * @param enabled + */ + void setAllGesturesEnabled( boolean enabled ); + + void setIndoorSwitchEnabled( boolean enabled ); + + void setLogoEnable( boolean enabled ); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/MogoBaseMapView.java b/libraries/mogo-map/src/main/java/com/mogo/map/MogoBaseMapView.java new file mode 100644 index 0000000000..64b4bfee63 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/MogoBaseMapView.java @@ -0,0 +1,114 @@ +package com.mogo.map; + +import android.content.Context; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.Nullable; + +import com.mogo.utils.logger.Logger; + + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 对地图的基本抽象 + */ +public abstract class MogoBaseMapView extends FrameLayout implements ILifeCycle { + + private static final String TAG = "MogoBaseMapView"; + + private IMogoMapView mMapView; + + public MogoBaseMapView( Context context ) { + this( context, null ); + } + + public MogoBaseMapView( Context context, @Nullable AttributeSet attrs ) { + this( context, attrs, 0 ); + } + + public MogoBaseMapView( Context context, @Nullable AttributeSet attrs, int defStyleAttr ) { + super( context, attrs, defStyleAttr ); + init( context ); + } + + private void init( Context context ) { + mMapView = createMapView( context ); + if ( mMapView != null ) { + final View mapView = mMapView.getMapView(); + if ( mapView != null ) { + addView( mapView ); + MogoMap.getInstance().init( context, getMap() ); + } else { + Logger.e( TAG, "create MapView instance failed." ); + } + } else { + Logger.e( TAG, "create IMogoMapView instance failed." ); + } + } + + /** + * 创建地图实例 + * + * @param context + * @return + */ + protected abstract IMogoMapView createMapView( Context context ); + + @Override + public void onCreate( Bundle bundle ) { + if ( mMapView != null ) { + mMapView.onCreate( bundle ); + } + } + + @Override + public void onResume() { + if ( mMapView != null ) { + mMapView.onResume(); + } + } + + @Override + public void onPause() { + if ( mMapView != null ) { + mMapView.onPause(); + } + } + + @Override + public void onDestroy() { + if ( mMapView != null ) { + mMapView.onDestroy(); + } + } + + @Override + public void onSaveInstanceState( Bundle outState ) { + if ( mMapView != null ) { + mMapView.onSaveInstanceState( outState ); + } + } + + @Override + public void onLowMemory() { + if ( mMapView != null ) { + mMapView.onLowMemory(); + } + } + + public IMogoMap getMap() { + if ( mMapView != null ) { + return mMapView.getMap(); + } + return null; + } + + public IMogoMapView getMapView() { + return mMapView; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/MogoLatLng.java b/libraries/mogo-map/src/main/java/com/mogo/map/MogoLatLng.java new file mode 100644 index 0000000000..cfa48c3326 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/MogoLatLng.java @@ -0,0 +1,49 @@ +package com.mogo.map; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 经纬度 + */ +public class MogoLatLng implements Parcelable { + + public final double lat; + public final double lng; + + public MogoLatLng( double lat, double lng ) { + this.lat = lat; + this.lng = lng; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeDouble( this.lat ); + dest.writeDouble( this.lng ); + } + + protected MogoLatLng( Parcel in ) { + this.lat = in.readDouble(); + this.lng = in.readDouble(); + } + + public static final Parcelable.Creator< MogoLatLng > CREATOR = new Parcelable.Creator< MogoLatLng >() { + @Override + public MogoLatLng createFromParcel( Parcel source ) { + return new MogoLatLng( source ); + } + + @Override + public MogoLatLng[] newArray( int size ) { + return new MogoLatLng[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/MogoMap.java b/libraries/mogo-map/src/main/java/com/mogo/map/MogoMap.java new file mode 100644 index 0000000000..7571480782 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/MogoMap.java @@ -0,0 +1,44 @@ +package com.mogo.map; + +import android.content.Context; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * 描述 + */ +public class MogoMap { + + private IMogoMap mMap; + private Context mContext; + + private static volatile MogoMap sInstance; + + private MogoMap() { + } + + public static MogoMap getInstance() { + if ( sInstance == null ) { + synchronized ( MogoMap.class ) { + if ( sInstance == null ) { + sInstance = new MogoMap(); + } + } + } + return sInstance; + } + + public void init( Context context, IMogoMap map ) { + this.mContext = context; + this.mMap = map; + } + + public Context getContext() { + return mContext; + } + + public IMogoMap getMogoMap() { + return mMap; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/MogoMapView.java b/libraries/mogo-map/src/main/java/com/mogo/map/MogoMapView.java new file mode 100644 index 0000000000..342d9d2a83 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/MogoMapView.java @@ -0,0 +1,35 @@ +package com.mogo.map; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.Nullable; + +import com.amap.api.navi.AMapNaviView; +import com.mogo.map.amap.AMapNaviViewWrapper; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 地图实例 + */ +public class MogoMapView extends MogoBaseMapView { + + public MogoMapView( Context context ) { + super( context ); + } + + public MogoMapView( Context context, @Nullable AttributeSet attrs ) { + super( context, attrs ); + } + + public MogoMapView( Context context, @Nullable AttributeSet attrs, int defStyleAttr ) { + super( context, attrs, defStyleAttr ); + } + + @Override + protected IMogoMapView createMapView( Context context ) { + return new AMapNaviViewWrapper( new AMapNaviView( context ) ); + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapNaviViewWrapper.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapNaviViewWrapper.java new file mode 100644 index 0000000000..5dfdcacb11 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapNaviViewWrapper.java @@ -0,0 +1,78 @@ +package com.mogo.map.amap; + +import android.os.Bundle; +import android.view.View; + +import com.amap.api.navi.AMapNaviView; +import com.mogo.map.IMogoMap; +import com.mogo.map.IMogoMapView; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 代理高德导航地图 + */ +public class AMapNaviViewWrapper implements IMogoMapView { + + private final AMapNaviView mMapView; + private IMogoMap mIMap; + + public AMapNaviViewWrapper( AMapNaviView mapView ) { + this.mMapView = mapView; + } + + @Override + public View getMapView() { + return mMapView; + } + + @Override + public IMogoMap getMap() { + if ( mMapView != null ) { + if ( mIMap == null ) { + mIMap = new AMapWrapper( mMapView.getMap() ); + } + } + return mIMap; + } + + @Override + public void onCreate( Bundle bundle ) { + if ( mMapView != null ) { + mMapView.onCreate( bundle ); + } + } + + @Override + public void onResume() { + if ( mMapView != null ) { + mMapView.onResume(); + } + } + + @Override + public void onPause() { + if ( mMapView != null ) { + mMapView.onPause(); + } + } + + @Override + public void onDestroy() { + if ( mMapView != null ) { + mMapView.onDestroy(); + } + } + + @Override + public void onSaveInstanceState( Bundle outState ) { + if ( mMapView != null ) { + mMapView.onSaveInstanceState( outState ); + } + } + + @Override + public void onLowMemory() { + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapUiSettingsWrapper.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapUiSettingsWrapper.java new file mode 100644 index 0000000000..ff966d000e --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapUiSettingsWrapper.java @@ -0,0 +1,104 @@ +package com.mogo.map.amap; + +import com.amap.api.maps.UiSettings; +import com.mogo.map.IUiSettings; + +import java.lang.reflect.Method; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 代理高德地图UiSettings + */ +public class AMapUiSettingsWrapper implements IUiSettings { + + private UiSettings mUiSettings; + + public AMapUiSettingsWrapper( UiSettings mUiSettings ) { + this.mUiSettings = mUiSettings; + } + + @Override + public void setScaleControlsEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setScaleControlsEnabled( enabled ); + } + } + + @Override + public void setZoomControlsEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setZoomControlsEnabled( enabled ); + } + } + + @Override + public void setCompassEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setCompassEnabled( enabled ); + } + } + + @Override + public void setMyLocationButtonEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setMyLocationButtonEnabled( enabled ); + } + } + + @Override + public void setScrollGesturesEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setScrollGesturesEnabled( enabled ); + } + } + + @Override + public void setZoomGesturesEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setZoomGesturesEnabled( enabled ); + } + } + + @Override + public void setTiltGesturesEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setTiltGesturesEnabled( enabled ); + } + } + + @Override + public void setRotateGesturesEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setRotateGesturesEnabled( enabled ); + } + } + + @Override + public void setAllGesturesEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setAllGesturesEnabled( enabled ); + } + } + + @Override + public void setIndoorSwitchEnabled( boolean enabled ) { + if ( mUiSettings != null ) { + mUiSettings.setIndoorSwitchEnabled( enabled ); + } + } + + @Override + public void setLogoEnable( boolean enabled ) { + if ( mUiSettings != null ) { + try { + Method method = mUiSettings.getClass().getMethod( "setLogoEnable", Boolean.class ); + method.setAccessible( true ); + method.invoke( mUiSettings, enabled ); + } catch ( Exception e ) { + e.printStackTrace(); + } + } + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapWrapper.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapWrapper.java new file mode 100644 index 0000000000..834b8b58e4 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/AMapWrapper.java @@ -0,0 +1,153 @@ +package com.mogo.map.amap; + +import com.amap.api.maps.AMap; +import com.amap.api.maps.model.Marker; +import com.amap.api.maps.model.MarkerOptions; +import com.mogo.map.IMogoMap; +import com.mogo.map.marker.IMogoMarker; +import com.mogo.map.IUiSettings; +import com.mogo.map.marker.MogoMarkerOptions; +import com.mogo.map.amap.marker.AMapMarkerWrapper; +import com.mogo.map.amap.utils.ObjectUtils; + +import java.util.ArrayList; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 代理高德AMap + */ +public class AMapWrapper implements IMogoMap { + + private AMap mAMap; + private IUiSettings mUiSettings; + + public AMapWrapper( AMap map ) { + this.mAMap = map; + } + + @Override + public IUiSettings getUiSettings() { + if ( mUiSettings == null ) { + mUiSettings = new AMapUiSettingsWrapper( mAMap.getUiSettings() ); + } + return mUiSettings; + } + + @Override + public IMogoMarker addMarker( MogoMarkerOptions options ) { + if ( mAMap == null ) { + return null; + } + MarkerOptions markerOptions = ObjectUtils.fromMogo( options ); + if ( markerOptions == null ) { + return null; + } + return new AMapMarkerWrapper( mAMap.addMarker( markerOptions ) ); + } + + @Override + public ArrayList< IMogoMarker > addMarkers( ArrayList< MogoMarkerOptions > options, boolean moveToCenter ) { + + if ( mAMap == null ) { + return null; + } + + if ( options == null || options.isEmpty() ) { + return null; + } + + ArrayList< Marker > markers = null; + ArrayList< MarkerOptions > markerOptions = new ArrayList<>(); + ArrayList< IMogoMarker > mogoMarkers = new ArrayList<>(); + + for ( MogoMarkerOptions option : options ) { + if ( option == null ) { + continue; + } + MarkerOptions mo = ObjectUtils.fromMogo( option ); + if ( mo == null ) { + continue; + } + markerOptions.add( mo ); + } + if ( markerOptions.isEmpty() ) { + return null; + } + markers = mAMap.addMarkers( markerOptions, moveToCenter ); + if ( markers == null || markers.isEmpty() ) { + return null; + } + for ( Marker marker : markers ) { + if ( marker == null ) { + continue; + } + mogoMarkers.add( new AMapMarkerWrapper( marker ) ); + } + return mogoMarkers; + } + + @Override + public void clear() { + if ( mAMap != null ) { + mAMap.clear(); + } + } + + @Override + public void clear( boolean isKeepMyLocationOverlay ) { + if ( mAMap != null ) { + mAMap.clear( isKeepMyLocationOverlay ); + } + } + + @Override + public void setPointToCenter( int x, int y ) { + if ( mAMap != null ) { + mAMap.setPointToCenter( x, y ); + } + } + + @Override + public void setTouchPoiEnable( boolean touchPoiEnable ) { + if ( mAMap != null ) { + mAMap.setTouchPoiEnable( touchPoiEnable ); + } + } + + @Override + public void setTrafficEnable( boolean enable ) { + if ( mAMap != null ) { + mAMap.setTrafficEnabled( enable ); + } + } + + @Override + public void showBuildings( boolean enabled ) { + if ( mAMap != null ) { + mAMap.showBuildings( enabled ); + } + } + + @Override + public void showIndoorMap( boolean enable ) { + if ( mAMap != null ) { + mAMap.showIndoorMap( enable ); + } + } + + @Override + public void showMapText( boolean enable ) { + if ( mAMap != null ) { + mAMap.showMapText( enable ); + } + } + + @Override + public void stopAnimation() { + if ( mAMap != null ) { + mAMap.stopAnimation(); + } + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/location/LocationClient.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/location/LocationClient.java new file mode 100644 index 0000000000..d764ec5b82 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/location/LocationClient.java @@ -0,0 +1,116 @@ +package com.mogo.map.amap.location; + +import android.content.Context; + +import com.amap.api.location.AMapLocation; +import com.amap.api.location.AMapLocationClient; +import com.amap.api.location.AMapLocationClientOption; +import com.amap.api.location.AMapLocationListener; +import com.mogo.map.amap.utils.ObjectUtils; +import com.mogo.map.location.ILocationListener; +import com.mogo.map.location.IMogoLocationClient; +import com.mogo.map.location.MogoLocation; +import com.mogo.utils.logger.Logger; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 高德定位 + */ +public class LocationClient implements IMogoLocationClient { + + private static final String TAG = "LocationClient"; + + private static volatile LocationClient sInstance; + private static Set< ILocationListener > sListeners = new HashSet<>( 10 ); + private static MogoLocation sLastLocation = new MogoLocation(); + + private LocationClient( Context context ) { + mClient = new AMapLocationClient( context ); + mClient.setLocationListener( new InternalLocationListener() ); + } + + public static LocationClient getInstance( Context context ) { + if ( sInstance == null ) { + synchronized ( LocationClient.class ) { + if ( sInstance == null ) { + sInstance = new LocationClient( context ); + } + } + } + return sInstance; + } + + private AMapLocationClient mClient; + + @Override + public void start() { + start( 2_000L ); + } + + @Override + public void start( long interval ) { + stop(); + AMapLocationClientOption option = new AMapLocationClientOption(); + option.setLocationMode( AMapLocationClientOption.AMapLocationMode.Hight_Accuracy ); + option.setNeedAddress( true ); + option.setInterval( interval ); + if ( mClient != null ) { + mClient.setLocationOption( option ); + } + } + + @Override + public void stop() { + if ( mClient != null ) { + mClient.stopLocation(); + } + } + + @Override + public void addLocationListener( ILocationListener listener ) { + if ( listener != null ) { + synchronized ( sListeners ) { + sListeners.add( listener ); + } + } + } + + @Override + public void removeLocationListener( ILocationListener listener ) { + if ( listener != null ) { + synchronized ( sListeners ) { + sListeners.remove( listener ); + } + } + } + + @Override + public MogoLocation getLastKnowLocation() { + return sLastLocation; + } + + private static class InternalLocationListener implements AMapLocationListener { + @Override + public void onLocationChanged( AMapLocation aMapLocation ) { + if ( aMapLocation == null || + aMapLocation.getLatitude() == 0.0D || + aMapLocation.getLongitude() == 0.0D ) { + return; + } + Logger.d( TAG, aMapLocation.toString() ); + sLastLocation = ObjectUtils.fromAMap( aMapLocation ); + synchronized ( sListeners ) { + Iterator< ILocationListener > listenerIterator = sListeners.iterator(); + while ( listenerIterator.hasNext() ) { + listenerIterator.next().onLocationChanged( sLastLocation.clone() ); + } + } + } + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/marker/AMapMarkerWrapper.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/marker/AMapMarkerWrapper.java new file mode 100644 index 0000000000..b89701ae59 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/marker/AMapMarkerWrapper.java @@ -0,0 +1,190 @@ +package com.mogo.map.amap.marker; + +import android.graphics.Bitmap; + +import com.amap.api.maps.model.BitmapDescriptor; +import com.amap.api.maps.model.BitmapDescriptorFactory; +import com.amap.api.maps.model.LatLng; +import com.amap.api.maps.model.Marker; +import com.amap.api.maps.model.MarkerOptions; +import com.mogo.map.marker.IMogoMarker; +import com.mogo.map.marker.MogoMarkerOptions; +import com.mogo.map.amap.utils.ObjectUtils; + +import java.util.ArrayList; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 代理高德marker + */ +public class AMapMarkerWrapper implements IMogoMarker { + + private Marker mMarker; + + public AMapMarkerWrapper( Marker mMarker ) { + this.mMarker = mMarker; + } + + @Override + public void destroy() { + if ( mMarker != null ) { + mMarker.destroy(); + } + } + + @Override + public void remove() { + if ( mMarker != null ) { + mMarker.remove(); + } + } + + @Override + public void hideInfoWindow() { + if ( mMarker != null ) { + mMarker.hideInfoWindow(); + } + } + + @Override + public void setAlpha( float alpha ) { + if ( mMarker != null ) { + mMarker.setAlpha( alpha ); + } + } + + @Override + public void setAnchor( float anchorU, float anchorV ) { + if ( mMarker != null ) { + mMarker.setAnchor( anchorU, anchorV ); + } + } + + @Override + public void setDraggable( boolean paramBoolean ) { + if ( mMarker != null ) { + mMarker.setDraggable( paramBoolean ); + } + } + + @Override + public void setIcon( Bitmap icon ) { + if ( icon == null || icon.isRecycled() ) { + return; + } + if ( mMarker != null ) { + mMarker.setIcon( BitmapDescriptorFactory.fromBitmap( icon ) ); + } + } + + @Override + public void setIcons( ArrayList< Bitmap > icons ) { + if ( icons == null || icons.isEmpty() ) { + return; + } + ArrayList< BitmapDescriptor > descriptors = new ArrayList<>(); + for ( Bitmap icon : icons ) { + if ( icon == null || icon.isRecycled() ) { + continue; + } + descriptors.add( BitmapDescriptorFactory.fromBitmap( icon ) ); + } + if ( descriptors.isEmpty() ) { + return; + } + if ( mMarker != null ) { + mMarker.setIcons( descriptors ); + } + } + + @Override + public void setInfoWindowEnable( boolean enabled ) { + if ( mMarker != null ) { + mMarker.setInfoWindowEnable( enabled ); + } + } + + @Override + public void setMarkerOptions( MogoMarkerOptions opt ) { + + final MarkerOptions options = ObjectUtils.fromMogo( opt ); + if ( options == null ) { + return; + } + if ( mMarker != null ) { + mMarker.setMarkerOptions( options ); + } + } + + @Override + public void setObject( Object object ) { + if ( mMarker != null ) { + mMarker.setObject( object ); + } + } + + @Override + public void setPeriod( int period ) { + if ( mMarker != null ) { + mMarker.setPeriod( period ); + } + } + + @Override + public void setPosition( double lat, double lng ) { + if ( mMarker != null ) { + mMarker.setPosition( new LatLng( lat, lng ) ); + } + } + + @Override + public void setRotateAngle( float rotate ) { + if ( mMarker != null ) { + mMarker.setRotateAngle( rotate ); + } + } + + @Override + public void setSnippet( String snippet ) { + if ( mMarker != null ) { + mMarker.setSnippet( snippet ); + } + } + + @Override + public void setTitle( String title ) { + if ( mMarker != null ) { + mMarker.setTitle( title ); + } + } + + @Override + public void setToTop() { + if ( mMarker != null ) { + mMarker.setToTop(); + } + } + + @Override + public void setVisible( boolean visible ) { + if ( mMarker != null ) { + mMarker.setVisible( visible ); + } + } + + @Override + public void setZIndex( float zIndex ) { + if ( mMarker != null ) { + mMarker.setZIndex( zIndex ); + } + } + + @Override + public void showInfoWindow() { + if ( mMarker != null ) { + mMarker.showInfoWindow(); + } + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/search/GeocodeSearchClient.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/search/GeocodeSearchClient.java new file mode 100644 index 0000000000..974ae5563d --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/search/GeocodeSearchClient.java @@ -0,0 +1,101 @@ +package com.mogo.map.amap.search; + +import android.content.Context; + +import com.amap.api.services.core.AMapException; +import com.amap.api.services.geocoder.GeocodeAddress; +import com.amap.api.services.geocoder.GeocodeResult; +import com.amap.api.services.geocoder.GeocodeSearch; +import com.amap.api.services.geocoder.RegeocodeAddress; +import com.amap.api.services.geocoder.RegeocodeResult; +import com.mogo.map.amap.utils.ObjectUtils; +import com.mogo.map.exception.MogoMapException; +import com.mogo.map.search.IMogoGeoSearch; +import com.mogo.map.search.IMogoGeoSearchListener; +import com.mogo.map.search.MogoGeocodeAddress; +import com.mogo.map.search.MogoRegeocodeAddress; +import com.mogo.map.search.query.MogoGeocodeQuery; +import com.mogo.map.search.query.MogoRegeocodeQuery; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 地理编码/逆地理编码高德实现 + */ +public class GeocodeSearchClient implements IMogoGeoSearch, GeocodeSearch.OnGeocodeSearchListener { + + private GeocodeSearch mClient; + private IMogoGeoSearchListener mListener; + + public GeocodeSearchClient( Context context ) { + mClient = new GeocodeSearch( context ); + mClient.setOnGeocodeSearchListener( this ); + } + + @Override + public void setGeoSearchListener( IMogoGeoSearchListener listener ) { + mListener = listener; + } + + @Override + public MogoRegeocodeAddress getFromLocation( MogoRegeocodeQuery query ) throws MogoMapException { + try { + RegeocodeAddress regeocodeAddress = mClient.getFromLocation( ObjectUtils.fromMogo( query ) ); + return ObjectUtils.fromAMap( regeocodeAddress ); + } catch ( AMapException e ) { + throw new MogoMapException( e ); + } + } + + @Override + public List< MogoGeocodeAddress > getFromLocationName( MogoGeocodeQuery query ) throws MogoMapException { + try { + List< GeocodeAddress > geocodeAddress = mClient.getFromLocationName( ObjectUtils.fromMogo( query ) ); + if ( geocodeAddress != null ) { + List< MogoGeocodeAddress > mogoGeocodeAddresses = new ArrayList<>(); + for ( GeocodeAddress address : geocodeAddress ) { + MogoGeocodeAddress mogoGeocodeAddress = ObjectUtils.fromAMap( address ); + if ( mogoGeocodeAddress != null ) { + mogoGeocodeAddresses.add( mogoGeocodeAddress ); + } + } + return mogoGeocodeAddresses; + } + return new ArrayList<>(); + } catch ( AMapException e ) { + throw new MogoMapException( e ); + } + } + + @Override + public void getFromLocationAsyn( MogoRegeocodeQuery query ) { + if ( mClient != null ) { + mClient.getFromLocationAsyn( ObjectUtils.fromMogo( query ) ); + } + } + + @Override + public void getFromLocationNameAsyn( MogoGeocodeQuery query ) { + if ( mClient != null ) { + mClient.getFromLocationNameAsyn( ObjectUtils.fromMogo( query ) ); + } + } + + @Override + public void onRegeocodeSearched( RegeocodeResult regeocodeResult, int i ) { + if ( mListener != null ) { + mListener.onRegeocodeSearched( ObjectUtils.fromAMap( regeocodeResult ), i ); + } + } + + @Override + public void onGeocodeSearched( GeocodeResult geocodeResult, int i ) { + if ( mListener != null ) { + mListener.onGeocodeSearched( ObjectUtils.fromAMap( geocodeResult ), i ); + } + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/search/InputtipsSearch.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/search/InputtipsSearch.java new file mode 100644 index 0000000000..9be69939df --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/search/InputtipsSearch.java @@ -0,0 +1,84 @@ +package com.mogo.map.amap.search; + +import android.content.Context; + +import com.amap.api.services.core.AMapException; +import com.amap.api.services.help.Inputtips; +import com.amap.api.services.help.InputtipsQuery; +import com.amap.api.services.help.Tip; +import com.mogo.map.amap.utils.ObjectUtils; +import com.mogo.map.exception.MogoMapException; +import com.mogo.map.search.IMogoInputtipsListener; +import com.mogo.map.search.IMogoInputtipsSearch; +import com.mogo.map.search.MogoTip; +import com.mogo.map.search.query.MogoInputtipsQuery; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * 高德地图 inputtips搜索实现 + */ +public class InputtipsSearch implements IMogoInputtipsSearch, Inputtips.InputtipsListener { + + private Inputtips mClient; + private InputtipsQuery mQuery; + private IMogoInputtipsListener mListener; + + public InputtipsSearch( Context context, MogoInputtipsQuery query ) { + mQuery = ObjectUtils.fromMogo( query ); + mClient = new Inputtips( context, mQuery ); + mClient.setInputtipsListener( this ); + } + + @Override + public void setQuery( MogoInputtipsQuery query ) { + this.mQuery = ObjectUtils.fromMogo( query ); + } + + @Override + public void setInputtipsListener( IMogoInputtipsListener listener ) { + this.mListener = listener; + } + + @Override + public void requestInputtipsAsyn() { + if ( mClient != null ) { + mClient.requestInputtipsAsyn(); + } + } + + @Override + public List< MogoTip > requestInputtips() throws MogoMapException { + try { + List< Tip > tips = mClient.requestInputtips(); + List< MogoTip > mogoTips = getResult( tips ); + return mogoTips; + } catch ( AMapException e ) { + throw new MogoMapException( e ); + } + } + + @Override + public void onGetInputtips( List< Tip > list, int i ) { + if ( mListener != null ) { + mListener.onGetInputtips( getResult( list ), i ); + } + } + + private List< MogoTip > getResult( List< Tip > tips ) { + List< MogoTip > mogoTips = new ArrayList<>(); + if ( tips != null ) { + for ( Tip tip : tips ) { + MogoTip mogoTip = ObjectUtils.fromAMap( tip ); + if ( mogoTip != null ) { + mogoTips.add( mogoTip ); + } + } + } + return mogoTips; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/amap/utils/ObjectUtils.java b/libraries/mogo-map/src/main/java/com/mogo/map/amap/utils/ObjectUtils.java new file mode 100644 index 0000000000..76222d54f2 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/amap/utils/ObjectUtils.java @@ -0,0 +1,466 @@ +package com.mogo.map.amap.utils; + +import android.graphics.Bitmap; + +import com.amap.api.location.AMapLocation; +import com.amap.api.maps.model.BitmapDescriptor; +import com.amap.api.maps.model.BitmapDescriptorFactory; +import com.amap.api.maps.model.LatLng; +import com.amap.api.maps.model.MarkerOptions; +import com.amap.api.services.core.LatLonPoint; +import com.amap.api.services.core.PoiItem; +import com.amap.api.services.geocoder.AoiItem; +import com.amap.api.services.geocoder.BusinessArea; +import com.amap.api.services.geocoder.GeocodeAddress; +import com.amap.api.services.geocoder.GeocodeQuery; +import com.amap.api.services.geocoder.GeocodeResult; +import com.amap.api.services.geocoder.RegeocodeAddress; +import com.amap.api.services.geocoder.RegeocodeQuery; +import com.amap.api.services.geocoder.RegeocodeResult; +import com.amap.api.services.geocoder.RegeocodeRoad; +import com.amap.api.services.geocoder.StreetNumber; +import com.amap.api.services.help.InputtipsQuery; +import com.amap.api.services.help.Tip; +import com.amap.api.services.poisearch.IndoorData; +import com.amap.api.services.poisearch.Photo; +import com.amap.api.services.poisearch.PoiItemExtension; +import com.amap.api.services.poisearch.SubPoiItem; +import com.amap.api.services.road.Crossroad; +import com.mogo.map.MogoLatLng; +import com.mogo.map.location.MogoLocation; +import com.mogo.map.marker.MogoMarkerOptions; +import com.mogo.map.search.MogoAoiItem; +import com.mogo.map.search.MogoBusinessArea; +import com.mogo.map.search.MogoCrossroad; +import com.mogo.map.search.MogoGeocodeAddress; +import com.mogo.map.search.MogoGeocodeResult; +import com.mogo.map.search.MogoIndoorData; +import com.mogo.map.search.MogoPhoto; +import com.mogo.map.search.MogoPoiItem; +import com.mogo.map.search.MogoPoiItemExtension; +import com.mogo.map.search.MogoRegeocodeAddress; +import com.mogo.map.search.MogoRegeocodeResult; +import com.mogo.map.search.MogoRegeocodeRoad; +import com.mogo.map.search.MogoStreetNumber; +import com.mogo.map.search.MogoSubPoiItem; +import com.mogo.map.search.MogoTip; +import com.mogo.map.search.query.MogoGeocodeQuery; +import com.mogo.map.search.query.MogoInputtipsQuery; +import com.mogo.map.search.query.MogoRegeocodeQuery; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 业务对象和实际对象转换 + */ +public class ObjectUtils { + + public static MarkerOptions fromMogo( MogoMarkerOptions opt ) { + + if ( opt == null ) { + return null; + } + ArrayList< BitmapDescriptor > descriptors = new ArrayList<>(); + final ArrayList< Bitmap > icons = opt.getIcons(); + if ( icons != null && !icons.isEmpty() ) { + for ( Bitmap icon : icons ) { + if ( icon == null || icon.isRecycled() ) { + continue; + } + descriptors.add( BitmapDescriptorFactory.fromBitmap( icon ) ); + } + } + + return new MarkerOptions() + .position( new LatLng( opt.getLatitude(), opt.getLongitude() ) ) + .title( opt.getTitle() ) + .snippet( opt.getSnippet() ) + .icon( BitmapDescriptorFactory.fromBitmap( opt.getIcon() ) ) + .icons( descriptors ) + .period( opt.getPeriod() ) + .rotateAngle( opt.getRotate() ) + .setFlat( opt.isFlat() ) + .visible( opt.isVisible() ) + .infoWindowEnable( opt.isInifoWindowEnable() ) + .alpha( opt.getAlpha() ) + .setGps( opt.isGps() ) + .anchor( opt.getU(), opt.getV() ) + .draggable( opt.isDraggable() ) + .setInfoWindowOffset( opt.getOffsetX(), opt.getOffsetY() ) + .zIndex( opt.getzIndex() ); + } + + + public static MogoLocation fromAMap( AMapLocation aLocation ) { + if ( aLocation == null ) { + return null; + } + MogoLocation location = new MogoLocation(); + location.setLocType( aLocation.getLocationType() ); + location.setSpeed( aLocation.getSpeed() ); + location.setLatitude( aLocation.getLatitude() ); + location.setLongitude( aLocation.getLongitude() ); + location.setAltitude( aLocation.getAltitude() ); + location.setTime( aLocation.getTime() ); + location.setBearing( aLocation.getBearing() ); + location.setAccuracy( aLocation.getAccuracy() ); + location.setCityCode( aLocation.getCityCode() ); + location.setCityName( aLocation.getCity() ); + location.setProvider( aLocation.getProvider() ); + location.setAddress( aLocation.getAddress() ); + location.setDistrict( aLocation.getDistrict() ); + location.setProvince( aLocation.getProvince() ); + location.setAdCode( aLocation.getAdCode() ); + location.setLocationDetail( aLocation.getLocationDetail() ); + location.setPoiName( aLocation.getPoiName() ); + location.setAoiName( aLocation.getAoiName() ); + location.setErrCode( aLocation.getErrorCode() ); + location.setErrInfo( aLocation.getErrorInfo() ); + location.setStreetNum( aLocation.getStreetNum() ); + location.setDescription( aLocation.getDescription() ); + location.setBuildingId( aLocation.getBuildingId() ); + location.setFloor( aLocation.getFloor() ); + location.setGpsAccuracyStatus( aLocation.getGpsAccuracyStatus() ); + location.setSatellite( aLocation.getSatellites() ); + return location; + } + + public static LatLonPoint fromMogo( MogoLatLng latLng ) { + if ( latLng == null ) { + return null; + } + return new LatLonPoint( latLng.lat, latLng.lng ); + } + + public static MogoLatLng fromAMap( LatLonPoint point ) { + if ( point == null ) { + return null; + } + return new MogoLatLng( point.getLatitude(), point.getLongitude() ); + } + + public static GeocodeQuery fromMogo( MogoGeocodeQuery query ) { + if ( query == null ) { + return null; + } + GeocodeQuery q = new GeocodeQuery( query.getLocationName(), query.getCity() ); + return q; + } + + public static RegeocodeQuery fromMogo( MogoRegeocodeQuery query ) { + if ( query == null ) { + return null; + } + RegeocodeQuery q = new RegeocodeQuery( fromMogo( query.getPoint() ), query.getRadius(), query.getLatlngType() ); + return q; + } + + public static MogoAoiItem fromAMap( AoiItem amapItem ) { + if ( amapItem == null ) { + return null; + } + MogoAoiItem mogoAoiItem = new MogoAoiItem(); + mogoAoiItem.setAdCode( amapItem.getAdCode() ); + mogoAoiItem.setAoiArea( amapItem.getAoiArea() ); + mogoAoiItem.setAoiCenterPoint( fromAMap( amapItem.getAoiCenterPoint() ) ); + mogoAoiItem.setAoiId( amapItem.getAoiId() ); + mogoAoiItem.setAoiName( amapItem.getAoiName() ); + return mogoAoiItem; + } + + public static MogoBusinessArea fromAMap( BusinessArea amapItem ) { + if ( amapItem == null ) { + return null; + } + MogoBusinessArea mogoBusinessArea = new MogoBusinessArea(); + mogoBusinessArea.setCenterPoint( fromAMap( amapItem.getCenterPoint() ) ); + mogoBusinessArea.setName( amapItem.getName() ); + return mogoBusinessArea; + } + + + public static MogoCrossroad fromAMap( Crossroad amapItem ) { + if ( amapItem == null ) { + return null; + } + MogoCrossroad mogoCrossroad = new MogoCrossroad(); + mogoCrossroad.setDirection( amapItem.getDirection() ); + mogoCrossroad.setDistance( amapItem.getDistance() ); + mogoCrossroad.setFirstRoadId( amapItem.getFirstRoadId() ); + mogoCrossroad.setFirstRoadName( amapItem.getFirstRoadName() ); + mogoCrossroad.setSecondRoadId( amapItem.getSecondRoadId() ); + mogoCrossroad.setSecondRoadName( amapItem.getSecondRoadName() ); + return mogoCrossroad; + } + + public static MogoGeocodeAddress fromAMap( GeocodeAddress address ) { + if ( address == null ) { + return null; + } + MogoGeocodeAddress mogoGeocodeAddress = new MogoGeocodeAddress(); + mogoGeocodeAddress.setAdcode( address.getAdcode() ); + mogoGeocodeAddress.setBuilding( address.getBuilding() ); + mogoGeocodeAddress.setCity( address.getCity() ); + mogoGeocodeAddress.setDistrict( address.getDistrict() ); + mogoGeocodeAddress.setFormatAddress( address.getFormatAddress() ); + mogoGeocodeAddress.setLatlng( fromAMap( address.getLatLonPoint() ) ); + mogoGeocodeAddress.setLevel( address.getLevel() ); + mogoGeocodeAddress.setNeighborhood( address.getNeighborhood() ); + mogoGeocodeAddress.setProvince( address.getProvince() ); + mogoGeocodeAddress.setTownship( address.getTownship() ); + return mogoGeocodeAddress; + } + + public static MogoGeocodeResult fromAMap( GeocodeResult result ) { + if ( result == null || result.getGeocodeAddressList() != null ) { + return null; + } + MogoGeocodeResult mogoGeocodeResult = new MogoGeocodeResult(); + final List< MogoGeocodeAddress > addresses = new ArrayList<>(); + for ( GeocodeAddress geocodeAddress : result.getGeocodeAddressList() ) { + final MogoGeocodeAddress mogoGeocodeAddress = fromAMap( geocodeAddress ); + if ( mogoGeocodeAddress != null ) { + addresses.add( mogoGeocodeAddress ); + } + } + + mogoGeocodeResult.setAddresses( addresses ); + return mogoGeocodeResult; + } + + public static MogoIndoorData fromAMap( IndoorData data ) { + if ( data == null ) { + return null; + } + MogoIndoorData mogoIndoorData = new MogoIndoorData(); + mogoIndoorData.setFloor( data.getFloor() ); + mogoIndoorData.setFloorName( data.getFloorName() ); + mogoIndoorData.setPoiId( data.getPoiId() ); + return mogoIndoorData; + } + + public static MogoPhoto fromAMap( Photo photo ) { + if ( photo == null ) { + return null; + } + MogoPhoto mogoPhoto = new MogoPhoto(); + mogoPhoto.setTitle( photo.getTitle() ); + mogoPhoto.setUrl( photo.getUrl() ); + return mogoPhoto; + } + + public static MogoPoiItemExtension fromAMap( PoiItemExtension poiItemExtension ) { + if ( poiItemExtension == null ) { + return null; + } + MogoPoiItemExtension mogoPoiItemExtension = new MogoPoiItemExtension(); + mogoPoiItemExtension.setOpentime( poiItemExtension.getOpentime() ); + mogoPoiItemExtension.setRating( poiItemExtension.getmRating() ); + return mogoPoiItemExtension; + } + + public static MogoRegeocodeRoad fromAMap( RegeocodeRoad regeocodeRoad ) { + if ( regeocodeRoad == null ) { + return null; + } + MogoRegeocodeRoad mogoRegeocodeRoad = new MogoRegeocodeRoad(); + mogoRegeocodeRoad.setDirection( regeocodeRoad.getDirection() ); + mogoRegeocodeRoad.setDistance( regeocodeRoad.getDistance() ); + mogoRegeocodeRoad.setId( regeocodeRoad.getId() ); + mogoRegeocodeRoad.setName( regeocodeRoad.getName() ); + mogoRegeocodeRoad.setPoint( fromAMap( regeocodeRoad.getLatLngPoint() ) ); + return mogoRegeocodeRoad; + } + + public static MogoStreetNumber fromAMap( StreetNumber streetNumber ) { + if ( streetNumber == null ) { + return null; + } + MogoStreetNumber mogoStreetNumber = new MogoStreetNumber(); + mogoStreetNumber.setDirection( streetNumber.getDirection() ); + mogoStreetNumber.setDistance( streetNumber.getDistance() ); + mogoStreetNumber.setLatLonPoint( fromAMap( streetNumber.getLatLonPoint() ) ); + mogoStreetNumber.setNumber( streetNumber.getNumber() ); + mogoStreetNumber.setStreet( streetNumber.getStreet() ); + return mogoStreetNumber; + } + + public static MogoSubPoiItem fromAMap( SubPoiItem subPoiItem ) { + if ( subPoiItem == null ) { + return null; + } + MogoSubPoiItem mogoSubPoiItem = new MogoSubPoiItem(); + mogoSubPoiItem.setDistance( subPoiItem.getDistance() ); + mogoSubPoiItem.setPoiId( subPoiItem.getPoiId() ); + mogoSubPoiItem.setPoint( fromAMap( subPoiItem.getLatLonPoint() ) ); + mogoSubPoiItem.setSnippet( subPoiItem.getSnippet() ); + mogoSubPoiItem.setSubName( mogoSubPoiItem.getSubName() ); + mogoSubPoiItem.setSubTypeDes( mogoSubPoiItem.getSubTypeDes() ); + mogoSubPoiItem.setTitle( mogoSubPoiItem.getTitle() ); + return mogoSubPoiItem; + } + + public static MogoPoiItem fromAMap( PoiItem poiItem ) { + if ( poiItem == null ) { + return null; + } + MogoPoiItem mogoPoiItem = new MogoPoiItem(); + mogoPoiItem.setAdCode( poiItem.getAdCode() ); + mogoPoiItem.setAdName( poiItem.getAdName() ); + mogoPoiItem.setBusinessArea( poiItem.getBusinessArea() ); + mogoPoiItem.setCityCode( poiItem.getCityCode() ); + mogoPoiItem.setCityName( poiItem.getCityName() ); + mogoPoiItem.setDirection( poiItem.getDirection() ); + mogoPoiItem.setDistance( poiItem.getDistance() ); + mogoPoiItem.setEmail( poiItem.getEmail() ); + mogoPoiItem.setEnter( fromAMap( poiItem.getEnter() ) ); + mogoPoiItem.setExit( fromAMap( poiItem.getExit() ) ); + mogoPoiItem.setIndoorData( fromAMap( poiItem.getIndoorData() ) ); + mogoPoiItem.setParkingType( poiItem.getParkingType() ); + mogoPoiItem.setIndoorMap( poiItem.isIndoorMap() ); + if ( poiItem.getPhotos() != null ) { + List< MogoPhoto > mogoPhotos = new ArrayList<>(); + for ( Photo photo : poiItem.getPhotos() ) { + MogoPhoto mogoPhoto = fromAMap( photo ); + if ( mogoPhoto != null ) { + mogoPhotos.add( mogoPhoto ); + } + } + mogoPoiItem.setPhotos( mogoPhotos ); + } + mogoPoiItem.setPoiExtension( fromAMap( poiItem.getPoiExtension() ) ); + mogoPoiItem.setPoiId( poiItem.getPoiId() ); + mogoPoiItem.setPoint( fromAMap( poiItem.getLatLonPoint() ) ); + mogoPoiItem.setPostcode( poiItem.getPostcode() ); + mogoPoiItem.setProvinceCode( poiItem.getProvinceCode() ); + mogoPoiItem.setProvinceName( poiItem.getProvinceName() ); + mogoPoiItem.setShopID( poiItem.getShopID() ); + mogoPoiItem.setSnippet( poiItem.getSnippet() ); + if ( poiItem.getSubPois() != null ) { + List< MogoSubPoiItem > mogoSubPoiItems = new ArrayList<>(); + for ( SubPoiItem subPois : poiItem.getSubPois() ) { + MogoSubPoiItem mogoSubPoiItem = fromAMap( subPois ); + if ( mogoPoiItem != null ) { + mogoSubPoiItems.add( mogoSubPoiItem ); + } + } + mogoPoiItem.setSubPois( mogoSubPoiItems ); + } + mogoPoiItem.setTel( poiItem.getTel() ); + mogoPoiItem.setTypeCode( poiItem.getTypeCode() ); + mogoPoiItem.setTitle( poiItem.getTitle() ); + mogoPoiItem.setTypeDes( poiItem.getTypeDes() ); + mogoPoiItem.setWebsite( poiItem.getWebsite() ); + return mogoPoiItem; + } + + public static MogoRegeocodeAddress fromAMap( RegeocodeAddress regeocodeAddress ) { + if ( regeocodeAddress == null ) { + return null; + } + MogoRegeocodeAddress mogoRegeocodeAddress = new MogoRegeocodeAddress(); + mogoRegeocodeAddress.setAdCode( regeocodeAddress.getAdCode() ); + if ( regeocodeAddress.getAois() != null ) { + List< MogoAoiItem > items = new ArrayList<>(); + for ( AoiItem aois : regeocodeAddress.getAois() ) { + final MogoAoiItem mogoAoiItem = fromAMap( aois ); + if ( mogoAoiItem != null ) { + items.add( mogoAoiItem ); + } + } + mogoRegeocodeAddress.setAois( items ); + } + + mogoRegeocodeAddress.setBuilding( regeocodeAddress.getBuilding() ); + if ( regeocodeAddress.getBusinessAreas() != null ) { + List< MogoBusinessArea > mogoBusinessAreas = new ArrayList<>(); + for ( BusinessArea businessArea : regeocodeAddress.getBusinessAreas() ) { + MogoBusinessArea mogoBusinessArea = fromAMap( businessArea ); + if ( mogoBusinessArea != null ) { + mogoBusinessAreas.add( mogoBusinessArea ); + } + } + mogoRegeocodeAddress.setBusinessAreas( mogoBusinessAreas ); + } + + mogoRegeocodeAddress.setCity( regeocodeAddress.getCity() ); + mogoRegeocodeAddress.setCityCode( regeocodeAddress.getCityCode() ); + mogoRegeocodeAddress.setCountry( regeocodeAddress.getCountry() ); + if ( regeocodeAddress.getCrossroads() != null ) { + List< MogoCrossroad > mogoCrossroads = new ArrayList<>(); + for ( Crossroad crossroad : regeocodeAddress.getCrossroads() ) { + + MogoCrossroad mogoCrossroad = fromAMap( crossroad ); + if ( mogoCrossroad != null ) { + mogoCrossroads.add( mogoCrossroad ); + } + } + mogoRegeocodeAddress.setCrossroads( mogoCrossroads ); + } + mogoRegeocodeAddress.setDistrict( regeocodeAddress.getDistrict() ); + mogoRegeocodeAddress.setFormatAddress( regeocodeAddress.getFormatAddress() ); + mogoRegeocodeAddress.setNeighborhood( regeocodeAddress.getNeighborhood() ); + if ( regeocodeAddress.getPois() != null ) { + List< MogoPoiItem > mogoPoiItems = new ArrayList<>(); + for ( PoiItem pois : regeocodeAddress.getPois() ) { + MogoPoiItem mogoPoiItem = fromAMap( pois ); + mogoPoiItems.add( mogoPoiItem ); + } + mogoRegeocodeAddress.setPois( mogoPoiItems ); + } + mogoRegeocodeAddress.setProvince( regeocodeAddress.getProvince() ); + if ( regeocodeAddress.getRoads() != null ) { + List< MogoRegeocodeRoad > mogoRegeocodeRoads = new ArrayList<>(); + for ( RegeocodeRoad road : regeocodeAddress.getRoads() ) { + MogoRegeocodeRoad mogoRegeocodeRoad = fromAMap( road ); + if ( mogoRegeocodeRoad != null ) { + mogoRegeocodeRoads.add( mogoRegeocodeRoad ); + } + } + mogoRegeocodeAddress.setRoads( mogoRegeocodeRoads ); + } + mogoRegeocodeAddress.setStreetNumber( fromAMap( regeocodeAddress.getStreetNumber() ) ); + mogoRegeocodeAddress.setTowncode( regeocodeAddress.getTowncode() ); + mogoRegeocodeAddress.setTownship( regeocodeAddress.getTownship() ); + return mogoRegeocodeAddress; + } + + public static MogoRegeocodeResult fromAMap( RegeocodeResult regeocodeResult ) { + if ( regeocodeResult == null ) { + return null; + } + MogoRegeocodeResult mogoRegeocodeResult = new MogoRegeocodeResult(); + mogoRegeocodeResult.setRegeocodeAddress( fromAMap( regeocodeResult.getRegeocodeAddress() ) ); + return mogoRegeocodeResult; + } + + public static InputtipsQuery fromMogo( MogoInputtipsQuery query ) { + if ( query == null ) { + return null; + } + InputtipsQuery inputtipsQuery = new InputtipsQuery( query.getKeyword(), query.getCity() ); + inputtipsQuery.setCityLimit( query.isCityLimit() ); + inputtipsQuery.setLocation( fromMogo( query.getLocation() ) ); + inputtipsQuery.setType( query.getType() ); + return inputtipsQuery; + } + + public static MogoTip fromAMap( Tip tip ) { + if ( tip == null ) { + return null; + } + MogoTip mogoTip = new MogoTip(); + mogoTip.setAdCode( tip.getAdcode() ); + mogoTip.setAddress( tip.getAddress() ); + mogoTip.setDistrict( tip.getDistrict() ); + mogoTip.setName( tip.getName() ); + mogoTip.setPoiID( tip.getPoiID() ); + mogoTip.setPoint( fromAMap( tip.getPoint() ) ); + mogoTip.setTypeCode( tip.getTypeCode() ); + return mogoTip; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/exception/MogoMapException.java b/libraries/mogo-map/src/main/java/com/mogo/map/exception/MogoMapException.java new file mode 100644 index 0000000000..1c85abbecc --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/exception/MogoMapException.java @@ -0,0 +1,25 @@ +package com.mogo.map.exception; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 异常 + */ +public class MogoMapException extends Exception { + + public MogoMapException() { + } + + public MogoMapException( String message ) { + super( message ); + } + + public MogoMapException( String message, Throwable cause ) { + super( message, cause ); + } + + public MogoMapException( Throwable cause ) { + super( cause ); + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/location/ILocationListener.java b/libraries/mogo-map/src/main/java/com/mogo/map/location/ILocationListener.java new file mode 100644 index 0000000000..7069166e20 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/location/ILocationListener.java @@ -0,0 +1,17 @@ +package com.mogo.map.location; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 定位回调 + */ +public interface ILocationListener { + + /** + * 定位发生改变 + * + * @param location 新定位点 + */ + void onLocationChanged( MogoLocation location ); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/location/IMogoLocationClient.java b/libraries/mogo-map/src/main/java/com/mogo/map/location/IMogoLocationClient.java new file mode 100644 index 0000000000..ea24dac392 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/location/IMogoLocationClient.java @@ -0,0 +1,43 @@ +package com.mogo.map.location; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 定位接口 + */ +public interface IMogoLocationClient { + + /** + * 开始定位 + */ + void start(); + + /** + * 开始定位 + * + * @param interval 默认定位间隔 + */ + void start( long interval ); + + /** + * 停止定位 + */ + void stop(); + + /** + * 注册定位回调 + * + * @param listener + */ + void addLocationListener( ILocationListener listener ); + + /** + * 注销定位回调 + * + * @param listener + */ + void removeLocationListener( ILocationListener listener ); + + MogoLocation getLastKnowLocation(); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/location/MogoLocation.java b/libraries/mogo-map/src/main/java/com/mogo/map/location/MogoLocation.java new file mode 100644 index 0000000000..f41f924c0b --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/location/MogoLocation.java @@ -0,0 +1,362 @@ +package com.mogo.map.location; + +import android.os.Parcel; +import android.os.Parcelable; + +public class MogoLocation implements Cloneable, Parcelable { + private int locType = 0; + private double latitude = 0; + private double longitude = 0; + private double altitude = 0; + private long time = 0; + private float bearing = 0; + private float accuracy = 0; + private float speed = 0; + private String cityName = ""; + private String cityCode = ""; + private String provider = ""; + private String address = ""; + private String district = ""; + private String province = ""; + private String adCode = ""; + private String locationDetail = ""; + private String poiName = ""; + private String aoiName = ""; + private int errCode = 0; + private String errInfo = ""; + private String streetNum = ""; + private String description = ""; + private String buildingId = ""; + private String floor = ""; + private int gpsAccuracyStatus = 0; + private int satellite = 0; + + public float getBearing() { + return bearing; + } + + public void setBearing( float bearing ) { + this.bearing = bearing; + } + + public float getAccuracy() { + return accuracy; + } + + public void setAccuracy( float accuracy ) { + this.accuracy = accuracy; + } + + public String getProvider() { + return provider; + } + + public void setProvider( String provider ) { + this.provider = provider; + } + + public float getSpeed() { + return speed; + } + + public void setSpeed( float speed ) { + this.speed = speed; + } + + public String getLocationDetail() { + return locationDetail; + } + + public void setLocationDetail( String locationDetail ) { + this.locationDetail = locationDetail; + } + + public String getPoiName() { + return poiName; + } + + public void setPoiName( String poiName ) { + this.poiName = poiName; + } + + public String getAoiName() { + return aoiName; + } + + public void setAoiName( String aoiName ) { + this.aoiName = aoiName; + } + + public int getErrCode() { + return errCode; + } + + public void setErrCode( int errCode ) { + this.errCode = errCode; + } + + public String getErrInfo() { + return errInfo; + } + + public void setErrInfo( String errInfo ) { + this.errInfo = errInfo; + } + + public String getStreetNum() { + return streetNum; + } + + public void setStreetNum( String streetNum ) { + this.streetNum = streetNum; + } + + public String getDescription() { + return description; + } + + public void setDescription( String description ) { + this.description = description; + } + + public String getBuildingId() { + return buildingId; + } + + public void setBuildingId( String buildingId ) { + this.buildingId = buildingId; + } + + public String getFloor() { + return floor; + } + + public void setFloor( String floor ) { + this.floor = floor; + } + + public int getGpsAccuracyStatus() { + return gpsAccuracyStatus; + } + + public void setGpsAccuracyStatus( int gpsAccuracyStatus ) { + this.gpsAccuracyStatus = gpsAccuracyStatus; + } + + public int getSatellite() { + return satellite; + } + + public void setSatellite( int satellite ) { + this.satellite = satellite; + } + + public MogoLocation() { + } + + @Override + public String toString() { + return "MogoLocation{" + + "locType=" + locType + + ", latitude=" + latitude + + ", longitude=" + longitude + + ", altitude=" + altitude + + ", time=" + time + + ", bearing=" + bearing + + ", accuracy=" + accuracy + + ", speed=" + speed + + ", cityName='" + cityName + '\'' + + ", cityCode='" + cityCode + '\'' + + ", provider='" + provider + '\'' + + ", address='" + address + '\'' + + ", district='" + district + '\'' + + ", province='" + province + '\'' + + ", adCode='" + adCode + '\'' + + ", locationDetail='" + locationDetail + '\'' + + ", poiName='" + poiName + '\'' + + ", aoiName='" + aoiName + '\'' + + ", errCode=" + errCode + + ", errInfo='" + errInfo + '\'' + + ", streetNum='" + streetNum + '\'' + + ", description='" + description + '\'' + + ", buildingId='" + buildingId + '\'' + + ", floor='" + floor + '\'' + + ", gpsAccuracyStatus=" + gpsAccuracyStatus + + ", satellite=" + satellite + + '}'; + } + + public double getAltitude() { + return altitude; + } + + public void setAltitude( double altitude ) { + this.altitude = altitude; + } + + public String getAddress() { + return address; + } + + public void setAddress( String address ) { + this.address = address; + } + + public String getDistrict() { + return district; + } + + public void setDistrict( String district ) { + this.district = district; + } + + public String getProvince() { + return province; + } + + public void setProvince( String province ) { + this.province = province; + } + + public String getAdCode() { + return adCode; + } + + public void setAdCode( String adCode ) { + this.adCode = adCode; + } + + public int getLocType() { + return locType; + } + + public void setLocType( int locType ) { + this.locType = locType; + } + + public double getLatitude() { + return latitude; + } + + public void setLatitude( double latitude ) { + this.latitude = latitude; + } + + public double getLongitude() { + return longitude; + } + + public void setLongitude( double longitude ) { + this.longitude = longitude; + } + + public long getTime() { + return time; + } + + public void setTime( long time ) { + this.time = time; + } + + public String getCityCode() { + return cityCode; + } + + public void setCityCode( String cityCode ) { + this.cityCode = cityCode; + } + + public String getCityName() { + return cityName; + } + + public void setCityName( String cityName ) { + this.cityName = cityName; + } + + @Override + public MogoLocation clone() { + try { + return ( MogoLocation ) super.clone(); + } catch ( CloneNotSupportedException e ) { + e.printStackTrace(); + } + return this; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeInt( this.locType ); + dest.writeDouble( this.latitude ); + dest.writeDouble( this.longitude ); + dest.writeDouble( this.altitude ); + dest.writeLong( this.time ); + dest.writeFloat( this.bearing ); + dest.writeFloat( this.accuracy ); + dest.writeFloat( this.speed ); + dest.writeString( this.cityName ); + dest.writeString( this.cityCode ); + dest.writeString( this.provider ); + dest.writeString( this.address ); + dest.writeString( this.district ); + dest.writeString( this.province ); + dest.writeString( this.adCode ); + dest.writeString( this.locationDetail ); + dest.writeString( this.poiName ); + dest.writeString( this.aoiName ); + dest.writeInt( this.errCode ); + dest.writeString( this.errInfo ); + dest.writeString( this.streetNum ); + dest.writeString( this.description ); + dest.writeString( this.buildingId ); + dest.writeString( this.floor ); + dest.writeInt( this.gpsAccuracyStatus ); + dest.writeInt( this.satellite ); + } + + protected MogoLocation( Parcel in ) { + this.locType = in.readInt(); + this.latitude = in.readDouble(); + this.longitude = in.readDouble(); + this.altitude = in.readDouble(); + this.time = in.readLong(); + this.bearing = in.readFloat(); + this.accuracy = in.readFloat(); + this.speed = in.readFloat(); + this.cityName = in.readString(); + this.cityCode = in.readString(); + this.provider = in.readString(); + this.address = in.readString(); + this.district = in.readString(); + this.province = in.readString(); + this.adCode = in.readString(); + this.locationDetail = in.readString(); + this.poiName = in.readString(); + this.aoiName = in.readString(); + this.errCode = in.readInt(); + this.errInfo = in.readString(); + this.streetNum = in.readString(); + this.description = in.readString(); + this.buildingId = in.readString(); + this.floor = in.readString(); + this.gpsAccuracyStatus = in.readInt(); + this.satellite = in.readInt(); + } + + public static final Parcelable.Creator< MogoLocation > CREATOR = new Parcelable.Creator< MogoLocation >() { + @Override + public MogoLocation createFromParcel( Parcel source ) { + return new MogoLocation( source ); + } + + @Override + public MogoLocation[] newArray( int size ) { + return new MogoLocation[size]; + } + }; +} \ No newline at end of file diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/location/MogoLocationClient.java b/libraries/mogo-map/src/main/java/com/mogo/map/location/MogoLocationClient.java new file mode 100644 index 0000000000..181444ada4 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/location/MogoLocationClient.java @@ -0,0 +1,75 @@ +package com.mogo.map.location; + +import android.content.Context; + +import com.mogo.map.amap.location.LocationClient; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ */ +public class MogoLocationClient implements IMogoLocationClient { + + private static volatile MogoLocationClient sInstance; + + private MogoLocationClient( Context context ) { + mDelegate = LocationClient.getInstance( context ); + } + + public static MogoLocationClient getInstance( Context context ) { + if ( sInstance == null ) { + synchronized ( MogoLocationClient.class ) { + if ( sInstance == null ) { + sInstance = new MogoLocationClient( context ); + } + } + } + return sInstance; + } + + private IMogoLocationClient mDelegate; + + @Override + public void start() { + if ( mDelegate != null ) { + mDelegate.start(); + } + } + + @Override + public void start( long interval ) { + if ( mDelegate != null ) { + mDelegate.start( interval ); + } + } + + @Override + public void stop() { + if ( mDelegate != null ) { + mDelegate.stop(); + } + } + + @Override + public void addLocationListener( ILocationListener listener ) { + if ( mDelegate != null ) { + mDelegate.addLocationListener( listener ); + } + } + + @Override + public void removeLocationListener( ILocationListener listener ) { + if ( mDelegate != null ) { + mDelegate.removeLocationListener( listener ); + } + } + + @Override + public MogoLocation getLastKnowLocation() { + if ( mDelegate != null ) { + return mDelegate.getLastKnowLocation(); + } + return null; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/marker/IMogoMarker.java b/libraries/mogo-map/src/main/java/com/mogo/map/marker/IMogoMarker.java new file mode 100644 index 0000000000..b6ba461919 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/marker/IMogoMarker.java @@ -0,0 +1,136 @@ +package com.mogo.map.marker; + +import android.graphics.Bitmap; + +import java.util.ArrayList; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 地图 marker 抽象 + */ +public interface IMogoMarker { + + /** + * 删除当前marker并销毁Marker的图片等资源 + */ + void destroy(); + + /** + * 删除当前marker。 + */ + void remove(); + + /** + * 隐藏Marker覆盖物的信息窗口。 + */ + void hideInfoWindow(); + + /** + * 设置Marker覆盖物的透明度 + * + * @param alpha + */ + void setAlpha( float alpha ); + + /** + * 设置Marker覆盖物的锚点比例。 + * + * @param anchorU + * @param anchorV + */ + void setAnchor( float anchorU, float anchorV ); + + /** + * 设置Marker覆盖物是否允许拖拽。 + * + * @param paramBoolean + */ + void setDraggable( boolean paramBoolean ); + + /** + * 设置 Marker覆盖物的图标 + * + * @param bitmap + */ + void setIcon( Bitmap bitmap ); + + /** + * 设置 Marker 的图标集合,相同图案的 icon 的 marker 最好使用同一个 BitmapDescriptor 对象以节省内存空间。 + * + * @param icons + */ + void setIcons( ArrayList< Bitmap > icons ); + + /** + * 设置Marker覆盖物的InfoWindow是否允许显示,默认为true + * 设置为false之后, 调用Marker.showInfoWindow() 将不会生效 + * + * @param enabled + */ + void setInfoWindowEnable( boolean enabled ); + + /** + * 设置Marker覆盖物的属性选项类 通过markerOption 给marker设置属性 + * + * @param opt + */ + void setMarkerOptions( MogoMarkerOptions opt ); + + void setObject( Object object ); + + /** + * 设置多少帧刷新一次图片资源,Marker动画的间隔时间,值越小动画越快。 + * + * @param period + */ + void setPeriod( int period ); + + void setPosition( double lat, double lng ); + + /** + * 设置Marker覆盖物图片旋转的角度,从正北开始,逆时针计算。 + * + * @param rotate + */ + void setRotateAngle( float rotate ); + + /** + * 设置Marker 覆盖物的文字片段。 + * + * @param snippet + */ + void setSnippet( String snippet ); + + /** + * 设置Marker 覆盖物的标题。 + * + * @param title + */ + void setTitle( String title ); + + /** + * 设置当前marker在最上面。 + */ + void setToTop(); + + /** + * 设置 Marker 覆盖物的可见属性。 + * + * @param visible + */ + void setVisible( boolean visible ); + + /** + * 设置Marker覆盖物的z轴值。 + * + * @param zIndex + */ + void setZIndex( float zIndex ); + + /** + * 显示 Marker 覆盖物的信息窗口。 + */ + void showInfoWindow(); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/marker/MogoMarker.java b/libraries/mogo-map/src/main/java/com/mogo/map/marker/MogoMarker.java new file mode 100644 index 0000000000..978642da00 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/marker/MogoMarker.java @@ -0,0 +1,10 @@ +package com.mogo.map.marker; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * 描述 + */ +public class MogoMarker { +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/marker/MogoMarkerOptions.java b/libraries/mogo-map/src/main/java/com/mogo/map/marker/MogoMarkerOptions.java new file mode 100644 index 0000000000..7b55d96847 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/marker/MogoMarkerOptions.java @@ -0,0 +1,222 @@ +package com.mogo.map.marker; + +import android.graphics.Bitmap; + +import java.util.ArrayList; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 地图marker + */ +public class MogoMarkerOptions { + + private double latitude; + private double longitude; + // 设置 Marker覆盖物 的标题 + private String title; + // 设置 Marker覆盖物的 文字描述 + private String snippet; + // 设置Marker覆盖物的图标。 + private Bitmap icon; + // 设置Marker覆盖物的动画帧图标列表,多张图片模拟gif的效果。 + private ArrayList< Bitmap > icons; + // 设置多少帧刷新一次图片资源,Marker动画的间隔时间,值越小动画越快。 + private int period = 20; + // 设置Marker覆盖物的图片旋转角度,从正北开始,逆时针计算。 + private float rotate; + // 设置Marker覆盖物是否平贴地图。 + private boolean flat = false; + // 设置Marker覆盖物是否可见。 + private boolean visible = true; + // 设置Marker覆盖物的InfoWindow是否允许显示 + private boolean inifoWindowEnable = true; + // 设置Marker覆盖物的透明度 + private float alpha = 1.0f; + + // Marker覆盖物的坐标是否是Gps + private boolean isGps = false; + + // Marker覆盖物锚点在水平范围的比例。 + private float u = 0.5f; + // Marker覆盖物锚点垂直范围的比例。 + private float v = 0.5f; + + // 设置Marker覆盖物是否可拖拽。 + private boolean draggable = false; + + // Marker覆盖物的InfoWindow相对X 轴的Marker的偏移 + private int offsetX = 0; + // Marker覆盖物的InfoWindow相对Y 轴的Marker的偏移 + private int offsetY = 0; + + // 设置Marker覆盖物 zIndex。 + private float zIndex = 0.0f; + + public MogoMarkerOptions latitude( double latitude ) { + this.latitude = latitude; + return this; + } + + public MogoMarkerOptions longitude( double longitude ) { + this.longitude = longitude; + return this; + } + + public MogoMarkerOptions title( String title ) { + this.title = title; + return this; + } + + public MogoMarkerOptions snippet( String snippet ) { + this.snippet = snippet; + return this; + } + + public MogoMarkerOptions icon( Bitmap icon ) { + this.icon = icon; + return this; + } + + public MogoMarkerOptions icons( ArrayList< Bitmap > icons ) { + this.icons = icons; + return this; + } + + public MogoMarkerOptions period( int period ) { + this.period = period; + if ( this.period < 1 ) { + this.period = 1; + } + return this; + } + + public MogoMarkerOptions rotate( float rotate ) { + this.rotate = rotate; + return this; + } + + public MogoMarkerOptions flat( boolean flat ) { + this.flat = flat; + return this; + } + + public MogoMarkerOptions visible( boolean visible ) { + this.visible = visible; + return this; + } + + public MogoMarkerOptions inifoWindowEnable( boolean inifoWindowEnable ) { + this.inifoWindowEnable = inifoWindowEnable; + return this; + } + + public MogoMarkerOptions alpha( float alpha ) { + this.alpha = alpha; + return this; + } + + public MogoMarkerOptions gps( boolean gps ) { + isGps = gps; + return this; + } + + public MogoMarkerOptions anchor( float u, float v ) { + this.u = u; + this.v = v; + return this; + } + + public MogoMarkerOptions draggable( boolean draggable ) { + this.draggable = draggable; + return this; + } + + public MogoMarkerOptions setInfoWindowOffset( int offsetX, int offsetY ) { + this.offsetX = offsetX; + this.offsetY = offsetY; + return this; + } + + public MogoMarkerOptions zIndex( float zIndex ) { + this.zIndex = zIndex; + return this; + } + + public double getLatitude() { + return latitude; + } + + public double getLongitude() { + return longitude; + } + + public String getTitle() { + return title; + } + + public String getSnippet() { + return snippet; + } + + public Bitmap getIcon() { + return icon; + } + + public ArrayList< Bitmap > getIcons() { + return icons; + } + + public int getPeriod() { + return period; + } + + public float getRotate() { + return rotate; + } + + public boolean isFlat() { + return flat; + } + + public boolean isVisible() { + return visible; + } + + public boolean isInifoWindowEnable() { + return inifoWindowEnable; + } + + public float getAlpha() { + return alpha; + } + + public boolean isGps() { + return isGps; + } + + public float getU() { + return u; + } + + public float getV() { + return v; + } + + public boolean isDraggable() { + return draggable; + } + + public int getOffsetX() { + return offsetX; + } + + public int getOffsetY() { + return offsetY; + } + + public float getzIndex() { + return zIndex; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoGeoSearch.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoGeoSearch.java new file mode 100644 index 0000000000..1a0ec205d3 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoGeoSearch.java @@ -0,0 +1,55 @@ +package com.mogo.map.search; + +import com.mogo.map.exception.MogoMapException; +import com.mogo.map.search.query.MogoGeocodeQuery; +import com.mogo.map.search.query.MogoRegeocodeQuery; + +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 地理/逆地理位置搜索 + */ +public interface IMogoGeoSearch { + + /** + * 添加异步编码回调 + * + * @param listener + */ + void setGeoSearchListener( IMogoGeoSearchListener listener ); + + /** + * 同步获取逆地理编码地址 + * + * @param query + * @return + * @throws MogoMapException + */ + MogoRegeocodeAddress getFromLocation( MogoRegeocodeQuery query ) throws MogoMapException; + + /** + * 同步获取地理编码地址列表 + * + * @param query + * @return + * @throws MogoMapException + */ + List< MogoGeocodeAddress > getFromLocationName( MogoGeocodeQuery query ) throws MogoMapException; + + /** + * 异步获取逆地理编码 + * + * @param query + */ + void getFromLocationAsyn( MogoRegeocodeQuery query ); + + /** + * 同步获取地理编码回调 + * + * @param query + */ + void getFromLocationNameAsyn( MogoGeocodeQuery query ); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoGeoSearchListener.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoGeoSearchListener.java new file mode 100644 index 0000000000..ae1689b236 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoGeoSearchListener.java @@ -0,0 +1,26 @@ +package com.mogo.map.search; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 逆地理/地理编码回调 + */ +public interface IMogoGeoSearchListener { + + /** + * 逆地理编码(根据经纬度获取地理位置信息) + * + * @param regeocodeResult + * @param rCode + */ + void onRegeocodeSearched( MogoRegeocodeResult regeocodeResult, int rCode ); + + /** + * 根据名称和城市获取地理位置信息 + * + * @param geocodeResult + * @param i + */ + void onGeocodeSearched( MogoGeocodeResult geocodeResult, int i ); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoInputtipsListener.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoInputtipsListener.java new file mode 100644 index 0000000000..1cba147ba2 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoInputtipsListener.java @@ -0,0 +1,14 @@ +package com.mogo.map.search; + +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * 描述 + */ +public interface IMogoInputtipsListener { + + void onGetInputtips( List< MogoTip > result, int code ); +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoInputtipsSearch.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoInputtipsSearch.java new file mode 100644 index 0000000000..0d7701e90d --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/IMogoInputtipsSearch.java @@ -0,0 +1,24 @@ +package com.mogo.map.search; + +import com.mogo.map.exception.MogoMapException; +import com.mogo.map.search.query.MogoInputtipsQuery; + +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * 描述 + */ +public interface IMogoInputtipsSearch { + + void setQuery( MogoInputtipsQuery query ); + + void setInputtipsListener( IMogoInputtipsListener listener ); + + void requestInputtipsAsyn(); + + List< MogoTip > requestInputtips() throws MogoMapException; +} + diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoAoiItem.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoAoiItem.java new file mode 100644 index 0000000000..e02c987202 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoAoiItem.java @@ -0,0 +1,99 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoAoiItem implements Parcelable { + + private String aoiId; + private String aoiName; + private String adCode; + private MogoLatLng aoiCenterPoint; + private float aoiArea; + + public String getAoiId() { + return aoiId; + } + + public void setAoiId( String aoiId ) { + this.aoiId = aoiId; + } + + public String getAoiName() { + return aoiName; + } + + public void setAoiName( String aoiName ) { + this.aoiName = aoiName; + } + + public String getAdCode() { + return adCode; + } + + public void setAdCode( String adCode ) { + this.adCode = adCode; + } + + public MogoLatLng getAoiCenterPoint() { + return aoiCenterPoint; + } + + public void setAoiCenterPoint( MogoLatLng aoiCenterPoint ) { + this.aoiCenterPoint = aoiCenterPoint; + } + + public float getAoiArea() { + return aoiArea; + } + + public void setAoiArea( float aoiArea ) { + this.aoiArea = aoiArea; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.aoiId ); + dest.writeString( this.aoiName ); + dest.writeString( this.adCode ); + dest.writeParcelable( this.aoiCenterPoint, flags ); + dest.writeFloat( this.aoiArea ); + } + + public MogoAoiItem() { + } + + protected MogoAoiItem( Parcel in ) { + this.aoiId = in.readString(); + this.aoiName = in.readString(); + this.adCode = in.readString(); + this.aoiCenterPoint = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.aoiArea = in.readFloat(); + } + + public static final Parcelable.Creator< MogoAoiItem > CREATOR = new Parcelable.Creator< MogoAoiItem >() { + @Override + public MogoAoiItem createFromParcel( Parcel source ) { + return new MogoAoiItem( source ); + } + + @Override + public MogoAoiItem[] newArray( int size ) { + return new MogoAoiItem[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoBusinessArea.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoBusinessArea.java new file mode 100644 index 0000000000..b3000a053d --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoBusinessArea.java @@ -0,0 +1,66 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoBusinessArea implements Parcelable { + + private MogoLatLng centerPoint; + private String name; + + public MogoLatLng getCenterPoint() { + return centerPoint; + } + + public void setCenterPoint( MogoLatLng centerPoint ) { + this.centerPoint = centerPoint; + } + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeParcelable( this.centerPoint, flags ); + dest.writeString( this.name ); + } + + public MogoBusinessArea() { + } + + protected MogoBusinessArea( Parcel in ) { + this.centerPoint = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.name = in.readString(); + } + + public static final Parcelable.Creator< MogoBusinessArea > CREATOR = new Parcelable.Creator< MogoBusinessArea >() { + @Override + public MogoBusinessArea createFromParcel( Parcel source ) { + return new MogoBusinessArea( source ); + } + + @Override + public MogoBusinessArea[] newArray( int size ) { + return new MogoBusinessArea[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoCrossroad.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoCrossroad.java new file mode 100644 index 0000000000..0fbc560d63 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoCrossroad.java @@ -0,0 +1,108 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoCrossroad implements Parcelable { + + private float distance; + private String direction; + private String firstRoadId; + private String firstRoadName; + private String secondRoadId; + private String secondRoadName; + + public float getDistance() { + return distance; + } + + public void setDistance( float distance ) { + this.distance = distance; + } + + public String getDirection() { + return direction; + } + + public void setDirection( String direction ) { + this.direction = direction; + } + + public String getFirstRoadId() { + return firstRoadId; + } + + public void setFirstRoadId( String firstRoadId ) { + this.firstRoadId = firstRoadId; + } + + public String getFirstRoadName() { + return firstRoadName; + } + + public void setFirstRoadName( String firstRoadName ) { + this.firstRoadName = firstRoadName; + } + + public String getSecondRoadId() { + return secondRoadId; + } + + public void setSecondRoadId( String secondRoadId ) { + this.secondRoadId = secondRoadId; + } + + public String getSecondRoadName() { + return secondRoadName; + } + + public void setSecondRoadName( String secondRoadName ) { + this.secondRoadName = secondRoadName; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeFloat( this.distance ); + dest.writeString( this.direction ); + dest.writeString( this.firstRoadId ); + dest.writeString( this.firstRoadName ); + dest.writeString( this.secondRoadId ); + dest.writeString( this.secondRoadName ); + } + + public MogoCrossroad() { + } + + protected MogoCrossroad( Parcel in ) { + this.distance = in.readFloat(); + this.direction = in.readString(); + this.firstRoadId = in.readString(); + this.firstRoadName = in.readString(); + this.secondRoadId = in.readString(); + this.secondRoadName = in.readString(); + } + + public static final Parcelable.Creator< MogoCrossroad > CREATOR = new Parcelable.Creator< MogoCrossroad >() { + @Override + public MogoCrossroad createFromParcel( Parcel source ) { + return new MogoCrossroad( source ); + } + + @Override + public MogoCrossroad[] newArray( int size ) { + return new MogoCrossroad[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeoSearch.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeoSearch.java new file mode 100644 index 0000000000..6d7c509b61 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeoSearch.java @@ -0,0 +1,62 @@ +package com.mogo.map.search; + +import android.content.Context; + +import com.mogo.map.amap.search.GeocodeSearchClient; +import com.mogo.map.exception.MogoMapException; +import com.mogo.map.search.query.MogoGeocodeQuery; +import com.mogo.map.search.query.MogoRegeocodeQuery; + +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 地理/逆地理位置搜索 + */ +public class MogoGeoSearch implements IMogoGeoSearch { + + private IMogoGeoSearch mDelegate; + + public MogoGeoSearch( Context context ) { + mDelegate = new GeocodeSearchClient( context ); + } + + @Override + public void setGeoSearchListener( IMogoGeoSearchListener listener ) { + if ( mDelegate != null ) { + mDelegate.setGeoSearchListener( listener ); + } + } + + @Override + public MogoRegeocodeAddress getFromLocation( MogoRegeocodeQuery query ) throws MogoMapException { + if ( mDelegate != null ) { + return mDelegate.getFromLocation( query ); + } + return null; + } + + @Override + public List< MogoGeocodeAddress > getFromLocationName( MogoGeocodeQuery query ) throws MogoMapException { + if ( mDelegate != null ) { + return mDelegate.getFromLocationName( query ); + } + return null; + } + + @Override + public void getFromLocationAsyn( MogoRegeocodeQuery query ) { + if ( mDelegate != null ) { + mDelegate.getFromLocationAsyn( query ); + } + } + + @Override + public void getFromLocationNameAsyn( MogoGeocodeQuery query ) { + if ( mDelegate != null ) { + mDelegate.getFromLocationNameAsyn( query ); + } + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeocodeAddress.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeocodeAddress.java new file mode 100644 index 0000000000..09d8700811 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeocodeAddress.java @@ -0,0 +1,103 @@ +package com.mogo.map.search; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * geo 搜索位置信息 + */ +public class MogoGeocodeAddress { + + private String formatAddress; + private String province; + private String city; + private String district; + private String township; + private String neighborhood; + private String building; + private String adcode; + private MogoLatLng latlng; + private String level; + + public String getFormatAddress() { + return formatAddress; + } + + public void setFormatAddress( String formatAddress ) { + this.formatAddress = formatAddress; + } + + public String getProvince() { + return province; + } + + public void setProvince( String province ) { + this.province = province; + } + + public String getCity() { + return city; + } + + public void setCity( String city ) { + this.city = city; + } + + public String getDistrict() { + return district; + } + + public void setDistrict( String district ) { + this.district = district; + } + + public String getTownship() { + return township; + } + + public void setTownship( String township ) { + this.township = township; + } + + public String getNeighborhood() { + return neighborhood; + } + + public void setNeighborhood( String neighborhood ) { + this.neighborhood = neighborhood; + } + + public String getBuilding() { + return building; + } + + public void setBuilding( String building ) { + this.building = building; + } + + public String getAdcode() { + return adcode; + } + + public void setAdcode( String adcode ) { + this.adcode = adcode; + } + + public MogoLatLng getLatlng() { + return latlng; + } + + public void setLatlng( MogoLatLng latlng ) { + this.latlng = latlng; + } + + public String getLevel() { + return level; + } + + public void setLevel( String level ) { + this.level = level; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeocodeResult.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeocodeResult.java new file mode 100644 index 0000000000..ecc855c190 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoGeocodeResult.java @@ -0,0 +1,23 @@ +package com.mogo.map.search; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 地理编码搜索结果 + */ +public class MogoGeocodeResult { + + private List< MogoGeocodeAddress > addresses = new ArrayList<>(); + + public List< MogoGeocodeAddress > getAddresses() { + return addresses; + } + + public void setAddresses( List< MogoGeocodeAddress > addresses ) { + this.addresses = addresses; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoIndoorData.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoIndoorData.java new file mode 100644 index 0000000000..69ffd1200a --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoIndoorData.java @@ -0,0 +1,75 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoIndoorData implements Parcelable { + + private String poiId; + private int floor; + private String floorName; + + public String getPoiId() { + return poiId; + } + + public void setPoiId( String poiId ) { + this.poiId = poiId; + } + + public int getFloor() { + return floor; + } + + public void setFloor( int floor ) { + this.floor = floor; + } + + public String getFloorName() { + return floorName; + } + + public void setFloorName( String floorName ) { + this.floorName = floorName; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.poiId ); + dest.writeInt( this.floor ); + dest.writeString( this.floorName ); + } + + public MogoIndoorData() { + } + + protected MogoIndoorData( Parcel in ) { + this.poiId = in.readString(); + this.floor = in.readInt(); + this.floorName = in.readString(); + } + + public static final Parcelable.Creator< MogoIndoorData > CREATOR = new Parcelable.Creator< MogoIndoorData >() { + @Override + public MogoIndoorData createFromParcel( Parcel source ) { + return new MogoIndoorData( source ); + } + + @Override + public MogoIndoorData[] newArray( int size ) { + return new MogoIndoorData[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoInputtipsSearch.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoInputtipsSearch.java new file mode 100644 index 0000000000..483889b905 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoInputtipsSearch.java @@ -0,0 +1,53 @@ +package com.mogo.map.search; + +import android.content.Context; + +import com.mogo.map.amap.search.InputtipsSearch; +import com.mogo.map.exception.MogoMapException; +import com.mogo.map.search.query.MogoInputtipsQuery; + +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * 描述 + */ +public class MogoInputtipsSearch implements IMogoInputtipsSearch { + + private IMogoInputtipsSearch mDelegate; + + public MogoInputtipsSearch( Context context, MogoInputtipsQuery query ) { + mDelegate = new InputtipsSearch( context, query ); + } + + @Override + public void setQuery( MogoInputtipsQuery query ) { + if ( mDelegate != null ) { + mDelegate.setQuery( query ); + } + } + + @Override + public void setInputtipsListener( IMogoInputtipsListener listener ) { + if ( mDelegate != null ) { + mDelegate.setInputtipsListener( listener ); + } + } + + @Override + public void requestInputtipsAsyn() { + if ( mDelegate != null ) { + mDelegate.requestInputtipsAsyn(); + } + } + + @Override + public List< MogoTip > requestInputtips() throws MogoMapException { + if ( mDelegate != null ) { + return mDelegate.requestInputtips(); + } + return null; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPhoto.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPhoto.java new file mode 100644 index 0000000000..feffddf23f --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPhoto.java @@ -0,0 +1,64 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoPhoto implements Parcelable { + + private String title; + private String url; + + public String getTitle() { + return title; + } + + public void setTitle( String title ) { + this.title = title; + } + + public String getUrl() { + return url; + } + + public void setUrl( String url ) { + this.url = url; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.title ); + dest.writeString( this.url ); + } + + public MogoPhoto() { + } + + protected MogoPhoto( Parcel in ) { + this.title = in.readString(); + this.url = in.readString(); + } + + public static final Parcelable.Creator< MogoPhoto > CREATOR = new Parcelable.Creator< MogoPhoto >() { + @Override + public MogoPhoto createFromParcel( Parcel source ) { + return new MogoPhoto( source ); + } + + @Override + public MogoPhoto[] newArray( int size ) { + return new MogoPhoto[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPoiItem.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPoiItem.java new file mode 100644 index 0000000000..22de089a44 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPoiItem.java @@ -0,0 +1,355 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoPoiItem implements Parcelable { + + private String businessArea; + private String adName; + private String cityName; + private String provinceName; + private String typeDes; + private String tel; + private String adCode; + private String poiId; + private int distance; + private String title; + private String snippet; + private MogoLatLng point; + private String cityCode; + private MogoLatLng enter; + private MogoLatLng exit; + private String website; + private String postcode; + private String email; + private String direction; + private boolean indoorMap; + private String provinceCode; + private String parkingType; + private List< MogoSubPoiItem > subPois = new ArrayList<>(); + private MogoIndoorData indoorData; + private List< MogoPhoto > photos = new ArrayList<>(); + private MogoPoiItemExtension poiExtension; + private String typeCode; + private String shopID; + + public String getBusinessArea() { + return businessArea; + } + + public void setBusinessArea( String businessArea ) { + this.businessArea = businessArea; + } + + public String getAdName() { + return adName; + } + + public void setAdName( String adName ) { + this.adName = adName; + } + + public String getCityName() { + return cityName; + } + + public void setCityName( String cityName ) { + this.cityName = cityName; + } + + public String getProvinceName() { + return provinceName; + } + + public void setProvinceName( String provinceName ) { + this.provinceName = provinceName; + } + + public String getTypeDes() { + return typeDes; + } + + public void setTypeDes( String typeDes ) { + this.typeDes = typeDes; + } + + public String getTel() { + return tel; + } + + public void setTel( String tel ) { + this.tel = tel; + } + + public String getAdCode() { + return adCode; + } + + public void setAdCode( String adCode ) { + this.adCode = adCode; + } + + public String getPoiId() { + return poiId; + } + + public void setPoiId( String poiId ) { + this.poiId = poiId; + } + + public int getDistance() { + return distance; + } + + public void setDistance( int distance ) { + this.distance = distance; + } + + public String getTitle() { + return title; + } + + public void setTitle( String title ) { + this.title = title; + } + + public String getSnippet() { + return snippet; + } + + public void setSnippet( String snippet ) { + this.snippet = snippet; + } + + public MogoLatLng getPoint() { + return point; + } + + public void setPoint( MogoLatLng point ) { + this.point = point; + } + + public String getCityCode() { + return cityCode; + } + + public void setCityCode( String cityCode ) { + this.cityCode = cityCode; + } + + public MogoLatLng getEnter() { + return enter; + } + + public void setEnter( MogoLatLng enter ) { + this.enter = enter; + } + + public MogoLatLng getExit() { + return exit; + } + + public void setExit( MogoLatLng exit ) { + this.exit = exit; + } + + public String getWebsite() { + return website; + } + + public void setWebsite( String website ) { + this.website = website; + } + + public String getPostcode() { + return postcode; + } + + public void setPostcode( String postcode ) { + this.postcode = postcode; + } + + public String getEmail() { + return email; + } + + public void setEmail( String email ) { + this.email = email; + } + + public String getDirection() { + return direction; + } + + public void setDirection( String direction ) { + this.direction = direction; + } + + public boolean isIndoorMap() { + return indoorMap; + } + + public void setIndoorMap( boolean indoorMap ) { + this.indoorMap = indoorMap; + } + + public String getProvinceCode() { + return provinceCode; + } + + public void setProvinceCode( String provinceCode ) { + this.provinceCode = provinceCode; + } + + public String getParkingType() { + return parkingType; + } + + public void setParkingType( String parkingType ) { + this.parkingType = parkingType; + } + + public List< MogoSubPoiItem > getSubPois() { + return subPois; + } + + public void setSubPois( List< MogoSubPoiItem > subPois ) { + this.subPois = subPois; + } + + public MogoIndoorData getIndoorData() { + return indoorData; + } + + public void setIndoorData( MogoIndoorData indoorData ) { + this.indoorData = indoorData; + } + + public List< MogoPhoto > getPhotos() { + return photos; + } + + public void setPhotos( List< MogoPhoto > photos ) { + this.photos = photos; + } + + public MogoPoiItemExtension getPoiExtension() { + return poiExtension; + } + + public void setPoiExtension( MogoPoiItemExtension poiExtension ) { + this.poiExtension = poiExtension; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode( String typeCode ) { + this.typeCode = typeCode; + } + + public String getShopID() { + return shopID; + } + + public void setShopID( String shopID ) { + this.shopID = shopID; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.businessArea ); + dest.writeString( this.adName ); + dest.writeString( this.cityName ); + dest.writeString( this.provinceName ); + dest.writeString( this.typeDes ); + dest.writeString( this.tel ); + dest.writeString( this.adCode ); + dest.writeString( this.poiId ); + dest.writeInt( this.distance ); + dest.writeString( this.title ); + dest.writeString( this.snippet ); + dest.writeParcelable( this.point, flags ); + dest.writeString( this.cityCode ); + dest.writeParcelable( this.enter, flags ); + dest.writeParcelable( this.exit, flags ); + dest.writeString( this.website ); + dest.writeString( this.postcode ); + dest.writeString( this.email ); + dest.writeString( this.direction ); + dest.writeByte( this.indoorMap ? ( byte ) 1 : ( byte ) 0 ); + dest.writeString( this.provinceCode ); + dest.writeString( this.parkingType ); + dest.writeTypedList( this.subPois ); + dest.writeParcelable( this.indoorData, flags ); + dest.writeTypedList( this.photos ); + dest.writeParcelable( this.poiExtension, flags ); + dest.writeString( this.typeCode ); + dest.writeString( this.shopID ); + } + + public MogoPoiItem() { + } + + protected MogoPoiItem( Parcel in ) { + this.businessArea = in.readString(); + this.adName = in.readString(); + this.cityName = in.readString(); + this.provinceName = in.readString(); + this.typeDes = in.readString(); + this.tel = in.readString(); + this.adCode = in.readString(); + this.poiId = in.readString(); + this.distance = in.readInt(); + this.title = in.readString(); + this.snippet = in.readString(); + this.point = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.cityCode = in.readString(); + this.enter = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.exit = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.website = in.readString(); + this.postcode = in.readString(); + this.email = in.readString(); + this.direction = in.readString(); + this.indoorMap = in.readByte() != 0; + this.provinceCode = in.readString(); + this.parkingType = in.readString(); + this.subPois = in.createTypedArrayList( MogoSubPoiItem.CREATOR ); + this.indoorData = in.readParcelable( MogoIndoorData.class.getClassLoader() ); + this.photos = in.createTypedArrayList( MogoPhoto.CREATOR ); + this.poiExtension = in.readParcelable( MogoPoiItemExtension.class.getClassLoader() ); + this.typeCode = in.readString(); + this.shopID = in.readString(); + } + + public static final Creator< MogoPoiItem > CREATOR = new Creator< MogoPoiItem >() { + @Override + public MogoPoiItem createFromParcel( Parcel source ) { + return new MogoPoiItem( source ); + } + + @Override + public MogoPoiItem[] newArray( int size ) { + return new MogoPoiItem[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPoiItemExtension.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPoiItemExtension.java new file mode 100644 index 0000000000..645b91c6bb --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoPoiItemExtension.java @@ -0,0 +1,63 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoPoiItemExtension implements Parcelable { + + private String opentime; + private String rating; + + public String getOpentime() { + return opentime; + } + + public void setOpentime( String opentime ) { + this.opentime = opentime; + } + + public String getRating() { + return rating; + } + + public void setRating( String rating ) { + this.rating = rating; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.opentime ); + dest.writeString( this.rating ); + } + + public MogoPoiItemExtension() { + } + + protected MogoPoiItemExtension( Parcel in ) { + this.opentime = in.readString(); + this.rating = in.readString(); + } + + public static final Parcelable.Creator< MogoPoiItemExtension > CREATOR = new Parcelable.Creator< MogoPoiItemExtension >() { + @Override + public MogoPoiItemExtension createFromParcel( Parcel source ) { + return new MogoPoiItemExtension( source ); + } + + @Override + public MogoPoiItemExtension[] newArray( int size ) { + return new MogoPoiItemExtension[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeAddress.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeAddress.java new file mode 100644 index 0000000000..b3d90e6556 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeAddress.java @@ -0,0 +1,231 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 逆地理编码地址 + */ +public class MogoRegeocodeAddress implements Parcelable { + + private String formatAddress; + private String province; + private String city; + private String cityCode; + private String adCode; + private String district; + private String township; + private String neighborhood; + private String building; + private MogoStreetNumber streetNumber; + private List< MogoRegeocodeRoad > roads; + private List< MogoPoiItem > pois; + private List< MogoCrossroad > crossroads; + private List< MogoBusinessArea > businessAreas; + private List< MogoAoiItem > aois; + private String towncode; + private String country; + + public String getFormatAddress() { + return formatAddress; + } + + public void setFormatAddress( String formatAddress ) { + this.formatAddress = formatAddress; + } + + public String getProvince() { + return province; + } + + public void setProvince( String province ) { + this.province = province; + } + + public String getCity() { + return city; + } + + public void setCity( String city ) { + this.city = city; + } + + public String getCityCode() { + return cityCode; + } + + public void setCityCode( String cityCode ) { + this.cityCode = cityCode; + } + + public String getAdCode() { + return adCode; + } + + public void setAdCode( String adCode ) { + this.adCode = adCode; + } + + public String getDistrict() { + return district; + } + + public void setDistrict( String district ) { + this.district = district; + } + + public String getTownship() { + return township; + } + + public void setTownship( String township ) { + this.township = township; + } + + public String getNeighborhood() { + return neighborhood; + } + + public void setNeighborhood( String neighborhood ) { + this.neighborhood = neighborhood; + } + + public String getBuilding() { + return building; + } + + public void setBuilding( String building ) { + this.building = building; + } + + public MogoStreetNumber getStreetNumber() { + return streetNumber; + } + + public void setStreetNumber( MogoStreetNumber streetNumber ) { + this.streetNumber = streetNumber; + } + + public List< MogoRegeocodeRoad > getRoads() { + return roads; + } + + public void setRoads( List< MogoRegeocodeRoad > roads ) { + this.roads = roads; + } + + public List< MogoPoiItem > getPois() { + return pois; + } + + public void setPois( List< MogoPoiItem > pois ) { + this.pois = pois; + } + + public List< MogoCrossroad > getCrossroads() { + return crossroads; + } + + public void setCrossroads( List< MogoCrossroad > crossroads ) { + this.crossroads = crossroads; + } + + public List< MogoBusinessArea > getBusinessAreas() { + return businessAreas; + } + + public void setBusinessAreas( List< MogoBusinessArea > businessAreas ) { + this.businessAreas = businessAreas; + } + + public List< MogoAoiItem > getAois() { + return aois; + } + + public void setAois( List< MogoAoiItem > aois ) { + this.aois = aois; + } + + public String getTowncode() { + return towncode; + } + + public void setTowncode( String towncode ) { + this.towncode = towncode; + } + + public String getCountry() { + return country; + } + + public void setCountry( String country ) { + this.country = country; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.formatAddress ); + dest.writeString( this.province ); + dest.writeString( this.city ); + dest.writeString( this.cityCode ); + dest.writeString( this.adCode ); + dest.writeString( this.district ); + dest.writeString( this.township ); + dest.writeString( this.neighborhood ); + dest.writeString( this.building ); + dest.writeParcelable( this.streetNumber, flags ); + dest.writeTypedList( this.roads ); + dest.writeTypedList( this.pois ); + dest.writeTypedList( this.crossroads ); + dest.writeTypedList( this.businessAreas ); + dest.writeTypedList( this.aois ); + dest.writeString( this.towncode ); + dest.writeString( this.country ); + } + + public MogoRegeocodeAddress() { + } + + protected MogoRegeocodeAddress( Parcel in ) { + this.formatAddress = in.readString(); + this.province = in.readString(); + this.city = in.readString(); + this.cityCode = in.readString(); + this.adCode = in.readString(); + this.district = in.readString(); + this.township = in.readString(); + this.neighborhood = in.readString(); + this.building = in.readString(); + this.streetNumber = in.readParcelable( MogoStreetNumber.class.getClassLoader() ); + this.roads = in.createTypedArrayList( MogoRegeocodeRoad.CREATOR ); + this.pois = in.createTypedArrayList( MogoPoiItem.CREATOR ); + this.crossroads = in.createTypedArrayList( MogoCrossroad.CREATOR ); + this.businessAreas = in.createTypedArrayList( MogoBusinessArea.CREATOR ); + this.aois = in.createTypedArrayList( MogoAoiItem.CREATOR ); + this.towncode = in.readString(); + this.country = in.readString(); + } + + public static final Parcelable.Creator< MogoRegeocodeAddress > CREATOR = new Parcelable.Creator< MogoRegeocodeAddress >() { + @Override + public MogoRegeocodeAddress createFromParcel( Parcel source ) { + return new MogoRegeocodeAddress( source ); + } + + @Override + public MogoRegeocodeAddress[] newArray( int size ) { + return new MogoRegeocodeAddress[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeResult.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeResult.java new file mode 100644 index 0000000000..6fd7141374 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeResult.java @@ -0,0 +1,53 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 逆地理编码结果 + */ +public class MogoRegeocodeResult implements Parcelable { + + private MogoRegeocodeAddress regeocodeAddress; + + public MogoRegeocodeAddress getRegeocodeAddress() { + return regeocodeAddress; + } + + public void setRegeocodeAddress( MogoRegeocodeAddress regeocodeAddress ) { + this.regeocodeAddress = regeocodeAddress; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeParcelable( this.regeocodeAddress, flags ); + } + + public MogoRegeocodeResult() { + } + + protected MogoRegeocodeResult( Parcel in ) { + this.regeocodeAddress = in.readParcelable( MogoRegeocodeAddress.class.getClassLoader() ); + } + + public static final Parcelable.Creator< MogoRegeocodeResult > CREATOR = new Parcelable.Creator< MogoRegeocodeResult >() { + @Override + public MogoRegeocodeResult createFromParcel( Parcel source ) { + return new MogoRegeocodeResult( source ); + } + + @Override + public MogoRegeocodeResult[] newArray( int size ) { + return new MogoRegeocodeResult[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeRoad.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeRoad.java new file mode 100644 index 0000000000..d3fb6c2ece --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoRegeocodeRoad.java @@ -0,0 +1,98 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoRegeocodeRoad implements Parcelable { + + private String id; + private String name; + private float distance; + private String direction; + private MogoLatLng point; + + public String getId() { + return id; + } + + public void setId( String id ) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } + + public float getDistance() { + return distance; + } + + public void setDistance( float distance ) { + this.distance = distance; + } + + public String getDirection() { + return direction; + } + + public void setDirection( String direction ) { + this.direction = direction; + } + + public MogoLatLng getPoint() { + return point; + } + + public void setPoint( MogoLatLng point ) { + this.point = point; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.id ); + dest.writeString( this.name ); + dest.writeFloat( this.distance ); + dest.writeString( this.direction ); + dest.writeParcelable( this.point, flags ); + } + + public MogoRegeocodeRoad() { + } + + protected MogoRegeocodeRoad( Parcel in ) { + this.id = in.readString(); + this.name = in.readString(); + this.distance = in.readFloat(); + this.direction = in.readString(); + this.point = in.readParcelable( MogoLatLng.class.getClassLoader() ); + } + + public static final Parcelable.Creator< MogoRegeocodeRoad > CREATOR = new Parcelable.Creator< MogoRegeocodeRoad >() { + @Override + public MogoRegeocodeRoad createFromParcel( Parcel source ) { + return new MogoRegeocodeRoad( source ); + } + + @Override + public MogoRegeocodeRoad[] newArray( int size ) { + return new MogoRegeocodeRoad[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoStreetNumber.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoStreetNumber.java new file mode 100644 index 0000000000..c12d20fbaf --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoStreetNumber.java @@ -0,0 +1,98 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoStreetNumber implements Parcelable { + + private String street; + private String number; + private MogoLatLng latLonPoint; + private String direction; + private float distance; + + public String getStreet() { + return street; + } + + public void setStreet( String street ) { + this.street = street; + } + + public String getNumber() { + return number; + } + + public void setNumber( String number ) { + this.number = number; + } + + public MogoLatLng getLatLonPoint() { + return latLonPoint; + } + + public void setLatLonPoint( MogoLatLng latLonPoint ) { + this.latLonPoint = latLonPoint; + } + + public String getDirection() { + return direction; + } + + public void setDirection( String direction ) { + this.direction = direction; + } + + public float getDistance() { + return distance; + } + + public void setDistance( float distance ) { + this.distance = distance; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.street ); + dest.writeString( this.number ); + dest.writeParcelable( this.latLonPoint, flags ); + dest.writeString( this.direction ); + dest.writeFloat( this.distance ); + } + + public MogoStreetNumber() { + } + + protected MogoStreetNumber( Parcel in ) { + this.street = in.readString(); + this.number = in.readString(); + this.latLonPoint = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.direction = in.readString(); + this.distance = in.readFloat(); + } + + public static final Parcelable.Creator< MogoStreetNumber > CREATOR = new Parcelable.Creator< MogoStreetNumber >() { + @Override + public MogoStreetNumber createFromParcel( Parcel source ) { + return new MogoStreetNumber( source ); + } + + @Override + public MogoStreetNumber[] newArray( int size ) { + return new MogoStreetNumber[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoSubPoiItem.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoSubPoiItem.java new file mode 100644 index 0000000000..f1240ef0ee --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoSubPoiItem.java @@ -0,0 +1,121 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 描述 + */ +public class MogoSubPoiItem implements Parcelable { + + private String poiId; + private String title; + private String subName; + private int distance; + private MogoLatLng point; + private String snippet; + private String subTypeDes; + + public String getPoiId() { + return poiId; + } + + public void setPoiId( String poiId ) { + this.poiId = poiId; + } + + public String getTitle() { + return title; + } + + public void setTitle( String title ) { + this.title = title; + } + + public String getSubName() { + return subName; + } + + public void setSubName( String subName ) { + this.subName = subName; + } + + public int getDistance() { + return distance; + } + + public void setDistance( int distance ) { + this.distance = distance; + } + + public MogoLatLng getPoint() { + return point; + } + + public void setPoint( MogoLatLng point ) { + this.point = point; + } + + public String getSnippet() { + return snippet; + } + + public void setSnippet( String snippet ) { + this.snippet = snippet; + } + + public String getSubTypeDes() { + return subTypeDes; + } + + public void setSubTypeDes( String subTypeDes ) { + this.subTypeDes = subTypeDes; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.poiId ); + dest.writeString( this.title ); + dest.writeString( this.subName ); + dest.writeInt( this.distance ); + dest.writeParcelable( this.point, flags ); + dest.writeString( this.snippet ); + dest.writeString( this.subTypeDes ); + } + + public MogoSubPoiItem() { + } + + protected MogoSubPoiItem( Parcel in ) { + this.poiId = in.readString(); + this.title = in.readString(); + this.subName = in.readString(); + this.distance = in.readInt(); + this.point = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.snippet = in.readString(); + this.subTypeDes = in.readString(); + } + + public static final Parcelable.Creator< MogoSubPoiItem > CREATOR = new Parcelable.Creator< MogoSubPoiItem >() { + @Override + public MogoSubPoiItem createFromParcel( Parcel source ) { + return new MogoSubPoiItem( source ); + } + + @Override + public MogoSubPoiItem[] newArray( int size ) { + return new MogoSubPoiItem[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoTip.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoTip.java new file mode 100644 index 0000000000..1f6c631c2a --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/MogoTip.java @@ -0,0 +1,120 @@ +package com.mogo.map.search; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * inputtips 搜索结果 + */ +public class MogoTip implements Parcelable { + + private String poiID; + private MogoLatLng point; + private String name; + private String district; + private String adCode; + private String address; + private String typeCode; + + public String getPoiID() { + return poiID; + } + + public void setPoiID( String poiID ) { + this.poiID = poiID; + } + + public MogoLatLng getPoint() { + return point; + } + + public void setPoint( MogoLatLng point ) { + this.point = point; + } + + public String getName() { + return name; + } + + public void setName( String name ) { + this.name = name; + } + + public String getDistrict() { + return district; + } + + public void setDistrict( String district ) { + this.district = district; + } + + public String getAdCode() { + return adCode; + } + + public void setAdCode( String adCode ) { + this.adCode = adCode; + } + + public String getAddress() { + return address; + } + + public void setAddress( String address ) { + this.address = address; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode( String typeCode ) { + this.typeCode = typeCode; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.poiID ); + dest.writeParcelable( this.point, flags ); + dest.writeString( this.name ); + dest.writeString( this.district ); + dest.writeString( this.adCode ); + dest.writeString( this.address ); + dest.writeString( this.typeCode ); + } + + public MogoTip() { + } + + protected MogoTip( Parcel in ) { + this.poiID = in.readString(); + this.point = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.name = in.readString(); + this.district = in.readString(); + this.adCode = in.readString(); + this.address = in.readString(); + this.typeCode = in.readString(); + } + + public static final Parcelable.Creator< MogoTip > CREATOR = new Parcelable.Creator< MogoTip >() { + @Override + public MogoTip createFromParcel( Parcel source ) { + return new MogoTip( source ); + } + + @Override + public MogoTip[] newArray( int size ) { + return new MogoTip[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoGeocodeQuery.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoGeocodeQuery.java new file mode 100644 index 0000000000..441bd20bad --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoGeocodeQuery.java @@ -0,0 +1,63 @@ +package com.mogo.map.search.query; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 地理编码查询条件 + */ +public class MogoGeocodeQuery implements Parcelable { + private String locationName; + private String city; + + public String getLocationName() { + return locationName; + } + + public void setLocationName( String locationName ) { + this.locationName = locationName; + } + + public String getCity() { + return city; + } + + public void setCity( String city ) { + this.city = city; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeString( this.locationName ); + dest.writeString( this.city ); + } + + public MogoGeocodeQuery() { + } + + protected MogoGeocodeQuery( Parcel in ) { + this.locationName = in.readString(); + this.city = in.readString(); + } + + public static final Parcelable.Creator< MogoGeocodeQuery > CREATOR = new Parcelable.Creator< MogoGeocodeQuery >() { + @Override + public MogoGeocodeQuery createFromParcel( Parcel source ) { + return new MogoGeocodeQuery( source ); + } + + @Override + public MogoGeocodeQuery[] newArray( int size ) { + return new MogoGeocodeQuery[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoInputtipsQuery.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoInputtipsQuery.java new file mode 100644 index 0000000000..7267328d81 --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoInputtipsQuery.java @@ -0,0 +1,58 @@ +package com.mogo.map.search.query; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-20 + *

+ * inputtips 搜索条件 + */ +public class MogoInputtipsQuery { + + private String keyword; + private String city; + private String type; + private boolean cityLimit; + private MogoLatLng location; + + public String getKeyword() { + return keyword; + } + + public void setKeyword( String keyword ) { + this.keyword = keyword; + } + + public String getCity() { + return city; + } + + public void setCity( String city ) { + this.city = city; + } + + public String getType() { + return type; + } + + public void setType( String type ) { + this.type = type; + } + + public boolean isCityLimit() { + return cityLimit; + } + + public void setCityLimit( boolean cityLimit ) { + this.cityLimit = cityLimit; + } + + public MogoLatLng getLocation() { + return location; + } + + public void setLocation( MogoLatLng location ) { + this.location = location; + } +} diff --git a/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoRegeocodeQuery.java b/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoRegeocodeQuery.java new file mode 100644 index 0000000000..cd295d404a --- /dev/null +++ b/libraries/mogo-map/src/main/java/com/mogo/map/search/query/MogoRegeocodeQuery.java @@ -0,0 +1,88 @@ +package com.mogo.map.search.query; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.mogo.map.MogoLatLng; + +/** + * @author congtaowang + * @since 2019-12-19 + *

+ * 逆地理编码查询条件 + */ +public class MogoRegeocodeQuery implements Parcelable { + + private MogoLatLng point; + private float radius; + private String latlngType; + private String poiType; + + public MogoLatLng getPoint() { + return point; + } + + public void setPoint( MogoLatLng point ) { + this.point = point; + } + + public float getRadius() { + return radius; + } + + public void setRadius( float radius ) { + this.radius = radius; + } + + public String getLatlngType() { + return latlngType; + } + + public void setLatlngType( String latlngType ) { + this.latlngType = latlngType; + } + + public String getPoiType() { + return poiType; + } + + public void setPoiType( String poiType ) { + this.poiType = poiType; + } + + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel( Parcel dest, int flags ) { + dest.writeParcelable( this.point, flags ); + dest.writeFloat( this.radius ); + dest.writeString( this.latlngType ); + dest.writeString( this.poiType ); + } + + public MogoRegeocodeQuery() { + } + + protected MogoRegeocodeQuery( Parcel in ) { + this.point = in.readParcelable( MogoLatLng.class.getClassLoader() ); + this.radius = in.readFloat(); + this.latlngType = in.readString(); + this.poiType = in.readString(); + } + + public static final Parcelable.Creator< MogoRegeocodeQuery > CREATOR = new Parcelable.Creator< MogoRegeocodeQuery >() { + @Override + public MogoRegeocodeQuery createFromParcel( Parcel source ) { + return new MogoRegeocodeQuery( source ); + } + + @Override + public MogoRegeocodeQuery[] newArray( int size ) { + return new MogoRegeocodeQuery[size]; + } + }; +} diff --git a/libraries/mogo-map/src/main/res/values/strings.xml b/libraries/mogo-map/src/main/res/values/strings.xml new file mode 100644 index 0000000000..27e89c2880 --- /dev/null +++ b/libraries/mogo-map/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + mogo-map + diff --git a/services/mogo-service-api/.gitignore b/services/mogo-service-api/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/services/mogo-service-api/.gitignore @@ -0,0 +1 @@ +/build diff --git a/services/mogo-service-api/build.gradle b/services/mogo-service-api/build.gradle new file mode 100644 index 0000000000..42f0f51704 --- /dev/null +++ b/services/mogo-service-api/build.gradle @@ -0,0 +1,38 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'androidx.appcompat:appcompat:1.0.2' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + + implementation project(":libraries:mogo-map") + implementation rootProject.ext.dependencies.arouter + api rootProject.ext.dependencies.fresco +} diff --git a/services/mogo-service-api/consumer-rules.pro b/services/mogo-service-api/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/services/mogo-service-api/proguard-rules.pro b/services/mogo-service-api/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/services/mogo-service-api/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/services/mogo-service-api/src/androidTest/java/com/mogo/service/map/ExampleInstrumentedTest.java b/services/mogo-service-api/src/androidTest/java/com/mogo/service/map/ExampleInstrumentedTest.java new file mode 100644 index 0000000000..44238af9f8 --- /dev/null +++ b/services/mogo-service-api/src/androidTest/java/com/mogo/service/map/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.mogo.service.map; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith( AndroidJUnit4.class ) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals( "com.mogo.service.amap.test", appContext.getPackageName() ); + } +} diff --git a/services/mogo-service-api/src/main/AndroidManifest.xml b/services/mogo-service-api/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..46f9830497 --- /dev/null +++ b/services/mogo-service-api/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/MogoServicePaths.java b/services/mogo-service-api/src/main/java/com/mogo/service/MogoServicePaths.java new file mode 100644 index 0000000000..fabd415899 --- /dev/null +++ b/services/mogo-service-api/src/main/java/com/mogo/service/MogoServicePaths.java @@ -0,0 +1,25 @@ +package com.mogo.service; + +/** + * @author congtaowang + * @since 2019-12-23 + *

+ * 对外服务模块路径 + *

+ *

+ * 使用方式: + *

+ * Arouter.getInstance().path("").navigate() + */ +public class MogoServicePaths { + + /** + * 地图服务接口路径 + */ + public static final String PATH_MAP_SERVICE = "/mogo/services/map"; + + /** + * 图片接口 + */ + public static final String PATH_IMAGE_LOADER = "/mogo/utils/imageloader"; +} diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/IMogoImageLoaderListener.java b/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/IMogoImageLoaderListener.java new file mode 100644 index 0000000000..7a5bab5de9 --- /dev/null +++ b/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/IMogoImageLoaderListener.java @@ -0,0 +1,20 @@ +package com.mogo.service.imageloader; + +import android.graphics.Bitmap; + +/** + * @author congtaowang + * @since 2019-12-23 + *

+ * 图片下载接口 + */ +public interface IMogoImageLoaderListener { + + void onStart(); + +// void onProcess( int completedSize, int totalSize ); + + void onCompleted( Bitmap bitmap ); + + void onFailure( Exception e ); +} \ No newline at end of file diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/IMogoImageloader.java b/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/IMogoImageloader.java new file mode 100644 index 0000000000..e03f59686c --- /dev/null +++ b/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/IMogoImageloader.java @@ -0,0 +1,28 @@ +package com.mogo.service.imageloader; + +import android.content.Context; + +import com.alibaba.android.arouter.facade.template.IProvider; + +/** + * @author congtaowang + * @since 2019-12-23 + *

+ * 图片接口 + */ +public interface IMogoImageloader extends IProvider { + + void init( Context context ); + + void displayImage( String url, MogoImageView imageView ); + + void displayImage( String url, MogoImageView imageView, int width, int height ); + + void displayImage( String url, MogoImageView imageView, int width, int height, final IMogoImageLoaderListener listener ); + + void displayImage( String url, MogoImageView imageView, final IMogoImageLoaderListener listener ); + + void downloadImage( Context context, String url, IMogoImageLoaderListener listener ); + + void destroy(); +} diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/MogoImageView.java b/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/MogoImageView.java new file mode 100644 index 0000000000..1719b7e803 --- /dev/null +++ b/services/mogo-service-api/src/main/java/com/mogo/service/imageloader/MogoImageView.java @@ -0,0 +1,174 @@ +package com.mogo.service.imageloader; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Matrix; +import android.util.AttributeSet; + +import androidx.annotation.DrawableRes; + +import com.facebook.drawee.view.GenericDraweeView; +import com.mogo.service.R; + +/** + * @author congtaowang + * @since 2019-12-23 + *

+ * 封装 facebook GenericDraweeView,实现占位图、失败占位图、形状、边框、模糊等效果 + */ +public class MogoImageView extends GenericDraweeView { + + private int mPlaceHolder; + private int mFailureHolder; + private int mOverlayImageId; + private int mShape; + private int mRadius; + private int mTopLeftRadius; + private int mTopRightRadius; + private int mBottomLeftRadius; + private int mBottomRightRadius; + private int mBorderWidth; + private int mBorderColor; + private int mBlurRadius; + private boolean mIsBlur; + + public static final int SHAPE_NORMAL = 0; + public static final int SHAPE_CIRCLE = 1; + public static final int SHAPE_ROUND = 2; + + public MogoImageView( Context context ) { + this( context, null, 0 ); + } + + public MogoImageView( Context context, AttributeSet attrs ) { + this( context, attrs, 0 ); + } + + public MogoImageView( Context context, AttributeSet attrs, int theme ) { + super( context, attrs, theme ); + TypedArray arrays = context.obtainStyledAttributes( attrs, R.styleable.MogoImageView ); + mPlaceHolder = arrays.getResourceId( R.styleable.MogoImageView_placeHolder, 0 ); + mFailureHolder = arrays.getResourceId( R.styleable.MogoImageView_failureHolder, 0 ); + mOverlayImageId = arrays.getResourceId( R.styleable.MogoImageView_overlayImageId, 0 ); + mShape = arrays.getInt( R.styleable.MogoImageView_shape, SHAPE_NORMAL ); + mRadius = arrays.getDimensionPixelSize( R.styleable.MogoImageView_radius, 0 ); + mTopLeftRadius = arrays.getDimensionPixelSize( R.styleable.MogoImageView_topLeftRadius, 0 ); + mTopRightRadius = arrays.getDimensionPixelSize( R.styleable.MogoImageView_topRightRadius, 0 ); + mBottomLeftRadius = arrays.getDimensionPixelSize( R.styleable.MogoImageView_bottomLeftRadius, 0 ); + mBottomRightRadius = arrays.getDimensionPixelSize( R.styleable.MogoImageView_bottomRightRadius, 0 ); + mBorderWidth = arrays.getDimensionPixelSize( R.styleable.MogoImageView_shapeBorderWidth, 0 ); + mBorderColor = arrays.getColor( R.styleable.MogoImageView_borderColor, Color.WHITE ); + mIsBlur = arrays.getBoolean( R.styleable.MogoImageView_isBlur, false ); + mBlurRadius = arrays.getInt( R.styleable.MogoImageView_blurRadius, 25 ); + arrays.recycle(); + } + + public void setRadius( float radius ) { + this.mRadius = ( int ) radius; + } + + public void setRadius( int[] radius ) { + if ( radius != null && radius.length == 8 ) { + mTopLeftRadius = radius[0] >= radius[1] ? radius[0] : radius[1]; + mTopRightRadius = radius[2] >= radius[3] ? radius[2] : radius[3]; + mBottomRightRadius = radius[4] >= radius[5] ? radius[4] : radius[5]; + mBottomLeftRadius = radius[6] >= radius[7] ? radius[6] : radius[7]; + } + } + + public void setShape( int shape ) { + this.mShape = shape; + } + + public void setBorderWidth( int width ) { + this.mBorderWidth = width; + } + + public void setBorderColor( int color ) { + this.mBorderColor = color; + } + + public void setPlaceHolder( int placeHolder ) { + this.mPlaceHolder = placeHolder; + } + + public void setFailureHolder( int failureHolder ) { + this.mFailureHolder = failureHolder; + } + + public void setOverlayImageId( @DrawableRes int overlayImageId ) { + this.mOverlayImageId = overlayImageId; + } + + public void setBlur( boolean isBlur ) { + this.mIsBlur = isBlur; + } + + public void setBlurRadius( int blurRadius ) { + this.mBlurRadius = blurRadius; + } + + public int getPlaceHolder() { + return mPlaceHolder; + } + + public int getFailureHolder() { + return mFailureHolder; + } + + public int getOverlayImageId() { + return mOverlayImageId; + } + + public int getShape() { + return mShape; + } + + public float[] getRadii() { + if ( mTopLeftRadius != 0 || mTopRightRadius != 0 || mBottomRightRadius != 0 || mBottomLeftRadius != 0 ) { + float[] radii = {mTopLeftRadius, mTopLeftRadius, mTopRightRadius, mTopRightRadius, + mBottomRightRadius, mBottomRightRadius, mBottomLeftRadius, mBottomLeftRadius}; + return radii; + } + + return null; + } + + public int getRadius() { + return mRadius; + } + + public int getBorderWidth() { + return mBorderWidth; + } + + public int getBorderColor() { + return mBorderColor; + } + + public boolean isBlur() { + return mIsBlur; + } + + public int getBlurRadius() { + return mBlurRadius; + } + + public String getParams() { + StringBuilder builder = new StringBuilder(); + builder.append( mIsBlur ).append( mBlurRadius ) + .append( mShape ).append( mRadius ) + .append( mBorderWidth ).append( mBorderColor ); + return builder.toString(); + } + + /** + * 解决共享动画无效的问题 + * + * @param matrix + */ + public void animateTransform( Matrix matrix ) { + invalidate(); + } +} \ No newline at end of file diff --git a/services/mogo-service-api/src/main/java/com/mogo/service/map/IMogoMapService.java b/services/mogo-service-api/src/main/java/com/mogo/service/map/IMogoMapService.java new file mode 100644 index 0000000000..6334ef265a --- /dev/null +++ b/services/mogo-service-api/src/main/java/com/mogo/service/map/IMogoMapService.java @@ -0,0 +1,64 @@ +package com.mogo.service.map; + +import android.content.Context; + +import com.alibaba.android.arouter.facade.template.IProvider; +import com.mogo.map.location.IMogoLocationClient; +import com.mogo.map.marker.IMogoMarker; +import com.mogo.map.marker.MogoMarkerOptions; +import com.mogo.map.search.IMogoGeoSearch; +import com.mogo.map.search.IMogoInputtipsSearch; +import com.mogo.map.search.query.MogoInputtipsQuery; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 地图对外地接口 + */ +public interface IMogoMapService extends IProvider { + + /** + * 获取定位服务实例 + * + * @param context + * @return + */ + IMogoLocationClient getSingletonLocationClient( Context context ); + + /** + * 添加marker + * + * @param options + * @return + */ + IMogoMarker addMarker( MogoMarkerOptions options ); + + /** + * 添加多个marker + * + * @param options + * @return + */ + List< IMogoMarker > addMarkers( ArrayList< MogoMarkerOptions > options, boolean moveToCenter ); + + /** + * 获取关键字搜索地址服务 + * + * @param context + * @param query + * @return + */ + IMogoInputtipsSearch getInputtipsSearch( Context context, MogoInputtipsQuery query ); + + /** + * 地理编码或逆地理编码服务 + * + * @param context + * @return + */ + IMogoGeoSearch getGeoSearch( Context context ); +} diff --git a/services/mogo-service-api/src/main/res/values/attrs.xml b/services/mogo-service-api/src/main/res/values/attrs.xml new file mode 100644 index 0000000000..febf72ba74 --- /dev/null +++ b/services/mogo-service-api/src/main/res/values/attrs.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/services/mogo-service-api/src/main/res/values/strings.xml b/services/mogo-service-api/src/main/res/values/strings.xml new file mode 100644 index 0000000000..eb1356c817 --- /dev/null +++ b/services/mogo-service-api/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + amap-api + diff --git a/services/mogo-service-api/src/test/java/com/mogo/service/map/ExampleUnitTest.java b/services/mogo-service-api/src/test/java/com/mogo/service/map/ExampleUnitTest.java new file mode 100644 index 0000000000..6c7fa71433 --- /dev/null +++ b/services/mogo-service-api/src/test/java/com/mogo/service/map/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.mogo.service.map; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals( 4, 2 + 2 ); + } +} \ No newline at end of file diff --git a/services/mogo-service/.gitignore b/services/mogo-service/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/services/mogo-service/.gitignore @@ -0,0 +1 @@ +/build diff --git a/services/mogo-service/build.gradle b/services/mogo-service/build.gradle new file mode 100644 index 0000000000..463966d78e --- /dev/null +++ b/services/mogo-service/build.gradle @@ -0,0 +1,47 @@ +apply plugin: 'com.android.library' + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.2" + + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles 'consumer-rules.pro' + + + javaCompileOptions { + annotationProcessorOptions { + arguments = [AROUTER_MODULE_NAME: project.getName()] + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'androidx.appcompat:appcompat:1.0.2' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + + implementation project(":libraries:mogo-map") + implementation project(":foudations:mogo-utils") + implementation project(':services:mogo-service-api') + implementation rootProject.ext.dependencies.arouter + annotationProcessor rootProject.ext.dependencies.aroutercompiler +} diff --git a/services/mogo-service/consumer-rules.pro b/services/mogo-service/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/services/mogo-service/proguard-rules.pro b/services/mogo-service/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/services/mogo-service/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/services/mogo-service/src/androidTest/java/com/mogo/service/map/ExampleInstrumentedTest.java b/services/mogo-service/src/androidTest/java/com/mogo/service/map/ExampleInstrumentedTest.java new file mode 100644 index 0000000000..44238af9f8 --- /dev/null +++ b/services/mogo-service/src/androidTest/java/com/mogo/service/map/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.mogo.service.map; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith( AndroidJUnit4.class ) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals( "com.mogo.service.amap.test", appContext.getPackageName() ); + } +} diff --git a/services/mogo-service/src/main/AndroidManifest.xml b/services/mogo-service/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..3b87b75ef7 --- /dev/null +++ b/services/mogo-service/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/MogoImageLoader.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/MogoImageLoader.java new file mode 100644 index 0000000000..798306bd7d --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/MogoImageLoader.java @@ -0,0 +1,69 @@ +package com.mogo.service.impl.imageloader; + +import android.content.Context; + +import com.alibaba.android.arouter.facade.annotation.Route; +import com.mogo.service.MogoServicePaths; +import com.mogo.service.imageloader.IMogoImageLoaderListener; +import com.mogo.service.imageloader.IMogoImageloader; +import com.mogo.service.imageloader.MogoImageView; +import com.mogo.service.impl.imageloader.glide.GlideImageLoader; + +/** + * @author congtaowang + * @since 2019-12-23 + *

+ * 实现图片接口 + */ +@Route( path = MogoServicePaths.PATH_IMAGE_LOADER ) +public class MogoImageLoader implements IMogoImageloader { + + private IMogoImageloader mDelegate; + + @Override + public void init( Context context ) { + mDelegate = GlideImageLoader.getInstance(); + } + + @Override + public void displayImage( String url, MogoImageView imageView ) { + if ( mDelegate != null ) { + mDelegate.displayImage( url, imageView ); + } + } + + @Override + public void displayImage( String url, MogoImageView imageView, int width, int height ) { + if ( mDelegate != null ) { + mDelegate.displayImage( url, imageView, width, height ); + } + } + + @Override + public void displayImage( String url, MogoImageView imageView, int width, int height, IMogoImageLoaderListener listener ) { + if ( mDelegate != null ) { + mDelegate.displayImage( url, imageView, width, height, listener ); + } + } + + @Override + public void displayImage( String url, MogoImageView imageView, IMogoImageLoaderListener listener ) { + if ( mDelegate != null ) { + mDelegate.displayImage( url, imageView, 0, 0, listener ); + } + } + + @Override + public void downloadImage( Context context, String url, IMogoImageLoaderListener listener ) { + if ( mDelegate != null ) { + mDelegate.downloadImage( context, url, listener ); + } + } + + @Override + public void destroy() { + if ( mDelegate != null ) { + mDelegate.destroy(); + } + } +} diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/GlideImageLoader.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/GlideImageLoader.java new file mode 100644 index 0000000000..a6a17fb32c --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/GlideImageLoader.java @@ -0,0 +1,169 @@ +package com.mogo.service.impl.imageloader.glide; + +import android.content.Context; +import android.graphics.Bitmap; + +import androidx.annotation.Nullable; + +import com.alibaba.android.arouter.facade.annotation.Route; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.Target; +import com.mogo.service.MogoServicePaths; +import com.mogo.service.imageloader.IMogoImageLoaderListener; +import com.mogo.service.imageloader.IMogoImageloader; +import com.mogo.service.imageloader.MogoImageView; +import com.mogo.service.impl.imageloader.glide.transform.GlideBlurTransform; +import com.mogo.service.impl.imageloader.glide.transform.GlideCircleBitmapTransform; +import com.mogo.service.impl.imageloader.glide.transform.GlideRoundBitmapTransform; +import com.mogo.service.impl.imageloader.glide.utils.DiskLruCacheManager; +import com.mogo.utils.glide.GlideApp; + +/** + * @author congtaowang + * @since 2019-12-23 + *

+ * 实现图片接口 + */ +@Route( path = MogoServicePaths.PATH_IMAGE_LOADER ) +public class GlideImageLoader implements IMogoImageloader { + + private static volatile GlideImageLoader sInstance; + + private GlideImageLoader() { + } + + public static GlideImageLoader getInstance() { + if ( sInstance == null ) { + synchronized ( GlideImageLoader.class ) { + if ( sInstance == null ) { + sInstance = new GlideImageLoader(); + } + } + } + return sInstance; + } + + @Override + public void init( Context context ) { + + } + + @Override + public void displayImage( String url, MogoImageView imageView ) { + displayImage( url, imageView, null ); + } + + @Override + public void displayImage( String url, MogoImageView imageView, int width, int height ) { + displayImage( url, imageView, width, height, null ); + } + + @Override + public void displayImage( String url, MogoImageView imageView, IMogoImageLoaderListener listener ) { + displayImage( url, imageView, 0, 0, listener ); + } + + @Override + public void displayImage( String url, MogoImageView imageView, int width, int height, final IMogoImageLoaderListener listener ) { + + if ( listener != null ) { + listener.onStart(); + } + // 缓存的key + Context context = imageView.getContext().getApplicationContext(); + String key = url + imageView.getParams(); + Bitmap bitmap = DiskLruCacheManager.getInstance( context ).get( key ); + if ( bitmap != null && !bitmap.isRecycled() ) { + imageView.setImageBitmap( bitmap ); + if ( listener != null ) { + listener.onCompleted( bitmap ); + } + return; + } + + RequestOptions options = new RequestOptions() + .placeholder( imageView.getPlaceHolder() ) + .error( imageView.getFailureHolder() ) + .dontAnimate(); + + // 根据imageView的形状,设置相应的transform + boolean isBlur = imageView.isBlur(); + int shape = imageView.getShape(); + if ( isBlur ) { + if ( shape == MogoImageView.SHAPE_CIRCLE ) { + options.transforms( new GlideBlurTransform( context, key, imageView.getBlurRadius() ), + new GlideCircleBitmapTransform( context, key, imageView.getBorderWidth(), imageView.getBorderColor() ) ); + } else if ( shape == MogoImageView.SHAPE_ROUND ) { + options.transforms( new GlideBlurTransform( context, key, imageView.getBlurRadius() ), + new GlideRoundBitmapTransform( context, key, imageView.getRadius(), imageView.getBorderWidth(), imageView.getBorderColor() ) ); + } else { + options.transforms( new GlideBlurTransform( context, key, imageView.getBlurRadius() ) ); + } + } else { + if ( shape == MogoImageView.SHAPE_CIRCLE ) { + options.transforms( new GlideCircleBitmapTransform( context, key, imageView.getBorderWidth(), imageView.getBorderColor() ) ); + } else if ( shape == MogoImageView.SHAPE_ROUND ) { + options.transforms( new GlideRoundBitmapTransform( context, key, imageView.getRadius(), imageView.getBorderWidth(), imageView.getBorderColor() ) ); + } + } + + GlideApp.with( context ) + .asBitmap() + .load( url ) + .apply( options ) + .addListener( new RequestListener< Bitmap >() { + + @Override + public boolean onLoadFailed( @Nullable GlideException e, Object model, Target< Bitmap > target, boolean isFirstResource ) { + if ( listener != null ) { + listener.onFailure( e ); + } + return false; + } + + @Override + public boolean onResourceReady( Bitmap resource, Object model, Target< Bitmap > target, DataSource dataSource, boolean isFirstResource ) { + if ( listener != null ) { + listener.onCompleted( resource ); + } + return false; + } + } ) + .into( imageView ); + } + + @Override + public void downloadImage( Context context, String url, final IMogoImageLoaderListener listener ) { + if ( listener != null ) { + listener.onStart(); + } + GlideApp.with( context.getApplicationContext() ) + .asBitmap() + .load( url ) + .listener( new RequestListener< Bitmap >() { + + @Override + public boolean onLoadFailed( @Nullable GlideException e, Object model, Target< Bitmap > target, boolean isFirstResource ) { + if ( listener != null ) { + listener.onFailure( e ); + } + return false; + } + + @Override + public boolean onResourceReady( Bitmap resource, Object model, Target< Bitmap > target, DataSource dataSource, boolean isFirstResource ) { + if ( listener != null ) { + listener.onCompleted( resource ); + } + return false; + } + } ); + } + + @Override + public void destroy() { + } +} diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideBlurTransform.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideBlurTransform.java new file mode 100644 index 0000000000..65f1c49fbf --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideBlurTransform.java @@ -0,0 +1,39 @@ +package com.mogo.service.impl.imageloader.glide.transform; + +import android.content.Context; +import android.graphics.Bitmap; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +import com.mogo.service.impl.imageloader.glide.utils.DiskLruCacheManager; +import com.mogo.service.impl.imageloader.glide.utils.FastBlurUtil; + +import java.security.MessageDigest; + +public class GlideBlurTransform extends BitmapTransformation { + + private String key; + private Context context; + private int blurRadius; + + public GlideBlurTransform( Context context, String key, int blurRadius ) { + this.context = context; + this.key = key; + this.blurRadius = blurRadius; + } + + @Override + protected Bitmap transform( @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight ) { + Bitmap bitmap = FastBlurUtil.doBlur( toTransform, 8, blurRadius ); + // 缓存高斯模糊图片 + DiskLruCacheManager.getInstance( context ).put( key, bitmap ); + return bitmap; + } + + @Override + public void updateDiskCacheKey( MessageDigest messageDigest ) { + + } +} \ No newline at end of file diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideCircleBitmapTransform.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideCircleBitmapTransform.java new file mode 100644 index 0000000000..340b8d1627 --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideCircleBitmapTransform.java @@ -0,0 +1,68 @@ + +package com.mogo.service.impl.imageloader.glide.transform; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +import com.mogo.service.impl.imageloader.glide.utils.DiskLruCacheManager; + +import java.security.MessageDigest; + +public class GlideCircleBitmapTransform extends BitmapTransformation { + + private int mBorderWidth; + private int mBorderColor; + private String mKey; + private Context mContext; + + public GlideCircleBitmapTransform( Context context, String key, int borderWidth, int borderColor ) { + this.mContext = context; + this.mKey = key; + this.mBorderWidth = borderWidth; + this.mBorderColor = borderColor; + } + + @Override + protected Bitmap transform( @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight ) { + int size = Math.min( toTransform.getWidth(), toTransform.getHeight() ); + int x = ( toTransform.getWidth() - size ) / 2 + mBorderWidth; + int y = ( toTransform.getHeight() - size ) / 2 + mBorderWidth; + int newSize = size - mBorderWidth * 2; + int radius = newSize / 2; + Bitmap bitmap = Bitmap.createBitmap( toTransform, x, y, newSize, newSize ); + Bitmap result = pool.get( newSize, newSize, toTransform.getConfig() ); + if ( result == null ) { + result = Bitmap.createBitmap( newSize, newSize, toTransform.getConfig() ); + } + + Canvas canvas = new Canvas( result ); + if ( mBorderWidth > 0 ) { + Paint borderPaint = new Paint(); + borderPaint.setStyle( Paint.Style.STROKE ); + borderPaint.setStrokeWidth( mBorderWidth ); + borderPaint.setColor( mBorderColor ); + borderPaint.setAntiAlias( true ); + canvas.drawCircle( radius, radius, radius - mBorderWidth / 2, borderPaint ); + } + Paint paint = new Paint(); + paint.setShader( new BitmapShader( bitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP ) ); + paint.setAntiAlias( true ); + canvas.drawCircle( radius, radius, radius - mBorderWidth, paint ); + + DiskLruCacheManager.getInstance( mContext ).put( mKey, result ); + + return result; + } + + @Override + public void updateDiskCacheKey( MessageDigest messageDigest ) { + + } +} \ No newline at end of file diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideRoundBitmapTransform.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideRoundBitmapTransform.java new file mode 100644 index 0000000000..261d7f9be4 --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/transform/GlideRoundBitmapTransform.java @@ -0,0 +1,69 @@ + +package com.mogo.service.impl.imageloader.glide.transform; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +import com.mogo.service.impl.imageloader.glide.utils.DiskLruCacheManager; + +import java.security.MessageDigest; + +public class GlideRoundBitmapTransform extends BitmapTransformation { + + private int mRadius; + private int mBorderWidth; + private int mBorderColor; + private String mKey; + private Context mContext; + + public GlideRoundBitmapTransform( Context context, String key, int radius, int borderWidth, int borderColor ) { + this.mContext = context; + this.mKey = key; + this.mRadius = radius; + this.mBorderWidth = borderWidth; + this.mBorderColor = borderColor; + } + + @Override + protected Bitmap transform( @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight ) { + if ( mRadius == 0 && mBorderWidth == 0 ) { + return toTransform; + } + int width = toTransform.getWidth(); + int height = toTransform.getHeight(); + RectF rectF = new RectF( mBorderWidth, mBorderWidth, width - mBorderWidth, height - mBorderWidth ); + Bitmap result = pool.get( width, height, toTransform.getConfig() ); + if ( result == null ) { + result = toTransform.copy( toTransform.getConfig(), true ); + } + Canvas canvas = new Canvas( result ); + Paint paint = new Paint(); + paint.setShader( new BitmapShader( toTransform, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP ) ); + paint.setAntiAlias( true ); + canvas.drawRoundRect( rectF, mRadius, mRadius, paint ); + if ( mBorderWidth > 0 ) { + Paint borderPaint = new Paint(); + borderPaint.setStyle( Paint.Style.STROKE ); + borderPaint.setStrokeWidth( mBorderWidth ); + borderPaint.setColor( mBorderColor ); + borderPaint.setAntiAlias( true ); + canvas.drawRoundRect( rectF, mRadius, mRadius, borderPaint ); + } + DiskLruCacheManager.getInstance( mContext ).put( mKey, result ); + + return result; + } + + @Override + public void updateDiskCacheKey( MessageDigest messageDigest ) { + + } +} \ No newline at end of file diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/DiskLruCache.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/DiskLruCache.java new file mode 100644 index 0000000000..b0beddced2 --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/DiskLruCache.java @@ -0,0 +1,974 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mogo.service.impl.imageloader.glide.utils; + +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A cache that uses a bounded amount of space on a filesystem. Each cache + * entry has a string key and a fixed number of values. Each key must match + * the regex [a-z0-9_-]{1,64}. Values are byte sequences, + * accessible as streams or files. Each value must be between {@code 0} and + * {@code Integer.MAX_VALUE} bytes in length. + * + *

The cache stores its data in a directory on the filesystem. This + * directory must be exclusive to the cache; the cache may delete or overwrite + * files from its directory. It is an error for multiple processes to use the + * same cache directory at the same time. + * + *

This cache limits the number of bytes that it will store on the + * filesystem. When the number of stored bytes exceeds the limit, the cache will + * remove entries in the background until the limit is satisfied. The limit is + * not strict: the cache may temporarily exceed it while waiting for files to be + * deleted. The limit does not include filesystem overhead or the cache + * journal so space-sensitive applications should set a conservative limit. + * + *

Clients call {@link #edit} to create or update the values of an entry. An + * entry may have only one editor at one time; if a value is not available to be + * edited then {@link #edit} will return null. + *

    + *
  • When an entry is being created it is necessary to + * supply a full set of values; the empty value should be used as a + * placeholder if necessary. + *
  • When an entry is being edited, it is not necessary + * to supply data for every value; values default to their previous + * value. + *
+ * Every {@link #edit} call must be matched by a call to {@link Editor#commit} + * or {@link Editor#abort}. Committing is atomic: a read observes the full set + * of values as they were before or after the commit, but never a mix of values. + * + *

Clients call {@link #get} to read a snapshot of an entry. The read will + * observe the value at the time that {@link #get} was called. Updates and + * removals after the call do not impact ongoing reads. + * + *

This class is tolerant of some I/O errors. If files are missing from the + * filesystem, the corresponding entries will be dropped from the cache. If + * an error occurs while writing a cache value, the edit will fail silently. + * Callers should handle other problems by catching {@code IOException} and + * responding appropriately. + */ +final class DiskLruCache implements Closeable { + static final String JOURNAL_FILE = "journal"; + static final String JOURNAL_FILE_TEMP = "journal.tmp"; + static final String JOURNAL_FILE_BACKUP = "journal.bkp"; + static final String MAGIC = "libcore.io.DiskLruCache"; + static final String VERSION_1 = "1"; + static final long ANY_SEQUENCE_NUMBER = -1; + static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,64}"); + private static final String CLEAN = "CLEAN"; + private static final String DIRTY = "DIRTY"; + private static final String REMOVE = "REMOVE"; + private static final String READ = "READ"; + + /* + * This cache uses a journal file named "journal". A typical journal file + * looks like this: + * libcore.io.DiskLruCache + * 1 + * 100 + * 2 + * + * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 + * DIRTY 335c4c6028171cfddfbaae1a9c313c52 + * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 + * REMOVE 335c4c6028171cfddfbaae1a9c313c52 + * DIRTY 1ab96a171faeeee38496d8b330771a7a + * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 + * READ 335c4c6028171cfddfbaae1a9c313c52 + * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 + * + * The first five lines of the journal form its header. They are the + * constant string "libcore.io.DiskLruCache", the disk cache's version, + * the application's version, the value count, and a blank line. + * + * Each of the subsequent lines in the file is a record of the state of a + * cache entry. Each line contains space-separated values: a state, a key, + * and optional state-specific values. + * o DIRTY lines track that an entry is actively being created or updated. + * Every successful DIRTY action should be followed by a CLEAN or REMOVE + * action. DIRTY lines without a matching CLEAN or REMOVE indicate that + * temporary files may need to be deleted. + * o CLEAN lines track a cache entry that has been successfully published + * and may be read. A publish line is followed by the lengths of each of + * its values. + * o READ lines track accesses for LRU. + * o REMOVE lines track entries that have been deleted. + * + * The journal file is appended to as cache operations occur. The journal may + * occasionally be compacted by dropping redundant lines. A temporary file named + * "journal.tmp" will be used during compaction; that file should be deleted if + * it exists when the cache is opened. + */ + + private final File directory; + private final File journalFile; + private final File journalFileTmp; + private final File journalFileBackup; + private final int appVersion; + private long maxSize; + private int maxFileCount; + private final int valueCount; + private long size = 0; + private int fileCount = 0; + private Writer journalWriter; + private final LinkedHashMap lruEntries = + new LinkedHashMap(0, 0.75f, true); + private int redundantOpCount; + + /** + * To differentiate between old and current snapshots, each entry is given + * a sequence number each time an edit is committed. A snapshot is stale if + * its sequence number is not equal to its entry's sequence number. + */ + private long nextSequenceNumber = 0; + + /** This cache uses a single background thread to evict entries. */ + final ThreadPoolExecutor executorService = + new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue()); + private final Callable cleanupCallable = new Callable() { + public Void call() throws Exception { + synchronized (DiskLruCache.this) { + if (journalWriter == null) { + return null; // Closed. + } + trimToSize(); + trimToFileCount(); + if (journalRebuildRequired()) { + rebuildJournal(); + redundantOpCount = 0; + } + } + return null; + } + }; + + private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount) { + this.directory = directory; + this.appVersion = appVersion; + this.journalFile = new File(directory, JOURNAL_FILE); + this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP); + this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP); + this.valueCount = valueCount; + this.maxSize = maxSize; + this.maxFileCount = maxFileCount; + } + + /** + * Opens the cache in {@code directory}, creating a cache if none exists + * there. + * + * @param directory a writable directory + * @param valueCount the number of values per cache entry. Must be positive. + * @param maxSize the maximum number of bytes this cache should use to store + * @param maxFileCount the maximum file count this cache should store + * @throws IOException if reading or writing the cache directory fails + */ + public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize, int maxFileCount) + throws IOException { + if (maxSize <= 0) { + throw new IllegalArgumentException("maxSize <= 0"); + } + if (maxFileCount <= 0) { + throw new IllegalArgumentException("maxFileCount <= 0"); + } + if (valueCount <= 0) { + throw new IllegalArgumentException("valueCount <= 0"); + } + + // If a bkp file exists, use it instead. + File backupFile = new File(directory, JOURNAL_FILE_BACKUP); + if (backupFile.exists()) { + File journalFile = new File(directory, JOURNAL_FILE); + // If journal file also exists just delete backup file. + if (journalFile.exists()) { + backupFile.delete(); + } else { + renameTo(backupFile, journalFile, false); + } + } + + // Prefer to pick up where we left off. + DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount); + if (cache.journalFile.exists()) { + try { + cache.readJournal(); + cache.processJournal(); + cache.journalWriter = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(cache.journalFile, true), Util.US_ASCII)); + return cache; + } catch (IOException journalIsCorrupt) { + System.out + .println("DiskLruCache " + + directory + + " is corrupt: " + + journalIsCorrupt.getMessage() + + ", removing"); + cache.delete(); + } + } + + // Create a new empty cache. + directory.mkdirs(); + cache = new DiskLruCache(directory, appVersion, valueCount, maxSize, maxFileCount); + cache.rebuildJournal(); + return cache; + } + + private void readJournal() throws IOException { + StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII); + try { + String magic = reader.readLine(); + String version = reader.readLine(); + String appVersionString = reader.readLine(); + String valueCountString = reader.readLine(); + String blank = reader.readLine(); + if (!MAGIC.equals(magic) + || !VERSION_1.equals(version) + || !Integer.toString(appVersion).equals(appVersionString) + || !Integer.toString(valueCount).equals(valueCountString) + || !"".equals(blank)) { + throw new IOException("unexpected journal header: [" + magic + ", " + version + ", " + + valueCountString + ", " + blank + "]"); + } + + int lineCount = 0; + while (true) { + try { + readJournalLine(reader.readLine()); + lineCount++; + } catch (EOFException endOfJournal) { + break; + } + } + redundantOpCount = lineCount - lruEntries.size(); + } finally { + Util.closeQuietly(reader); + } + } + + private void readJournalLine(String line) throws IOException { + int firstSpace = line.indexOf(' '); + if (firstSpace == -1) { + throw new IOException("unexpected journal line: " + line); + } + + int keyBegin = firstSpace + 1; + int secondSpace = line.indexOf(' ', keyBegin); + final String key; + if (secondSpace == -1) { + key = line.substring(keyBegin); + if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) { + lruEntries.remove(key); + return; + } + } else { + key = line.substring(keyBegin, secondSpace); + } + + Entry entry = lruEntries.get(key); + if (entry == null) { + entry = new Entry(key); + lruEntries.put(key, entry); + } + + if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) { + String[] parts = line.substring(secondSpace + 1).split(" "); + entry.readable = true; + entry.currentEditor = null; + entry.setLengths(parts); + } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) { + entry.currentEditor = new Editor(entry); + } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) { + // This work was already done by calling lruEntries.get(). + } else { + throw new IOException("unexpected journal line: " + line); + } + } + + /** + * Computes the initial size and collects garbage as a part of opening the + * cache. Dirty entries are assumed to be inconsistent and will be deleted. + */ + private void processJournal() throws IOException { + deleteIfExists(journalFileTmp); + for (Iterator i = lruEntries.values().iterator(); i.hasNext(); ) { + Entry entry = i.next(); + if (entry.currentEditor == null) { + for (int t = 0; t < valueCount; t++) { + size += entry.lengths[t]; + fileCount++; + } + } else { + entry.currentEditor = null; + for (int t = 0; t < valueCount; t++) { + deleteIfExists(entry.getCleanFile(t)); + deleteIfExists(entry.getDirtyFile(t)); + } + i.remove(); + } + } + } + + /** + * Creates a new journal that omits redundant information. This replaces the + * current journal if it exists. + */ + private synchronized void rebuildJournal() throws IOException { + if (journalWriter != null) { + journalWriter.close(); + } + + Writer writer = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII)); + try { + writer.write(MAGIC); + writer.write("\n"); + writer.write(VERSION_1); + writer.write("\n"); + writer.write(Integer.toString(appVersion)); + writer.write("\n"); + writer.write(Integer.toString(valueCount)); + writer.write("\n"); + writer.write("\n"); + + for (Entry entry : lruEntries.values()) { + if (entry.currentEditor != null) { + writer.write(DIRTY + ' ' + entry.key + '\n'); + } else { + writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); + } + } + } finally { + writer.close(); + } + + if (journalFile.exists()) { + renameTo(journalFile, journalFileBackup, true); + } + renameTo(journalFileTmp, journalFile, false); + journalFileBackup.delete(); + + journalWriter = new BufferedWriter( + new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII)); + } + + private static void deleteIfExists(File file) throws IOException { + if (file.exists() && !file.delete()) { + throw new IOException(); + } + } + + private static void renameTo(File from, File to, boolean deleteDestination) throws IOException { + if (deleteDestination) { + deleteIfExists(to); + } + if (!from.renameTo(to)) { + throw new IOException(); + } + } + + /** + * Returns a snapshot of the entry named {@code key}, or null if it doesn't + * exist is not currently readable. If a value is returned, it is moved to + * the head of the LRU queue. + */ + public synchronized Snapshot get(String key) throws IOException { + checkNotClosed(); + validateKey(key); + Entry entry = lruEntries.get(key); + if (entry == null) { + return null; + } + + if (!entry.readable) { + return null; + } + + // Open all streams eagerly to guarantee that we see a single published + // snapshot. If we opened streams lazily then the streams could come + // from different edits. + File[] files = new File[valueCount]; + InputStream[] ins = new InputStream[valueCount]; + try { + File file; + for (int i = 0; i < valueCount; i++) { + file = entry.getCleanFile(i); + files[i] = file; + ins[i] = new FileInputStream(file); + } + } catch (FileNotFoundException e) { + // A file must have been deleted manually! + for (int i = 0; i < valueCount; i++) { + if (ins[i] != null) { + Util.closeQuietly(ins[i]); + } else { + break; + } + } + return null; + } + + redundantOpCount++; + journalWriter.append(READ + ' ' + key + '\n'); + if (journalRebuildRequired()) { + executorService.submit(cleanupCallable); + } + + return new Snapshot(key, entry.sequenceNumber, files, ins, entry.lengths); + } + + /** + * Returns an editor for the entry named {@code key}, or null if another + * edit is in progress. + */ + public Editor edit(String key) throws IOException { + return edit(key, ANY_SEQUENCE_NUMBER); + } + + private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { + checkNotClosed(); + validateKey(key); + Entry entry = lruEntries.get(key); + if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null + || entry.sequenceNumber != expectedSequenceNumber)) { + return null; // Snapshot is stale. + } + if (entry == null) { + entry = new Entry(key); + lruEntries.put(key, entry); + } else if (entry.currentEditor != null) { + return null; // Another edit is in progress. + } + + Editor editor = new Editor(entry); + entry.currentEditor = editor; + + // Flush the journal before creating files to prevent file leaks. + journalWriter.write(DIRTY + ' ' + key + '\n'); + journalWriter.flush(); + return editor; + } + + /** Returns the directory where this cache stores its data. */ + public File getDirectory() { + return directory; + } + + /** + * Returns the maximum number of bytes that this cache should use to store + * its data. + */ + public synchronized long getMaxSize() { + return maxSize; + } + + /** Returns the maximum number of files that this cache should store */ + public synchronized int getMaxFileCount() { + return maxFileCount; + } + + /** + * Changes the maximum number of bytes the cache can store and queues a job + * to trim the existing store, if necessary. + */ + public synchronized void setMaxSize(long maxSize) { + this.maxSize = maxSize; + executorService.submit(cleanupCallable); + } + + /** + * Returns the number of bytes currently being used to store the values in + * this cache. This may be greater than the max size if a background + * deletion is pending. + */ + public synchronized long size() { + return size; + } + + /** + * Returns the number of files currently being used to store the values in + * this cache. This may be greater than the max file count if a background + * deletion is pending. + */ + public synchronized long fileCount() { + return fileCount; + } + + private synchronized void completeEdit(Editor editor, boolean success) throws IOException { + Entry entry = editor.entry; + if (entry.currentEditor != editor) { + throw new IllegalStateException(); + } + + // If this edit is creating the entry for the first time, every index must have a value. + if (success && !entry.readable) { + for (int i = 0; i < valueCount; i++) { + if (!editor.written[i]) { + editor.abort(); + throw new IllegalStateException("Newly created entry didn't create value for index " + i); + } + if (!entry.getDirtyFile(i).exists()) { + editor.abort(); + return; + } + } + } + + for (int i = 0; i < valueCount; i++) { + File dirty = entry.getDirtyFile(i); + if (success) { + if (dirty.exists()) { + File clean = entry.getCleanFile(i); + dirty.renameTo(clean); + long oldLength = entry.lengths[i]; + long newLength = clean.length(); + entry.lengths[i] = newLength; + size = size - oldLength + newLength; + fileCount++; + } + } else { + deleteIfExists(dirty); + } + } + + redundantOpCount++; + entry.currentEditor = null; + if (entry.readable | success) { + entry.readable = true; + journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); + if (success) { + entry.sequenceNumber = nextSequenceNumber++; + } + } else { + lruEntries.remove(entry.key); + journalWriter.write(REMOVE + ' ' + entry.key + '\n'); + } + journalWriter.flush(); + + if (size > maxSize || fileCount > maxFileCount || journalRebuildRequired()) { + executorService.submit(cleanupCallable); + } + } + + /** + * We only rebuild the journal when it will halve the size of the journal + * and eliminate at least 2000 ops. + */ + private boolean journalRebuildRequired() { + final int redundantOpCompactThreshold = 2000; + return redundantOpCount >= redundantOpCompactThreshold // + && redundantOpCount >= lruEntries.size(); + } + + /** + * Drops the entry for {@code key} if it exists and can be removed. Entries + * actively being edited cannot be removed. + * + * @return true if an entry was removed. + */ + public synchronized boolean remove(String key) throws IOException { + checkNotClosed(); + validateKey(key); + Entry entry = lruEntries.get(key); + if (entry == null || entry.currentEditor != null) { + return false; + } + + for (int i = 0; i < valueCount; i++) { + File file = entry.getCleanFile(i); + if (file.exists() && !file.delete()) { + throw new IOException("failed to delete " + file); + } + size -= entry.lengths[i]; + fileCount--; + entry.lengths[i] = 0; + } + + redundantOpCount++; + journalWriter.append(REMOVE + ' ' + key + '\n'); + lruEntries.remove(key); + + if (journalRebuildRequired()) { + executorService.submit(cleanupCallable); + } + + return true; + } + + /** Returns true if this cache has been closed. */ + public synchronized boolean isClosed() { + return journalWriter == null; + } + + private void checkNotClosed() { + if (journalWriter == null) { + throw new IllegalStateException("cache is closed"); + } + } + + /** Force buffered operations to the filesystem. */ + public synchronized void flush() throws IOException { + checkNotClosed(); + trimToSize(); + trimToFileCount(); + journalWriter.flush(); + } + + /** Closes this cache. Stored values will remain on the filesystem. */ + public synchronized void close() throws IOException { + if (journalWriter == null) { + return; // Already closed. + } + for (Entry entry : new ArrayList(lruEntries.values())) { + if (entry.currentEditor != null) { + entry.currentEditor.abort(); + } + } + trimToSize(); + trimToFileCount(); + journalWriter.close(); + journalWriter = null; + } + + private void trimToSize() throws IOException { + while (size > maxSize) { + Map.Entry toEvict = lruEntries.entrySet().iterator().next(); + remove(toEvict.getKey()); + } + } + + private void trimToFileCount() throws IOException { + while (fileCount > maxFileCount) { + Map.Entry toEvict = lruEntries.entrySet().iterator().next(); + remove(toEvict.getKey()); + } + } + + /** + * Closes the cache and deletes all of its stored values. This will delete + * all files in the cache directory including files that weren't created by + * the cache. + */ + public void delete() throws IOException { + close(); + Util.deleteContents(directory); + } + + private void validateKey(String key) { + Matcher matcher = LEGAL_KEY_PATTERN.matcher(key); + if (!matcher.matches()) { + throw new IllegalArgumentException("keys must match regex [a-z0-9_-]{1,64}: \"" + key + "\""); + } + } + + private static String inputStreamToString(InputStream in) throws IOException { + return Util.readFully(new InputStreamReader(in, Util.UTF_8)); + } + + /** A snapshot of the values for an entry. */ + public final class Snapshot implements Closeable { + private final String key; + private final long sequenceNumber; + private File[] files; + private final InputStream[] ins; + private final long[] lengths; + + private Snapshot(String key, long sequenceNumber, File[] files, InputStream[] ins, long[] lengths) { + this.key = key; + this.sequenceNumber = sequenceNumber; + this.files = files; + this.ins = ins; + this.lengths = lengths; + } + + /** + * Returns an editor for this snapshot's entry, or null if either the + * entry has changed since this snapshot was created or if another edit + * is in progress. + */ + public Editor edit() throws IOException { + return DiskLruCache.this.edit(key, sequenceNumber); + } + + /** Returns file with the value for {@code index}. */ + public File getFile(int index) { + return files[index]; + } + + /** Returns the unbuffered stream with the value for {@code index}. */ + public InputStream getInputStream(int index) { + return ins[index]; + } + + /** Returns the string value for {@code index}. */ + public String getString(int index) throws IOException { + return inputStreamToString(getInputStream(index)); + } + + /** Returns the byte length of the value for {@code index}. */ + public long getLength(int index) { + return lengths[index]; + } + + public void close() { + for (InputStream in : ins) { + Util.closeQuietly(in); + } + } + } + + private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() { + @Override + public void write(int b) throws IOException { + // Eat all writes silently. Nom nom. + } + }; + + /** Edits the values for an entry. */ + public final class Editor { + private final Entry entry; + private final boolean[] written; + private boolean hasErrors; + private boolean committed; + + private Editor(Entry entry) { + this.entry = entry; + this.written = (entry.readable) ? null : new boolean[valueCount]; + } + + /** + * Returns an unbuffered input stream to read the last committed value, + * or null if no value has been committed. + */ + public InputStream newInputStream(int index) throws IOException { + synchronized (DiskLruCache.this) { + if (entry.currentEditor != this) { + throw new IllegalStateException(); + } + if (!entry.readable) { + return null; + } + try { + return new FileInputStream(entry.getCleanFile(index)); + } catch (FileNotFoundException e) { + return null; + } + } + } + + /** + * Returns the last committed value as a string, or null if no value + * has been committed. + */ + public String getString(int index) throws IOException { + InputStream in = newInputStream(index); + return in != null ? inputStreamToString(in) : null; + } + + /** + * Returns a new unbuffered output stream to write the value at + * {@code index}. If the underlying output stream encounters errors + * when writing to the filesystem, this edit will be aborted when + * {@link #commit} is called. The returned output stream does not throw + * IOExceptions. + */ + public OutputStream newOutputStream(int index) throws IOException { + synchronized (DiskLruCache.this) { + if (entry.currentEditor != this) { + throw new IllegalStateException(); + } + if (!entry.readable) { + written[index] = true; + } + File dirtyFile = entry.getDirtyFile(index); + FileOutputStream outputStream; + try { + outputStream = new FileOutputStream(dirtyFile); + } catch (FileNotFoundException e) { + // Attempt to recreate the cache directory. + directory.mkdirs(); + try { + outputStream = new FileOutputStream(dirtyFile); + } catch (FileNotFoundException e2) { + // We are unable to recover. Silently eat the writes. + return NULL_OUTPUT_STREAM; + } + } + return new FaultHidingOutputStream(outputStream); + } + } + + /** Sets the value at {@code index} to {@code value}. */ + public void set(int index, String value) throws IOException { + Writer writer = null; + try { + writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8); + writer.write(value); + } finally { + Util.closeQuietly(writer); + } + } + + /** + * Commits this edit so it is visible to readers. This releases the + * edit lock so another edit may be started on the same key. + */ + public void commit() throws IOException { + if (hasErrors) { + completeEdit(this, false); + remove(entry.key); // The previous entry is stale. + } else { + completeEdit(this, true); + } + committed = true; + } + + /** + * Aborts this edit. This releases the edit lock so another edit may be + * started on the same key. + */ + public void abort() throws IOException { + completeEdit(this, false); + } + + public void abortUnlessCommitted() { + if (!committed) { + try { + abort(); + } catch (IOException ignored) { + } + } + } + + private class FaultHidingOutputStream extends FilterOutputStream { + private FaultHidingOutputStream(OutputStream out) { + super(out); + } + + @Override public void write(int oneByte) { + try { + out.write(oneByte); + } catch (IOException e) { + hasErrors = true; + } + } + + @Override public void write(byte[] buffer, int offset, int length) { + try { + out.write(buffer, offset, length); + } catch (IOException e) { + hasErrors = true; + } + } + + @Override public void close() { + try { + out.close(); + } catch (IOException e) { + hasErrors = true; + } + } + + @Override public void flush() { + try { + out.flush(); + } catch (IOException e) { + hasErrors = true; + } + } + } + } + + private final class Entry { + private final String key; + + /** Lengths of this entry's files. */ + private final long[] lengths; + + /** True if this entry has ever been published. */ + private boolean readable; + + /** The ongoing edit or null if this entry is not being edited. */ + private Editor currentEditor; + + /** The sequence number of the most recently committed edit to this entry. */ + private long sequenceNumber; + + private Entry(String key) { + this.key = key; + this.lengths = new long[valueCount]; + } + + public String getLengths() throws IOException { + StringBuilder result = new StringBuilder(); + for (long size : lengths) { + result.append(' ').append(size); + } + return result.toString(); + } + + /** Set lengths using decimal numbers like "10123". */ + private void setLengths(String[] strings) throws IOException { + if (strings.length != valueCount) { + throw invalidLengths(strings); + } + + try { + for (int i = 0; i < strings.length; i++) { + lengths[i] = Long.parseLong(strings[i]); + } + } catch (NumberFormatException e) { + throw invalidLengths(strings); + } + } + + private IOException invalidLengths(String[] strings) throws IOException { + throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings)); + } + + public File getCleanFile(int i) { + return new File(directory, key + "" + i); + } + + public File getDirtyFile(int i) { + return new File(directory, key + "" + i + ".tmp"); + } + } +} diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/DiskLruCacheManager.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/DiskLruCacheManager.java new file mode 100644 index 0000000000..0c38ed7c12 --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/DiskLruCacheManager.java @@ -0,0 +1,101 @@ +package com.mogo.service.impl.imageloader.glide.utils; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.text.TextUtils; + +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.MessageDigest; + +/******************************************************************************* + * Description: 用于缓存经过高斯模糊的图片 + * + * Author: Freeman + * + * Date: 2018/9/4 + * + * Copyright: all rights reserved by Freeman. + *******************************************************************************/ +public class DiskLruCacheManager { + + private DiskLruCache diskLruCache; + private static DiskLruCacheManager instance; + + private final int MAX_CACHE_SIZE = 64 * 1024 * 1024; + + private DiskLruCacheManager(Context context) { + try { + diskLruCache = DiskLruCache.open(context.getCacheDir(), 1, 1, + MAX_CACHE_SIZE, Integer.MAX_VALUE); + } catch (Exception e) { + e.printStackTrace(System.err); + } + } + + public static DiskLruCacheManager getInstance(Context context) { + if (instance == null) { + synchronized (DiskLruCacheManager.class) { + if (instance == null) { + instance = new DiskLruCacheManager(context.getApplicationContext()); + } + } + } + + return instance; + } + + public void put(String url, Bitmap bitmap) { + if (TextUtils.isEmpty(url) || bitmap == null || bitmap.isRecycled()) { + return; + } + + try { + DiskLruCache.Editor editor = diskLruCache.edit(getKey(url)); + OutputStream outputStream = editor.newOutputStream(0); + if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)) { + editor.commit(); + } + diskLruCache.flush(); + } catch (Exception e) { + e.printStackTrace(System.err); + } + } + + public Bitmap get(String url) { + try { + DiskLruCache.Snapshot snapshot = diskLruCache.get(getKey(url)); + if (snapshot != null) { + InputStream inputStream = snapshot.getInputStream(0); + return BitmapFactory.decodeStream(inputStream); + } + } catch (Exception e) { + e.printStackTrace(System.err); + } + + return null; + } + + public static String getKey(String url) { + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + byte[] md5 = digest.digest(url.getBytes()); + BigInteger bigInteger = new BigInteger(1, md5); + return bigInteger.toString(16); + } catch (Exception e) { + e.printStackTrace(System.err); + } + + return null; + } + + public void close() { + try { + diskLruCache.close(); + } catch (Exception e) { + e.printStackTrace(System.err); + } + } +} diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/FastBlurUtil.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/FastBlurUtil.java new file mode 100755 index 0000000000..9de44f0cd3 --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/FastBlurUtil.java @@ -0,0 +1,241 @@ +package com.mogo.service.impl.imageloader.glide.utils; + +import android.graphics.Bitmap; + +/** + * Created by jay on 11/7/15. + */ +public class FastBlurUtil { + + public static Bitmap doBlur(Bitmap sentBitmap, int scaleRadius, int radius) { + + // Stack Blur v1.0 from + // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html + // + // Java Author: Mario Klingemann + // http://incubator.quasimondo.com + // created Feburary 29, 2004 + // Android port : Yahel Bouaziz + // http://www.kayenko.com + // ported april 5th, 2012 + + // This is a compromise between Gaussian Blur and Box blur + // It creates much better looking blurs than Box Blur, but is + // 7x faster than my Gaussian Blur implementation. + // + // I called it Stack Blur because this describes best how this + // filter works internally: it creates a kind of moving stack + // of colors whilst scanning through the image. Thereby it + // just has to add one new block of color to the right side + // of the stack and remove the leftmost color. The remaining + // colors on the topmost layer of the stack are either added on + // or reduced by one, depending on if they are on the right or + // on the left side of the stack. + // + // If you are using this algorithm in your code please add + // the following line: + // + // Stack Blur Algorithm by Mario Klingemann + if (scaleRadius > 0) { + sentBitmap = Bitmap.createScaledBitmap(sentBitmap, sentBitmap.getWidth() / scaleRadius, + sentBitmap.getHeight() / scaleRadius, false); + } + Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true); + + if (radius < 1) { + return (null); + } + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + int[] pix = new int[w * h]; + bitmap.getPixels(pix, 0, w, 0, 0, w, h); + + int wm = w - 1; + int hm = h - 1; + int wh = w * h; + int div = radius + radius + 1; + + int r[] = new int[wh]; + int g[] = new int[wh]; + int b[] = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int vmin[] = new int[Math.max(w, h)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int dv[] = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + + bitmap.setPixels(pix, 0, w, 0, 0, w, h); + + return (bitmap); + } +} diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/StrictLineReader.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/StrictLineReader.java new file mode 100644 index 0000000000..6083bb107a --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/StrictLineReader.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mogo.service.impl.imageloader.glide.utils; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; + +/** + * Buffers input from an {@link InputStream} for reading lines. + * + *

This class is used for buffered reading of lines. For purposes of this class, a line ends + * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated + * line at end of input is invalid and will be ignored, the caller may use {@code + * hasUnterminatedLine()} to detect it after catching the {@code EOFException}. + * + *

This class is intended for reading input that strictly consists of lines, such as line-based + * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction + * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different + * end-of-input reporting and a more restrictive definition of a line. + * + *

This class supports only charsets that encode '\r' and '\n' as a single byte with value 13 + * and 10, respectively, and the representation of no other character contains these values. + * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1. + * The default charset is US_ASCII. + */ +class StrictLineReader implements Closeable { + private static final byte CR = (byte) '\r'; + private static final byte LF = (byte) '\n'; + + private final InputStream in; + private final Charset charset; + + /* + * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end + * and the data in the range [pos, end) is buffered for reading. At end of input, if there is + * an unterminated line, we set end == -1, otherwise end == pos. If the underlying + * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1. + */ + private byte[] buf; + private int pos; + private int end; + + /** + * Constructs a new {@code LineReader} with the specified charset and the default capacity. + * + * @param in the {@code InputStream} to read data from. + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are + * supported. + * @throws NullPointerException if {@code in} or {@code charset} is null. + * @throws IllegalArgumentException if the specified charset is not supported. + */ + public StrictLineReader(InputStream in, Charset charset) { + this(in, 8192, charset); + } + + /** + * Constructs a new {@code LineReader} with the specified capacity and charset. + * + * @param in the {@code InputStream} to read data from. + * @param capacity the capacity of the buffer. + * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are + * supported. + * @throws NullPointerException if {@code in} or {@code charset} is null. + * @throws IllegalArgumentException if {@code capacity} is negative or zero + * or the specified charset is not supported. + */ + public StrictLineReader(InputStream in, int capacity, Charset charset) { + if (in == null || charset == null) { + throw new NullPointerException(); + } + if (capacity < 0) { + throw new IllegalArgumentException("capacity <= 0"); + } + if (!(charset.equals(Util.US_ASCII))) { + throw new IllegalArgumentException("Unsupported encoding"); + } + + this.in = in; + this.charset = charset; + buf = new byte[capacity]; + } + + /** + * Closes the reader by closing the underlying {@code InputStream} and + * marking this reader as closed. + * + * @throws IOException for errors when closing the underlying {@code InputStream}. + */ + public void close() throws IOException { + synchronized (in) { + if (buf != null) { + buf = null; + in.close(); + } + } + } + + /** + * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"}, + * this end of line marker is not included in the result. + * + * @return the next line from the input. + * @throws IOException for underlying {@code InputStream} errors. + * @throws EOFException for the end of source stream. + */ + public String readLine() throws IOException { + synchronized (in) { + if (buf == null) { + throw new IOException("LineReader is closed"); + } + + // Read more data if we are at the end of the buffered data. + // Though it's an error to read after an exception, we will let {@code fillBuf()} + // throw again if that happens; thus we need to handle end == -1 as well as end == pos. + if (pos >= end) { + fillBuf(); + } + // Try to find LF in the buffered data and return the line if successful. + for (int i = pos; i != end; ++i) { + if (buf[i] == LF) { + int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i; + String res = new String(buf, pos, lineEnd - pos, charset.name()); + pos = i + 1; + return res; + } + } + + // Let's anticipate up to 80 characters on top of those already read. + ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) { + @Override + public String toString() { + int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count; + try { + return new String(buf, 0, length, charset.name()); + } catch (UnsupportedEncodingException e) { + throw new AssertionError(e); // Since we control the charset this will never happen. + } + } + }; + + while (true) { + out.write(buf, pos, end - pos); + // Mark unterminated line in case fillBuf throws EOFException or IOException. + end = -1; + fillBuf(); + // Try to find LF in the buffered data and return the line if successful. + for (int i = pos; i != end; ++i) { + if (buf[i] == LF) { + if (i != pos) { + out.write(buf, pos, i - pos); + } + pos = i + 1; + return out.toString(); + } + } + } + } + } + + /** + * Reads new input data into the buffer. Call only with pos == end or end == -1, + * depending on the desired outcome if the function throws. + */ + private void fillBuf() throws IOException { + int result = in.read(buf, 0, buf.length); + if (result == -1) { + throw new EOFException(); + } + pos = 0; + end = result; + } +} + diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/Util.java b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/Util.java new file mode 100644 index 0000000000..eaf34b7a49 --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/imageloader/glide/utils/Util.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mogo.service.impl.imageloader.glide.utils; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.io.StringWriter; +import java.nio.charset.Charset; + +/** Junk drawer of utility methods. */ +final class Util { + static final Charset US_ASCII = Charset.forName("US-ASCII"); + static final Charset UTF_8 = Charset.forName("UTF-8"); + + private Util() { + } + + static String readFully(Reader reader) throws IOException { + try { + StringWriter writer = new StringWriter(); + char[] buffer = new char[1024]; + int count; + while ((count = reader.read(buffer)) != -1) { + writer.write(buffer, 0, count); + } + return writer.toString(); + } finally { + reader.close(); + } + } + + /** + * Deletes the contents of {@code dir}. Throws an IOException if any file + * could not be deleted, or if {@code dir} is not a readable directory. + */ + static void deleteContents(File dir) throws IOException { + File[] files = dir.listFiles(); + if (files == null) { + throw new IOException("not a readable directory: " + dir); + } + for (File file : files) { + if (file.isDirectory()) { + deleteContents(file); + } + if (!file.delete()) { + throw new IOException("failed to delete file: " + file); + } + } + } + + static void closeQuietly(/*Auto*/Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (RuntimeException rethrown) { + throw rethrown; + } catch (Exception ignored) { + } + } + } +} \ No newline at end of file diff --git a/services/mogo-service/src/main/java/com/mogo/service/impl/map/MogoMapService.java b/services/mogo-service/src/main/java/com/mogo/service/impl/map/MogoMapService.java new file mode 100644 index 0000000000..e1ab65de28 --- /dev/null +++ b/services/mogo-service/src/main/java/com/mogo/service/impl/map/MogoMapService.java @@ -0,0 +1,68 @@ +package com.mogo.service.impl.map; + +import android.content.Context; + +import com.alibaba.android.arouter.facade.annotation.Route; +import com.mogo.map.MogoMap; +import com.mogo.map.location.IMogoLocationClient; +import com.mogo.map.location.MogoLocationClient; +import com.mogo.map.marker.IMogoMarker; +import com.mogo.map.marker.MogoMarkerOptions; +import com.mogo.map.search.IMogoGeoSearch; +import com.mogo.map.search.IMogoInputtipsSearch; +import com.mogo.map.search.MogoGeoSearch; +import com.mogo.map.search.MogoInputtipsSearch; +import com.mogo.map.search.query.MogoInputtipsQuery; +import com.mogo.service.map.IMogoMapService; +import com.mogo.service.MogoServicePaths; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author congtaowang + * @since 2019-12-18 + *

+ * 地图对外地接口 + */ +@Route( path = MogoServicePaths.PATH_MAP_SERVICE ) +public class MogoMapService implements IMogoMapService { + + @Override + public IMogoLocationClient getSingletonLocationClient( Context context ) { + return MogoLocationClient.getInstance( context ); + } + + @Override + public IMogoMarker addMarker( MogoMarkerOptions options ) { + try { + return MogoMap.getInstance().getMogoMap().addMarker( options ); + } catch ( Exception e ) { + return null; + } + } + + @Override + public List< IMogoMarker > addMarkers( ArrayList< MogoMarkerOptions > options, boolean moveToCenter ) { + try { + return MogoMap.getInstance().getMogoMap().addMarkers( options, moveToCenter ); + } catch ( Exception e ) { + return null; + } + } + + @Override + public IMogoInputtipsSearch getInputtipsSearch( Context context, MogoInputtipsQuery query ) { + return new MogoInputtipsSearch( context, query ); + } + + @Override + public IMogoGeoSearch getGeoSearch( Context context ) { + return new MogoGeoSearch( context ); + } + + @Override + public void init( Context context ) { + + } +} diff --git a/services/mogo-service/src/main/res/values/strings.xml b/services/mogo-service/src/main/res/values/strings.xml new file mode 100644 index 0000000000..eb1356c817 --- /dev/null +++ b/services/mogo-service/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + amap-api + diff --git a/services/mogo-service/src/test/java/com/mogo/service/map/ExampleUnitTest.java b/services/mogo-service/src/test/java/com/mogo/service/map/ExampleUnitTest.java new file mode 100644 index 0000000000..6c7fa71433 --- /dev/null +++ b/services/mogo-service/src/test/java/com/mogo/service/map/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package com.mogo.service.map; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals( 4, 2 + 2 ); + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..e1990b3af7 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,7 @@ +rootProject.name = 'MogoLauncher' +include ':app' +include ':foudations:mogo-utils' +include ':services:mogo-service-api' +include ':services:mogo-service' +include ':libraries:mogo-map' +include ':foudations:mogo-commons'