diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
new file mode 100644
index 0000000..b6752b7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.model
+
+/**
+ * 普通实体类,失败/成功数据结构一致
+ */
+class CommonResultModel {
+ var code = 0
+ var data: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
new file mode 100644
index 0000000..b6752b7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.model
+
+/**
+ * 普通实体类,失败/成功数据结构一致
+ */
+class CommonResultModel {
+ var code = 0
+ var data: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
new file mode 100644
index 0000000..0b56128
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.model
+
+class ErrorMessageModel {
+ var code = 0
+ var data: String? = null
+ var exceptionClazz: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
new file mode 100644
index 0000000..b6752b7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.model
+
+/**
+ * 普通实体类,失败/成功数据结构一致
+ */
+class CommonResultModel {
+ var code = 0
+ var data: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
new file mode 100644
index 0000000..0b56128
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.model
+
+class ErrorMessageModel {
+ var code = 0
+ var data: String? = null
+ var exceptionClazz: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
new file mode 100644
index 0000000..5f617b5
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
@@ -0,0 +1,36 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.Manifest
+
+
+object Constant {
+ val USER_PERMISSIONS = arrayOf(
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+
+ const val INTENT_PARAM = "intentParam"
+ const val DEFAULT_SERVER_CONFIG = "defaultServerConfig"
+ const val ACCOUNT = "account"
+ const val PASSWORD = "password"
+ const val USER_DETAIL_MODEL = "userDetailModel"
+ const val APP_AUTHORITY = "com.casic.app.smartwell.fileprovider"
+
+ const val PERMISSIONS_CODE = 999
+ const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L
+ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米
+ const val DISTANCE = 5 //两点间距离阈值,单位:米
+ const val PAGE_LIMIT = 20
+
+ // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf)
+ val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防")
+ val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成")
+ val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理")
+
+ // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite)
+ val POPUP_TITLES = arrayOf("标准地图", "卫星地图")
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
new file mode 100644
index 0000000..b6752b7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.model
+
+/**
+ * 普通实体类,失败/成功数据结构一致
+ */
+class CommonResultModel {
+ var code = 0
+ var data: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
new file mode 100644
index 0000000..0b56128
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.model
+
+class ErrorMessageModel {
+ var code = 0
+ var data: String? = null
+ var exceptionClazz: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
new file mode 100644
index 0000000..5f617b5
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
@@ -0,0 +1,36 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.Manifest
+
+
+object Constant {
+ val USER_PERMISSIONS = arrayOf(
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+
+ const val INTENT_PARAM = "intentParam"
+ const val DEFAULT_SERVER_CONFIG = "defaultServerConfig"
+ const val ACCOUNT = "account"
+ const val PASSWORD = "password"
+ const val USER_DETAIL_MODEL = "userDetailModel"
+ const val APP_AUTHORITY = "com.casic.app.smartwell.fileprovider"
+
+ const val PERMISSIONS_CODE = 999
+ const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L
+ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米
+ const val DISTANCE = 5 //两点间距离阈值,单位:米
+ const val PAGE_LIMIT = 20
+
+ // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf)
+ val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防")
+ val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成")
+ val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理")
+
+ // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite)
+ val POPUP_TITLES = arrayOf("标准地图", "卫星地图")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
new file mode 100644
index 0000000..c62b362
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
@@ -0,0 +1,29 @@
+package com.casic.app.smartwell.sanxi.utils;
+
+import android.app.Activity
+import android.view.WindowManager
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog
+
+object DialogHelper {
+ private lateinit var loadingDialog: QMUITipDialog
+
+ fun showLoadingDialog(activity: Activity, message: String?) {
+ loadingDialog = QMUITipDialog.Builder(activity)
+ .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING)
+ .setTipWord(message)
+ .create()
+ if (!activity.isDestroyed) {
+ try {
+ loadingDialog.show()
+ } catch (e: WindowManager.BadTokenException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ fun dismissLoadingDialog() {
+ if (loadingDialog.isShowing) {
+ loadingDialog.dismiss()
+ }
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
new file mode 100644
index 0000000..b6752b7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.model
+
+/**
+ * 普通实体类,失败/成功数据结构一致
+ */
+class CommonResultModel {
+ var code = 0
+ var data: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
new file mode 100644
index 0000000..0b56128
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.model
+
+class ErrorMessageModel {
+ var code = 0
+ var data: String? = null
+ var exceptionClazz: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
new file mode 100644
index 0000000..5f617b5
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
@@ -0,0 +1,36 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.Manifest
+
+
+object Constant {
+ val USER_PERMISSIONS = arrayOf(
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+
+ const val INTENT_PARAM = "intentParam"
+ const val DEFAULT_SERVER_CONFIG = "defaultServerConfig"
+ const val ACCOUNT = "account"
+ const val PASSWORD = "password"
+ const val USER_DETAIL_MODEL = "userDetailModel"
+ const val APP_AUTHORITY = "com.casic.app.smartwell.fileprovider"
+
+ const val PERMISSIONS_CODE = 999
+ const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L
+ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米
+ const val DISTANCE = 5 //两点间距离阈值,单位:米
+ const val PAGE_LIMIT = 20
+
+ // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf)
+ val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防")
+ val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成")
+ val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理")
+
+ // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite)
+ val POPUP_TITLES = arrayOf("标准地图", "卫星地图")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
new file mode 100644
index 0000000..c62b362
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
@@ -0,0 +1,29 @@
+package com.casic.app.smartwell.sanxi.utils;
+
+import android.app.Activity
+import android.view.WindowManager
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog
+
+object DialogHelper {
+ private lateinit var loadingDialog: QMUITipDialog
+
+ fun showLoadingDialog(activity: Activity, message: String?) {
+ loadingDialog = QMUITipDialog.Builder(activity)
+ .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING)
+ .setTipWord(message)
+ .create()
+ if (!activity.isDestroyed) {
+ try {
+ loadingDialog.show()
+ } catch (e: WindowManager.BadTokenException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ fun dismissLoadingDialog() {
+ if (loadingDialog.isShowing) {
+ loadingDialog.dismiss()
+ }
+ }
+}
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt
new file mode 100644
index 0000000..3e9ae35
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.os.Environment
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import java.io.File
+
+object FileUtils {
+ private const val Tag = "FileUtils"
+ private val context = BaseApplication.obtainInstance()
+
+ //储存下载文件的目录
+ val downloadFilePath: String
+ get() {
+ val downloadDir =
+ File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "")
+ if (!downloadDir.exists()) {
+ downloadDir.mkdir()
+ }
+ return downloadDir.toString()
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
new file mode 100644
index 0000000..b6752b7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.model
+
+/**
+ * 普通实体类,失败/成功数据结构一致
+ */
+class CommonResultModel {
+ var code = 0
+ var data: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
new file mode 100644
index 0000000..0b56128
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.model
+
+class ErrorMessageModel {
+ var code = 0
+ var data: String? = null
+ var exceptionClazz: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
new file mode 100644
index 0000000..5f617b5
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
@@ -0,0 +1,36 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.Manifest
+
+
+object Constant {
+ val USER_PERMISSIONS = arrayOf(
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+
+ const val INTENT_PARAM = "intentParam"
+ const val DEFAULT_SERVER_CONFIG = "defaultServerConfig"
+ const val ACCOUNT = "account"
+ const val PASSWORD = "password"
+ const val USER_DETAIL_MODEL = "userDetailModel"
+ const val APP_AUTHORITY = "com.casic.app.smartwell.fileprovider"
+
+ const val PERMISSIONS_CODE = 999
+ const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L
+ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米
+ const val DISTANCE = 5 //两点间距离阈值,单位:米
+ const val PAGE_LIMIT = 20
+
+ // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf)
+ val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防")
+ val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成")
+ val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理")
+
+ // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite)
+ val POPUP_TITLES = arrayOf("标准地图", "卫星地图")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
new file mode 100644
index 0000000..c62b362
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
@@ -0,0 +1,29 @@
+package com.casic.app.smartwell.sanxi.utils;
+
+import android.app.Activity
+import android.view.WindowManager
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog
+
+object DialogHelper {
+ private lateinit var loadingDialog: QMUITipDialog
+
+ fun showLoadingDialog(activity: Activity, message: String?) {
+ loadingDialog = QMUITipDialog.Builder(activity)
+ .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING)
+ .setTipWord(message)
+ .create()
+ if (!activity.isDestroyed) {
+ try {
+ loadingDialog.show()
+ } catch (e: WindowManager.BadTokenException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ fun dismissLoadingDialog() {
+ if (loadingDialog.isShowing) {
+ loadingDialog.dismiss()
+ }
+ }
+}
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt
new file mode 100644
index 0000000..3e9ae35
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.os.Environment
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import java.io.File
+
+object FileUtils {
+ private const val Tag = "FileUtils"
+ private val context = BaseApplication.obtainInstance()
+
+ //储存下载文件的目录
+ val downloadFilePath: String
+ get() {
+ val downloadDir =
+ File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "")
+ if (!downloadDir.exists()) {
+ downloadDir.mkdir()
+ }
+ return downloadDir.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/GlideLoadEngine.kt
new file mode 100644
index 0000000..922cdb5
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/GlideLoadEngine.kt
@@ -0,0 +1,77 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.widget.ImageView
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.BitmapImageViewTarget
+import com.casic.app.smartwell.sanxi.R
+import com.luck.picture.lib.engine.ImageEngine
+import com.luck.picture.lib.listener.OnImageCompleteCallback
+import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView
+
+class GlideLoadEngine private constructor() : ImageEngine {
+ companion object {
+ private var instance: GlideLoadEngine? = null
+ fun createGlideEngine(): GlideLoadEngine? {
+ if (null == instance) {
+ synchronized(GlideLoadEngine::class.java) {
+ if (null == instance) {
+ instance = GlideLoadEngine()
+ }
+ }
+ }
+ return instance
+ }
+ }
+
+ override fun loadImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context).load(url).into(imageView)
+ }
+
+ override fun loadImage(
+ context: Context,
+ url: String,
+ imageView: ImageView,
+ longImageView: SubsamplingScaleImageView,
+ callback: OnImageCompleteCallback
+ ) {
+
+ }
+
+ override fun loadImage(
+ context: Context,
+ url: String,
+ imageView: ImageView,
+ longImageView: SubsamplingScaleImageView
+ ) {
+ }
+
+ override fun loadFolderImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context)
+ .asBitmap()
+ .load(url)
+ .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder))
+ .into(object : BitmapImageViewTarget(imageView) {
+ override fun setResource(resource: Bitmap?) {
+ val circularBitmapDrawable =
+ RoundedBitmapDrawableFactory.create(context.resources, resource)
+ circularBitmapDrawable.cornerRadius = 8f
+ imageView.setImageDrawable(circularBitmapDrawable)
+ }
+ })
+ }
+
+ override fun loadAsGifImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context).asGif().load(url).into(imageView)
+ }
+
+ override fun loadGridImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context)
+ .load(url)
+ .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder))
+ .into(imageView)
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson(
+ this, object : TypeToken() {}.type
+ )
+ return errorModel.message.toString()
+}
+
+fun String.downloadFile(listener: IDownloadListener) {
+ val httpClient = OkHttpClient()
+ val request = Request.Builder().get().url(this).build()
+ val newCall = httpClient.newCall(request)
+ /**
+ * 如果已被加入下载队列,则取消之前的,重新下载
+ * 断点下载以后再考虑
+ * */
+ if (newCall.isExecuted()) {
+ newCall.cancel()
+ }
+ val downloadPath = this
+ newCall.enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ call.cancel()
+ e.printStackTrace()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ var stream: InputStream? = null
+ val buf = ByteArray(2048)
+ var len: Int
+ var fos: FileOutputStream? = null
+ try {
+ val fileBody = response.body
+ if (fileBody != null) {
+ stream = fileBody.byteStream()
+ val total: Long = fileBody.contentLength()
+ listener.onDownloadStart(total)
+ val file = File(
+ FileUtils.downloadFilePath,
+ downloadPath.substring(downloadPath.lastIndexOf("/") + 1)
+ )
+ fos = FileOutputStream(file)
+ var current: Long = 0
+ while (stream.read(buf).also { len = it } != -1) {
+ fos.write(buf, 0, len)
+ current += len.toLong()
+ listener.onProgressChanged(current)
+ }
+ fos.flush()
+ listener.onDownloadEnd(file)
+ }
+ } catch (e: Exception) {
+ call.cancel()
+ e.printStackTrace()
+ } finally {
+ try {
+ stream?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ try {
+ fos?.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ })
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
new file mode 100644
index 0000000..c49bff7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/View.kt
@@ -0,0 +1,23 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Bitmap
+import android.view.View
+
+/**
+ * 把一个view转化成bitmap对象
+ */
+fun View.toBitmap(): Bitmap? {
+ var bitmap: Bitmap? = null
+ try {
+ this.measure(
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ )
+ this.layout(0, 0, this.measuredWidth, this.measuredHeight)
+ this.buildDrawingCache()
+ bitmap = this.drawingCache
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return bitmap
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
new file mode 100644
index 0000000..833b83e
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ViewModel.kt
@@ -0,0 +1,33 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * ViewModel扩展方法:启动协程
+ * @param block 协程逻辑
+ * @param onError 错误回调方法
+ * @param onComplete 完成回调方法
+ */
+fun ViewModel.launch(
+ block: suspend CoroutineScope.() -> Unit,
+ onError: (e: Throwable) -> Unit = {},
+ onComplete: () -> Unit = {}
+) {
+ viewModelScope.launch(
+ CoroutineExceptionHandler { _, throwable ->
+ run {
+ onError(throwable)
+ }
+ }
+ ) {
+ try {
+ block.invoke(this)
+ } finally {
+ onComplete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
new file mode 100644
index 0000000..b6752b7
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/CommonResultModel.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.model
+
+/**
+ * 普通实体类,失败/成功数据结构一致
+ */
+class CommonResultModel {
+ var code = 0
+ var data: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
new file mode 100644
index 0000000..0b56128
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/model/ErrorMessageModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.model
+
+class ErrorMessageModel {
+ var code = 0
+ var data: String? = null
+ var exceptionClazz: String? = null
+ var message: String? = null
+ var isSuccess = false
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
new file mode 100644
index 0000000..5f617b5
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/Constant.kt
@@ -0,0 +1,36 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.Manifest
+
+
+object Constant {
+ val USER_PERMISSIONS = arrayOf(
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.CAMERA,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ )
+
+ const val INTENT_PARAM = "intentParam"
+ const val DEFAULT_SERVER_CONFIG = "defaultServerConfig"
+ const val ACCOUNT = "account"
+ const val PASSWORD = "password"
+ const val USER_DETAIL_MODEL = "userDetailModel"
+ const val APP_AUTHORITY = "com.casic.app.smartwell.fileprovider"
+
+ const val PERMISSIONS_CODE = 999
+ const val FIVE_YEARS = 5L * 365 * 60 * 60 * 24 * 1000L
+ const val RADIUS_SIZE = 100 //相距多少米才聚合,单位:米
+ const val DISTANCE = 5 //两点间距离阈值,单位:米
+ const val PAGE_LIMIT = 20
+
+ // val HOME_ICONS = arrayOf(R.drawable.ic_well, R.drawable.ic_overtime, R.drawable.ic_bfcf)
+ val HOME_ITEMS = arrayOf("闸井管理", "超时工单", "布防撤防")
+ val SUB_PAGE_TITLES = arrayOf("待处理", "待确认", "处理中", "已完成")
+ val OVER_TIME_PAGE_TITLES = arrayOf("超时未接单", "超时未处理")
+
+ // val POPUP_IMAGES = arrayOf(R.drawable.ic_menu_map, R.drawable.ic_satellite)
+ val POPUP_TITLES = arrayOf("标准地图", "卫星地图")
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
new file mode 100644
index 0000000..c62b362
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/DialogHelper.kt
@@ -0,0 +1,29 @@
+package com.casic.app.smartwell.sanxi.utils;
+
+import android.app.Activity
+import android.view.WindowManager
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog
+
+object DialogHelper {
+ private lateinit var loadingDialog: QMUITipDialog
+
+ fun showLoadingDialog(activity: Activity, message: String?) {
+ loadingDialog = QMUITipDialog.Builder(activity)
+ .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING)
+ .setTipWord(message)
+ .create()
+ if (!activity.isDestroyed) {
+ try {
+ loadingDialog.show()
+ } catch (e: WindowManager.BadTokenException) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ fun dismissLoadingDialog() {
+ if (loadingDialog.isShowing) {
+ loadingDialog.dismiss()
+ }
+ }
+}
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt
new file mode 100644
index 0000000..3e9ae35
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/FileUtils.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.os.Environment
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import java.io.File
+
+object FileUtils {
+ private const val Tag = "FileUtils"
+ private val context = BaseApplication.obtainInstance()
+
+ //储存下载文件的目录
+ val downloadFilePath: String
+ get() {
+ val downloadDir =
+ File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "")
+ if (!downloadDir.exists()) {
+ downloadDir.mkdir()
+ }
+ return downloadDir.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/GlideLoadEngine.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/GlideLoadEngine.kt
new file mode 100644
index 0000000..922cdb5
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/GlideLoadEngine.kt
@@ -0,0 +1,77 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.widget.ImageView
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.BitmapImageViewTarget
+import com.casic.app.smartwell.sanxi.R
+import com.luck.picture.lib.engine.ImageEngine
+import com.luck.picture.lib.listener.OnImageCompleteCallback
+import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView
+
+class GlideLoadEngine private constructor() : ImageEngine {
+ companion object {
+ private var instance: GlideLoadEngine? = null
+ fun createGlideEngine(): GlideLoadEngine? {
+ if (null == instance) {
+ synchronized(GlideLoadEngine::class.java) {
+ if (null == instance) {
+ instance = GlideLoadEngine()
+ }
+ }
+ }
+ return instance
+ }
+ }
+
+ override fun loadImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context).load(url).into(imageView)
+ }
+
+ override fun loadImage(
+ context: Context,
+ url: String,
+ imageView: ImageView,
+ longImageView: SubsamplingScaleImageView,
+ callback: OnImageCompleteCallback
+ ) {
+
+ }
+
+ override fun loadImage(
+ context: Context,
+ url: String,
+ imageView: ImageView,
+ longImageView: SubsamplingScaleImageView
+ ) {
+ }
+
+ override fun loadFolderImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context)
+ .asBitmap()
+ .load(url)
+ .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder))
+ .into(object : BitmapImageViewTarget(imageView) {
+ override fun setResource(resource: Bitmap?) {
+ val circularBitmapDrawable =
+ RoundedBitmapDrawableFactory.create(context.resources, resource)
+ circularBitmapDrawable.cornerRadius = 8f
+ imageView.setImageDrawable(circularBitmapDrawable)
+ }
+ })
+ }
+
+ override fun loadAsGifImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context).asGif().load(url).into(imageView)
+ }
+
+ override fun loadGridImage(context: Context, url: String, imageView: ImageView) {
+ Glide.with(context)
+ .load(url)
+ .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder))
+ .into(imageView)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/utils/IDownloadListener.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/IDownloadListener.kt
new file mode 100644
index 0000000..62b3873
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/utils/IDownloadListener.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.utils
+
+import java.io.File
+
+interface IDownloadListener {
+ fun onDownloadStart(totalBytes: Long)
+
+ fun onProgressChanged(currentBytes: Long)
+
+ fun onDownloadEnd(file: File?)
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac033af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+*.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
+local.properties
+/.idea/.gitignore
+/.idea/compiler.xml
+/.idea/gradle.xml
+/.idea/jarRepositories.xml
+/.idea/misc.xml
+/.idea/vcs.xml
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..aa3e500
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,111 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ signingConfigs {
+ release {
+ storeFile file('/Users/a203/Desktop/AndroidProjects/SanxiTownSmartWell/app/SmartWell.jks')
+ storePassword '123456789'
+ keyAlias 'key0'
+ keyPassword '123456789'
+ }
+ }
+ compileSdkVersion 31
+ buildToolsVersion "30.0.3"
+
+ defaultConfig {
+ applicationId "com.casic.app.smartwell.sanxi"
+ minSdkVersion 23
+ targetSdkVersion 31
+ versionCode 1
+ versionName "1.0.0"
+
+ // 添加手机架构过滤,去掉老旧手机兼容
+ /**
+ * 只适配armeabi:
+ * 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
+ * 缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差
+
+ * 只适配 armeabi-v7a
+ * 筛掉了一部分老旧设备,在性能和兼容二者中比较平衡
+
+ * 只适配 arm64-v8
+ * 优点:性能最佳(微信大哥采用的)
+ * 缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户
+ * */
+ ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8" }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ signingConfig signingConfigs.release
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+ }
+
+ applicationVariants.all { variant ->
+ variant.outputs.all {
+ outputFileName = defaultConfig.versionName + ".apk"
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.appcompat:appcompat:1.4.0'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ //基础依赖库
+ implementation 'com.github.AndroidCoderPeng:Android-library:1.6.0'
+ //Google官方授权框架
+ implementation 'pub.devrel:easypermissions:3.0.0'
+ //腾讯Android UI框架
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+ implementation 'com.qmuiteam:arch:0.3.1'
+ //沉浸式状态栏。基础依赖包,必须要依赖
+ implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
+ //fragment快速实现
+ implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0'
+ //MVVM+LiveData
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
+ //Kotlin协程
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
+ //图片加载框架
+ implementation 'com.github.bumptech.glide:glide:4.9.0'
+ //图片选择框架
+ implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0'
+ //返回值转换器
+ implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
+ implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'
+ implementation 'com.squareup.retrofit2:converter-gson:2.8.1'
+ //okhttp3日志拦截器
+ implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0'
+ //网络请求和接口封装
+ implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.9.0'
+ //官方Json解析库
+ implementation 'com.google.code.gson:gson:2.8.6'
+ //上拉加载下拉刷新
+ implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.1.0'
+ //高德导航
+ implementation 'com.amap.api:navi-3dmap:8.1.0_3dmap8.1.0'
+ //日期选择器
+ implementation 'com.jzxiang.pickerview:TimePickerDialog:1.0.1'
+ //标签流式布局
+ implementation group: 'com.hyman', name: 'flowlayout-lib', version: '1.1.2'
+}
\ No newline at end of file
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /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
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..82a0cf2
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
new file mode 100644
index 0000000..70e879b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseActivity.kt
@@ -0,0 +1,87 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.os.Bundle
+import android.provider.Settings
+import androidx.appcompat.app.AppCompatActivity
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.extensions.convertColor
+import com.casic.app.smartwell.sanxi.extensions.isNetworkConnected
+import com.casic.app.smartwell.sanxi.utils.PageNavigationManager
+import com.casic.app.smartwell.sanxi.utils.StatusBarColorUtil
+import com.casic.app.smartwell.sanxi.widgets.NoNetworkDialog
+import com.gyf.immersionbar.ImmersionBar
+import com.pengxh.app.multilib.utils.BroadcastManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+
+
+/**
+ * 普通页面的基础类
+ * */
+abstract class BaseActivity : AppCompatActivity(), CoroutineScope by MainScope() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(initLayoutView())
+ ImmersionBar.with(this).statusBarDarkFont(false).init() //沉浸式状态栏
+ when (this.javaClass.simpleName) {
+ "WelcomeActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ "SplashScreenActivity" -> ImmersionBar.with(this).statusBarDarkFont(false).init()
+ "LoginActivity" -> ImmersionBar.with(this).statusBarDarkFont(true).init()
+ else -> StatusBarColorUtil.setColor(this, R.color.mainThemeColor.convertColor(this))
+ }
+ setupTopBarLayout()
+ initData()
+ initEvent()
+ PageNavigationManager.addActivity(this)
+ BroadcastManager.getInstance(this)
+ .addAction(ConnectivityManager.CONNECTIVITY_ACTION, object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ if (!context!!.isNetworkConnected()) {
+ NoNetworkDialog.Builder()
+ .setContext(this@BaseActivity)
+ .setOnDialogButtonClickListener(object :
+ NoNetworkDialog.OnDialogButtonClickListener {
+ override fun onButtonClick() {
+ startActivity(Intent(Settings.ACTION_DATA_ROAMING_SETTINGS))
+ }
+ }).build().show()
+ }
+ }
+ })
+ }
+
+ /**
+ * 初始化xml布局
+ */
+ abstract fun initLayoutView(): Int
+
+ /**
+ * 特定页面定制沉浸式状态栏
+ */
+ protected abstract fun setupTopBarLayout()
+
+ /**
+ * 初始化默认数据
+ */
+ abstract fun initData()
+
+ /**
+ * 初始化业务逻辑
+ */
+ abstract fun initEvent()
+
+ /**
+ * 取消协程
+ * */
+ override fun onDestroy() {
+ BroadcastManager.getInstance(this).destroy(ConnectivityManager.CONNECTIVITY_ACTION)
+ cancel()
+ super.onDestroy()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
new file mode 100644
index 0000000..2c5ea4a
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseApplication.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.base
+
+import android.app.Application
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import kotlin.properties.Delegates
+
+class BaseApplication : Application() {
+
+ companion object {
+ private var instance: BaseApplication by Delegates.notNull()
+
+ fun obtainInstance() = instance
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ instance = this
+ SaveKeyValues.initSharedPreferences()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
new file mode 100644
index 0000000..e0b5582
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/base/BaseViewModel.kt
@@ -0,0 +1,9 @@
+package com.casic.app.smartwell.sanxi.base
+
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.casic.app.smartwell.sanxi.utils.LoadState
+
+abstract class BaseViewModel : ViewModel() {
+ val loadState = MutableLiveData()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
new file mode 100644
index 0000000..92ee9b2
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Any.kt
@@ -0,0 +1,5 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import com.google.gson.Gson
+
+fun Any.toJson(): String = Gson().toJson(this)
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
new file mode 100644
index 0000000..e77dbaa
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ArrayList.kt
@@ -0,0 +1,28 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+/**
+ * ArrayList扩展方法
+ */
+
+//将图片集合格式化成满足上传格式的数据
+fun ArrayList.reformat(): String {
+ if (this.isEmpty()) return ""
+ val builder = StringBuilder()
+ //循环遍历元素,同时得到元素index(下标)
+ this.forEachIndexed { index, s ->
+ if (index == this.size - 1) {
+ builder.append(s)
+ } else {
+ builder.append(s).append(",")
+ }
+ }
+ return builder.toString()
+}
+
+fun addAll(vararg args: String): ArrayList {
+ val result = ArrayList()
+ args.forEach {
+ result.add(it)
+ }
+ return result
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
new file mode 100644
index 0000000..e73f47f
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Context.kt
@@ -0,0 +1,54 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import androidx.annotation.RequiresPermission
+import com.casic.app.smartwell.sanxi.utils.Constant
+
+/**
+ * 判断是否有网络连接
+ * @return
+ */
+fun Context.isNetworkConnected(): Boolean { //true是连接,false是没连接
+ val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?
+ if (manager == null) {
+ return false
+ } else {
+ val netWorkInfo = manager.activeNetworkInfo
+ if (netWorkInfo != null) {
+ return netWorkInfo.isAvailable
+ }
+ }
+ return false
+}
+
+/**
+ * Context内联函数-扩展函数
+ * */
+inline fun Context.getSystemService(): T? {
+ return this.getSystemService(T::class.java)
+}
+
+inline fun Context.navigatePageTo() {
+ startActivity(Intent(this, T::class.java))
+}
+
+inline fun Context.navigatePageTo(value: String) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra(Constant.INTENT_PARAM, value)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(values: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putStringArrayListExtra(Constant.INTENT_PARAM, values)
+ startActivity(intent)
+}
+
+inline fun Context.navigatePageTo(index: Int, imageList: ArrayList) {
+ val intent = Intent(this, T::class.java)
+ intent.putExtra("index", index)
+ intent.putStringArrayListExtra("images", imageList)
+ startActivity(intent)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
new file mode 100644
index 0000000..e5eb835
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Dialog.kt
@@ -0,0 +1,21 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Dialog
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.view.WindowManager
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.pengxh.app.multilib.utils.SizeUtil
+
+fun Dialog.initDialogLayoutParams(gravity: Int, animResId: Int, scale: Float) {
+ val window = window ?: return
+ window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ window.decorView.setBackgroundColor(Color.TRANSPARENT)
+ window.setGravity(gravity)
+ //设置Dialog出现的动画
+ window.setWindowAnimations(animResId)
+ val params = window.attributes
+ params.width = (SizeUtil.getScreenWidth(BaseApplication.obtainInstance()) * scale).toInt()
+ params.height = WindowManager.LayoutParams.WRAP_CONTENT
+ window.attributes = params
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
new file mode 100644
index 0000000..0cf73cc
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/ImageView.kt
@@ -0,0 +1,11 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.animation.LinearInterpolator
+import android.widget.ImageView
+
+fun ImageView.arrowAnimation(angle: Float) {
+ val animation = this.animate()
+ animation.duration = 300
+ animation.interpolator = LinearInterpolator()
+ animation.rotation(angle)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
new file mode 100644
index 0000000..01e173b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Int.kt
@@ -0,0 +1,41 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import androidx.core.content.ContextCompat
+
+//颜色扩展
+fun Int.convertColor(context: Context): Int = ContextCompat.getColor(context, this)
+
+//控件ID内连函数扩展
+inline fun Int.findViewById(activity: Activity): T {
+ return activity.findViewById(this)
+}
+
+//窨井类型转换
+fun Int.valueToType(): String {
+ return when (this) {
+ 1 -> "雨水井"
+ 2 -> "污水井"
+ 3 -> "燃气井"
+ 4 -> "热力井"
+ 5 -> "电力井"
+ 6 -> "交通井"
+ 7 -> "路灯井"
+ 8 -> "通信井"
+ 9 -> "监控井"
+ 10 -> "其他"
+ else -> {
+ "未知类型"
+ }
+ }
+}
+
+fun Int.toLevel(): String {
+ return if (this == 0) {
+ ""
+ } else {
+ this.toString()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
new file mode 100644
index 0000000..3be08a8
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Long.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import java.text.ParseException
+import java.text.SimpleDateFormat
+import java.util.*
+
+fun Long.timestampToDate(): String = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date(this))
+
+fun Long.isEarlierThanStart(date: String): Boolean {
+ if (date.isBlank()) {
+ return false
+ }
+ val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
+ try {
+ return this < dateFormat.parse(date)!!.time
+ } catch (e: ParseException) {
+
+ }
+ return true
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
new file mode 100644
index 0000000..e61367c
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/Poi.kt
@@ -0,0 +1,20 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.content.Context
+import com.amap.api.maps.model.Poi
+import com.amap.api.navi.AmapNaviPage
+import com.amap.api.navi.AmapNaviParams
+import com.amap.api.navi.AmapNaviType
+import com.amap.api.navi.AmapPageType
+
+/**
+ * 步行导航扩展函数
+ * */
+fun Poi.showRouteOnMap(context: Context) {
+ val params = AmapNaviParams(
+ null, null, this,
+ AmapNaviType.WALK,
+ AmapPageType.ROUTE
+ )
+ AmapNaviPage.getInstance().showRouteActivity(context, params, null)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
new file mode 100644
index 0000000..0e9e95b
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/QMUIEmptyView.kt
@@ -0,0 +1,12 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.view.View
+import com.qmuiteam.qmui.widget.QMUIEmptyView
+
+fun QMUIEmptyView.showEmptyPage(onButtonClickListener: View.OnClickListener) {
+ this.show(false, "抱歉,无法查询到相关记录", null, "重试", onButtonClickListener)
+}
+
+fun QMUIEmptyView.showEmptyPage(title: String, onButtonClickListener: View.OnClickListener) {
+ this.show(false, title, null, "刷新", onButtonClickListener)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
new file mode 100644
index 0000000..714b5fb
--- /dev/null
+++ b/app/src/main/java/com/casic/app/smartwell/sanxi/extensions/String.kt
@@ -0,0 +1,171 @@
+package com.casic.app.smartwell.sanxi.extensions
+
+import android.graphics.Color
+import android.view.Gravity
+import android.widget.TextView
+import android.widget.Toast
+import com.casic.app.smartwell.model.ErrorMessageModel
+import com.casic.app.smartwell.sanxi.R
+import com.casic.app.smartwell.sanxi.base.BaseApplication
+import com.casic.app.smartwell.sanxi.utils.Constant
+import com.casic.app.smartwell.sanxi.utils.FileUtils
+import com.casic.app.smartwell.sanxi.utils.IDownloadListener
+import com.casic.app.smartwell.sanxi.utils.SaveKeyValues
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import com.qmuiteam.qmui.util.QMUIDisplayHelper
+import okhttp3.*
+import org.json.JSONObject
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * String扩展方法
+ */
+
+//将Toast扩展到String
+fun String.show() {
+ val context = BaseApplication.obtainInstance()
+ val toast = Toast(context)
+ val textView = TextView(context)
+ textView.setBackgroundResource(R.drawable.bg_toast_layout)
+ textView.setTextColor(Color.WHITE)
+ textView.textSize = 16.0f
+ textView.text = this
+ textView.setPadding(
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10),
+ QMUIDisplayHelper.dp2px(context, 20),
+ QMUIDisplayHelper.dp2px(context, 10)
+ )
+ toast.setGravity(Gravity.BOTTOM, 0, QMUIDisplayHelper.dp2px(context, 80))
+ toast.view = textView
+ toast.duration = Toast.LENGTH_SHORT
+ toast.show()
+}
+
+fun String.isLetterAndDigit(): Boolean {
+ var isDigit = false
+ var isLetter = false
+ for (i in this.indices) {
+ if (Character.isDigit(this[i])) {
+ isDigit = true
+ } else if (Character.isLetter(this[i])) {
+ isLetter = true
+ }
+ }
+ return isDigit && isLetter
+}
+
+//拼接图片地址
+fun String.combineImagePath(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this.replace("\\", "/")}"
+}
+
+/**
+ * 下载路径为 http://xx.com/static/ 拼接downloadUrl
+ * */
+fun String.appendDownloadUrl(): String {
+ if (this.isEmpty()) return this
+ val defaultValue = SaveKeyValues.getValue(
+ Constant.DEFAULT_SERVER_CONFIG,
+ "http://111.198.10.15:11304"
+ ) as String
+ return "$defaultValue/static/${this}"
+}
+
+//窨井类型转换
+fun String.toChinese(): String {
+ return when (this) {
+ "0" -> "全部"
+ "1" -> "一级"
+ "2" -> "二级"
+ "3" -> "三级"
+ else -> {
+ "未知"
+ }
+ }
+}
+
+fun String.separateResponseCode(): Int {
+ if (this.isBlank()) {
+ return 404
+ }
+ return JSONObject(this).getInt("code")
+}
+
+fun String.toErrorMessage(): String {
+ val errorModel = Gson().fromJson