diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..41348ab --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean +import retrofit2.http.GET + +/** + * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符? + * */ +@JvmSuppressWildcards +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/pan-tilt/config/baseConfig") + suspend fun obtainPublicKeyAsync(): PublicKeyBean +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..41348ab --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean +import retrofit2.http.GET + +/** + * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符? + * */ +@JvmSuppressWildcards +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/pan-tilt/config/baseConfig") + suspend fun obtainPublicKeyAsync(): PublicKeyBean +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..fe9869b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): PublicKeyBean { + return api.obtainPublicKeyAsync() + } +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..41348ab --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean +import retrofit2.http.GET + +/** + * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符? + * */ +@JvmSuppressWildcards +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/pan-tilt/config/baseConfig") + suspend fun obtainPublicKeyAsync(): PublicKeyBean +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..fe9869b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): PublicKeyBean { + return api.obtainPublicKeyAsync() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt new file mode 100644 index 0000000..a5578c3 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt @@ -0,0 +1,56 @@ +package com.casic.birmm.inspect.view + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.CountDownTimer +import android.view.ViewGroup.MarginLayoutParams +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity +import com.qmuiteam.qmui.util.QMUIDisplayHelper +import kotlinx.android.synthetic.main.activity_guide.* + +@SuppressLint("SetTextI18n") +class GuideActivity : BaseActivity() { + + private lateinit var countDownTimer: CountDownTimer + + override fun initLayoutView(): Int = R.layout.activity_guide + + override fun setupTopBarLayout() { + + } + + override fun initData() { + //根据不同设备状态栏高度设置"跳过"按钮的Margin值 + val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) + val rightMargin = QMUIDisplayHelper.dp2px(this, 10) + if (skipButton.layoutParams is MarginLayoutParams) { + val params = skipButton.layoutParams as MarginLayoutParams + params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) + skipButton.requestLayout() + } + + countDownTimer = object : CountDownTimer(4000, 1000) { + override fun onFinish() { + startLoginActivity() + } + + override fun onTick(millisUntilFinished: Long) { + skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" + } + } + } + + override fun initEvent() { + countDownTimer.start() + skipButton.setOnClickListener { + countDownTimer.cancel() + startLoginActivity() + } + } + + fun startLoginActivity() { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + } +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..41348ab --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean +import retrofit2.http.GET + +/** + * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符? + * */ +@JvmSuppressWildcards +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/pan-tilt/config/baseConfig") + suspend fun obtainPublicKeyAsync(): PublicKeyBean +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..fe9869b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): PublicKeyBean { + return api.obtainPublicKeyAsync() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt new file mode 100644 index 0000000..a5578c3 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt @@ -0,0 +1,56 @@ +package com.casic.birmm.inspect.view + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.CountDownTimer +import android.view.ViewGroup.MarginLayoutParams +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity +import com.qmuiteam.qmui.util.QMUIDisplayHelper +import kotlinx.android.synthetic.main.activity_guide.* + +@SuppressLint("SetTextI18n") +class GuideActivity : BaseActivity() { + + private lateinit var countDownTimer: CountDownTimer + + override fun initLayoutView(): Int = R.layout.activity_guide + + override fun setupTopBarLayout() { + + } + + override fun initData() { + //根据不同设备状态栏高度设置"跳过"按钮的Margin值 + val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) + val rightMargin = QMUIDisplayHelper.dp2px(this, 10) + if (skipButton.layoutParams is MarginLayoutParams) { + val params = skipButton.layoutParams as MarginLayoutParams + params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) + skipButton.requestLayout() + } + + countDownTimer = object : CountDownTimer(4000, 1000) { + override fun onFinish() { + startLoginActivity() + } + + override fun onTick(millisUntilFinished: Long) { + skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" + } + } + } + + override fun initEvent() { + countDownTimer.start() + skipButton.setOnClickListener { + countDownTimer.cancel() + startLoginActivity() + } + } + + fun startLoginActivity() { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt new file mode 100644 index 0000000..bdb3f2d --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt @@ -0,0 +1,110 @@ +package com.casic.birmm.inspect.view + +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.KeyEvent +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.TextView +import androidx.lifecycle.Observer +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseViewModelActivity +import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.vm.AuthenticateViewModel +import com.casic.birmm.inspect.widgets.InputDialog +import kotlinx.android.synthetic.main.activity_login.* + + +class LoginActivity : BaseViewModelActivity() { + + companion object { + private const val Tag = "LoginActivity" + } + + override fun createViewModelByClass(): Class? = + AuthenticateViewModel::class.java + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + + } + + override fun initData() { + // 监听数据变化,从而显示数据在页面 + viewModel.keyBean.observe(this, Observer { + if (it.isSuccess) { + Log.d(Tag, "${it.data!!.publicKey}") + val keyString = it.data!!.publicKey + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + } else { + OtherUtils.showTipsDialog(this, it.message!!, loginButton) + } + }) + // 监听加载状态变化 从而改变页面 + viewModel.loadState.observe(this, Observer { + when (it) { + is LoadState.Loading -> { + OtherUtils.showLoadingDialog(this, "登录中,请稍后") + } + else -> OtherUtils.dismissLoadingDialog() + } + }) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable) { + if (s.toString().trim { it <= ' ' }.length > 12) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + }) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + viewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setChangeAlphaWhenPress(true) + loginButton.setOnClickListener { + viewModel.obtainPublicKey() + } + + //修改服务器配置 + changeServerConfigView.setOnClickListener { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + InputDialog.Builder().setContext(this) + .setTitle("请输入后台服务器地址") + .setDefaultValue(defaultValue) + .setNegativeButton("取消") + .setPositiveButton("保存") + .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { + override fun onButtonClick(input: String) { + SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) + } + }) + .build().show() + } + } +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..41348ab --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean +import retrofit2.http.GET + +/** + * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符? + * */ +@JvmSuppressWildcards +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/pan-tilt/config/baseConfig") + suspend fun obtainPublicKeyAsync(): PublicKeyBean +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..fe9869b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): PublicKeyBean { + return api.obtainPublicKeyAsync() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt new file mode 100644 index 0000000..a5578c3 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt @@ -0,0 +1,56 @@ +package com.casic.birmm.inspect.view + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.CountDownTimer +import android.view.ViewGroup.MarginLayoutParams +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity +import com.qmuiteam.qmui.util.QMUIDisplayHelper +import kotlinx.android.synthetic.main.activity_guide.* + +@SuppressLint("SetTextI18n") +class GuideActivity : BaseActivity() { + + private lateinit var countDownTimer: CountDownTimer + + override fun initLayoutView(): Int = R.layout.activity_guide + + override fun setupTopBarLayout() { + + } + + override fun initData() { + //根据不同设备状态栏高度设置"跳过"按钮的Margin值 + val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) + val rightMargin = QMUIDisplayHelper.dp2px(this, 10) + if (skipButton.layoutParams is MarginLayoutParams) { + val params = skipButton.layoutParams as MarginLayoutParams + params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) + skipButton.requestLayout() + } + + countDownTimer = object : CountDownTimer(4000, 1000) { + override fun onFinish() { + startLoginActivity() + } + + override fun onTick(millisUntilFinished: Long) { + skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" + } + } + } + + override fun initEvent() { + countDownTimer.start() + skipButton.setOnClickListener { + countDownTimer.cancel() + startLoginActivity() + } + } + + fun startLoginActivity() { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt new file mode 100644 index 0000000..bdb3f2d --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt @@ -0,0 +1,110 @@ +package com.casic.birmm.inspect.view + +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.KeyEvent +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.TextView +import androidx.lifecycle.Observer +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseViewModelActivity +import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.vm.AuthenticateViewModel +import com.casic.birmm.inspect.widgets.InputDialog +import kotlinx.android.synthetic.main.activity_login.* + + +class LoginActivity : BaseViewModelActivity() { + + companion object { + private const val Tag = "LoginActivity" + } + + override fun createViewModelByClass(): Class? = + AuthenticateViewModel::class.java + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + + } + + override fun initData() { + // 监听数据变化,从而显示数据在页面 + viewModel.keyBean.observe(this, Observer { + if (it.isSuccess) { + Log.d(Tag, "${it.data!!.publicKey}") + val keyString = it.data!!.publicKey + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + } else { + OtherUtils.showTipsDialog(this, it.message!!, loginButton) + } + }) + // 监听加载状态变化 从而改变页面 + viewModel.loadState.observe(this, Observer { + when (it) { + is LoadState.Loading -> { + OtherUtils.showLoadingDialog(this, "登录中,请稍后") + } + else -> OtherUtils.dismissLoadingDialog() + } + }) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable) { + if (s.toString().trim { it <= ' ' }.length > 12) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + }) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + viewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setChangeAlphaWhenPress(true) + loginButton.setOnClickListener { + viewModel.obtainPublicKey() + } + + //修改服务器配置 + changeServerConfigView.setOnClickListener { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + InputDialog.Builder().setContext(this) + .setTitle("请输入后台服务器地址") + .setDefaultValue(defaultValue) + .setNegativeButton("取消") + .setPositiveButton("保存") + .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { + override fun onButtonClick(input: String) { + SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) + } + }) + .build().show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt new file mode 100644 index 0000000..98b9e9a --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt @@ -0,0 +1,21 @@ +package com.casic.birmm.inspect.view + +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity + +class MainActivity : BaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_main + + override fun setupTopBarLayout() { + + } + + override fun initData() { + + } + + override fun initEvent() { + + } +} diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..41348ab --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean +import retrofit2.http.GET + +/** + * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符? + * */ +@JvmSuppressWildcards +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/pan-tilt/config/baseConfig") + suspend fun obtainPublicKeyAsync(): PublicKeyBean +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..fe9869b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): PublicKeyBean { + return api.obtainPublicKeyAsync() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt new file mode 100644 index 0000000..a5578c3 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt @@ -0,0 +1,56 @@ +package com.casic.birmm.inspect.view + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.CountDownTimer +import android.view.ViewGroup.MarginLayoutParams +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity +import com.qmuiteam.qmui.util.QMUIDisplayHelper +import kotlinx.android.synthetic.main.activity_guide.* + +@SuppressLint("SetTextI18n") +class GuideActivity : BaseActivity() { + + private lateinit var countDownTimer: CountDownTimer + + override fun initLayoutView(): Int = R.layout.activity_guide + + override fun setupTopBarLayout() { + + } + + override fun initData() { + //根据不同设备状态栏高度设置"跳过"按钮的Margin值 + val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) + val rightMargin = QMUIDisplayHelper.dp2px(this, 10) + if (skipButton.layoutParams is MarginLayoutParams) { + val params = skipButton.layoutParams as MarginLayoutParams + params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) + skipButton.requestLayout() + } + + countDownTimer = object : CountDownTimer(4000, 1000) { + override fun onFinish() { + startLoginActivity() + } + + override fun onTick(millisUntilFinished: Long) { + skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" + } + } + } + + override fun initEvent() { + countDownTimer.start() + skipButton.setOnClickListener { + countDownTimer.cancel() + startLoginActivity() + } + } + + fun startLoginActivity() { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt new file mode 100644 index 0000000..bdb3f2d --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt @@ -0,0 +1,110 @@ +package com.casic.birmm.inspect.view + +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.KeyEvent +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.TextView +import androidx.lifecycle.Observer +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseViewModelActivity +import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.vm.AuthenticateViewModel +import com.casic.birmm.inspect.widgets.InputDialog +import kotlinx.android.synthetic.main.activity_login.* + + +class LoginActivity : BaseViewModelActivity() { + + companion object { + private const val Tag = "LoginActivity" + } + + override fun createViewModelByClass(): Class? = + AuthenticateViewModel::class.java + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + + } + + override fun initData() { + // 监听数据变化,从而显示数据在页面 + viewModel.keyBean.observe(this, Observer { + if (it.isSuccess) { + Log.d(Tag, "${it.data!!.publicKey}") + val keyString = it.data!!.publicKey + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + } else { + OtherUtils.showTipsDialog(this, it.message!!, loginButton) + } + }) + // 监听加载状态变化 从而改变页面 + viewModel.loadState.observe(this, Observer { + when (it) { + is LoadState.Loading -> { + OtherUtils.showLoadingDialog(this, "登录中,请稍后") + } + else -> OtherUtils.dismissLoadingDialog() + } + }) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable) { + if (s.toString().trim { it <= ' ' }.length > 12) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + }) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + viewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setChangeAlphaWhenPress(true) + loginButton.setOnClickListener { + viewModel.obtainPublicKey() + } + + //修改服务器配置 + changeServerConfigView.setOnClickListener { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + InputDialog.Builder().setContext(this) + .setTitle("请输入后台服务器地址") + .setDefaultValue(defaultValue) + .setNegativeButton("取消") + .setPositiveButton("保存") + .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { + override fun onButtonClick(input: String) { + SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) + } + }) + .build().show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt new file mode 100644 index 0000000..98b9e9a --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt @@ -0,0 +1,21 @@ +package com.casic.birmm.inspect.view + +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity + +class MainActivity : BaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_main + + override fun setupTopBarLayout() { + + } + + override fun initData() { + + } + + override fun initEvent() { + + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/view/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/PermissionActivity.kt new file mode 100644 index 0000000..07f48a5 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/PermissionActivity.kt @@ -0,0 +1,53 @@ +package com.casic.birmm.inspect.view + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.casic.birmm.inspect.utils.Constant +import pub.devrel.easypermissions.EasyPermissions +import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks + +class PermissionActivity : AppCompatActivity(), PermissionCallbacks { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + startGuideActivity() + } else { + EasyPermissions.requestPermissions( + this, + "需要获取相关权限", + Constant.PERMISSIONS_CODE, + *Constant.USER_PERMISSIONS + ) + } + } else { + startGuideActivity() + } + } + + private fun startGuideActivity() { + startActivity(Intent(this, GuideActivity::class.java)) + finish() + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) + } + + override fun onPermissionsGranted(requestCode: Int, perms: List) { + startGuideActivity() + } + + override fun onPermissionsDenied(requestCode: Int, perms: List) { + + } +} \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4ca4a9f..0002b4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,12 @@ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + kotlin { + experimental { + coroutines 'enable' + } + } } dependencies { @@ -47,19 +53,24 @@ implementation 'com.gyf.immersionbar:immersionbar:3.0.0' //fragment快速实现 implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' + //MVVM + implementation "androidx.lifecycle:lifecycle-livedata:2.2.0" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" + //Kotlin协程 + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' + //返回值转换器 + implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' + implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' + implementation 'com.squareup.retrofit2:converter-gson:2.8.1' + //okhttp3日志拦截器 + implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' - //MVP网络请求框架retrofit2+rxjava - implementation 'io.reactivex:rxjava:1.3.8' - implementation 'io.reactivex:rxandroid:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.6.0' - implementation 'com.squareup.retrofit2:converter-gson:2.8.1' - implementation 'com.squareup.retrofit2:adapter-rxjava:2.8.1' - implementation 'com.squareup.okhttp3:logging-interceptor:4.6.0' //官方Json解析库 implementation 'com.google.code.gson:gson:2.8.6' - //Toast提示 - implementation 'com.sdsmdg.tastytoast:tastytoast:0.1.1' //图片选择框架 implementation 'com.github.LuckSiege.PictureSelector:picture_library:v2.6.0' //上拉加载下拉刷新 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7096527..be35467 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,16 +23,16 @@ android:theme="@style/QMUI.Compat.NoActionBar" android:usesCleartextTraffic="true" tools:targetApi="m"> - + - - - + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt index aefdbb6..b6b9f53 100644 --- a/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseActivity.kt @@ -4,6 +4,9 @@ import androidx.appcompat.app.AppCompatActivity import com.qmuiteam.qmui.util.QMUIStatusBarHelper +/** + * 无网络请求等普通页面的基础类 + * */ abstract class BaseActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt new file mode 100644 index 0000000..968b877 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModel.kt @@ -0,0 +1,9 @@ +package com.casic.birmm.inspect.base + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.casic.birmm.inspect.utils.LoadState + +abstract class BaseViewModel : ViewModel() { + val loadState = MutableLiveData() +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt new file mode 100644 index 0000000..ee93c84 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/base/BaseViewModelActivity.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.base + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.qmuiteam.qmui.util.QMUIStatusBarHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel + +/** + * MV ViewModel 架构 + * + * 有网络请求等普通页面的基础类 + */ +abstract class BaseViewModelActivity : AppCompatActivity(), + CoroutineScope by MainScope() { + + protected lateinit var viewModel: VM + + /** + * 初始化xml布局 + */ + protected abstract fun initLayoutView(): Int + + /** + * 特定页面定制沉浸式状态栏 + */ + protected abstract fun setupTopBarLayout() + + /** + * 初始化默认数据 + */ + abstract fun initData() + + /** + * 初始化业务逻辑 + */ + abstract fun initEvent() + + /** + * 提供ViewModel类 + */ + protected abstract fun createViewModelByClass(): Class? + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(initLayoutView()) + createViewModelByClass()?.let { viewModel = ViewModelProvider(this).get(it) } + QMUIStatusBarHelper.translucent(this) //沉浸式状态栏 + setupTopBarLayout() + initData() + initEvent() + } + + override fun onDestroy() { + cancel()// 取消协程 + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt new file mode 100644 index 0000000..25c5436 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/model/PublicKeyBean.kt @@ -0,0 +1,18 @@ +package com.casic.birmm.inspect.model + +/** + * PublicKey 登录校验Key + */ +class PublicKeyBean { + var code = 0 + var data: DataBean? = null + var message: String? = null + var isSuccess = false + + class DataBean { + var isAppKaptcha = false + var isKaptcha = false + var publicKey: String? = null + var sid: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt deleted file mode 100644 index ddc160a..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/GuideActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.CountDownTimer -import android.view.ViewGroup.MarginLayoutParams -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.qmuiteam.qmui.util.QMUIDisplayHelper -import kotlinx.android.synthetic.main.activity_guide.* - -@SuppressLint("SetTextI18n") -class GuideActivity : BaseActivity() { - - private lateinit var countDownTimer: CountDownTimer - - override fun initLayoutView(): Int = R.layout.activity_guide - - override fun setupTopBarLayout() { - - } - - override fun initData() { - //根据不同设备状态栏高度设置"跳过"按钮的Margin值 - val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) - val rightMargin = QMUIDisplayHelper.dp2px(this, 10) - if (skipButton.layoutParams is MarginLayoutParams) { - val params = skipButton.layoutParams as MarginLayoutParams - params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) - skipButton.requestLayout() - } - - countDownTimer = object : CountDownTimer(4000, 1000) { - override fun onFinish() { - startLoginActivity() - } - - override fun onTick(millisUntilFinished: Long) { - skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" - } - } - } - - override fun initEvent() { - countDownTimer.start() - skipButton.setOnClickListener { - countDownTimer.cancel() - startLoginActivity() - } - } - - fun startLoginActivity() { - startActivity(Intent(this, LoginActivity::class.java)) - finish() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt deleted file mode 100644 index 6ef4548..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/LoginActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.text.Editable -import android.text.TextWatcher -import android.view.KeyEvent -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import android.widget.TextView -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity -import com.casic.birmm.inspect.utils.Constant -import com.casic.birmm.inspect.utils.SaveKeyValues -import com.casic.birmm.inspect.widgets.InputDialog -import kotlinx.android.synthetic.main.activity_login.* - - -class LoginActivity : BaseActivity() { - - companion object { - private const val Tag = "LoginActivity" - } - - override fun initLayoutView(): Int = R.layout.activity_login - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - loginButton.setChangeAlphaWhenPress(true) - val editText: EditText? = inputLayout.editText - editText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - - } - - override fun afterTextChanged(s: Editable) { - if (s.toString().trim { it <= ' ' }.length > 12) { - inputLayout.error = "密码长度超出限制" - } else { - inputLayout.error = null - } - } - }) - //点击输入法键盘"完成" - editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> - if (actionId == EditorInfo.IME_ACTION_DONE) { -// authenticatePresenter.onReadyRetrofitRequest() - return@setOnEditorActionListener true - } - false - } - - //修改服务器配置 - changeServerConfigView.setOnClickListener { - val defaultValue = SaveKeyValues.getValue( - Constant.DEFAULT_SERVER_CONFIG, - "http://139.198.6.177:8093/" - ) as String - InputDialog.Builder().setContext(this) - .setTitle("请输入后台服务器地址") - .setDefaultValue(defaultValue) - .setNegativeButton("取消") - .setPositiveButton("保存") - .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { - override fun onButtonClick(input: String) { - SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) - } - }) - .build().show() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt deleted file mode 100644 index 0e97af7..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/MainActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.casic.birmm.inspect.ui - -import com.casic.birmm.inspect.R -import com.casic.birmm.inspect.base.BaseActivity - -class MainActivity : BaseActivity() { - - override fun initLayoutView(): Int = R.layout.activity_main - - override fun setupTopBarLayout() { - - } - - override fun initData() { - - } - - override fun initEvent() { - - } -} diff --git a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt deleted file mode 100644 index 21fbba2..0000000 --- a/app/src/main/java/com/casic/birmm/inspect/ui/PermissionActivity.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.casic.birmm.inspect.ui - -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.util.Log -import androidx.appcompat.app.AppCompatActivity -import com.casic.birmm.inspect.utils.Constant -import pub.devrel.easypermissions.EasyPermissions -import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks - -class PermissionActivity : AppCompatActivity(), PermissionCallbacks { - - companion object { - private const val TAG = "PermissionActivity" - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { - startGuideActivity() - } else { - EasyPermissions.requestPermissions( - this, - "需要获取相关权限", - Constant.PERMISSIONS_CODE, - *Constant.USER_PERMISSIONS - ) - } - } else { - startGuideActivity() - } - } - - private fun startGuideActivity() { - startActivity(Intent(this, GuideActivity::class.java)) - finish() - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) - } - - override fun onPermissionsGranted(requestCode: Int, perms: List) { - startGuideActivity() - } - - override fun onPermissionsDenied(requestCode: Int, perms: List) { - Log.e(TAG, "onPermissionsDenied: $perms") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt new file mode 100644 index 0000000..2259965 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/ExtensionViewModel.kt @@ -0,0 +1,33 @@ +package com.casic.birmm.inspect.utils + +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/birmm/inspect/utils/LoadState.kt b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt new file mode 100644 index 0000000..9c83224 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/LoadState.kt @@ -0,0 +1,22 @@ +package com.casic.birmm.inspect.utils + +/** + * 加载状态 + * sealed 关键字表示此类仅内部继承 + */ +sealed class LoadState(val msg: String) { + /** + * 加载中 + */ + class Loading(msg: String = "") : LoadState(msg) + + /** + * 成功 + */ + class Success(msg: String = "") : LoadState(msg) + + /** + * 失败 + */ + class Fail(msg: String = "") : LoadState(msg) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt index 4c38241..ff63747 100644 --- a/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt +++ b/app/src/main/java/com/casic/birmm/inspect/utils/OtherUtils.kt @@ -6,6 +6,8 @@ object OtherUtils { + private var loadingDialog: QMUITipDialog? = null + //替换Toast fun showTipsDialog(context: Context, message: String, view: View) { val tipDialog = QMUITipDialog.Builder(context) @@ -16,4 +18,20 @@ tipDialog.dismiss() }, 2000) } + + fun showLoadingDialog(context: Context?, message: String?) { + loadingDialog = QMUITipDialog.Builder(context) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(message) + .create() + loadingDialog!!.show() + } + + fun dismissLoadingDialog() { + if (loadingDialog != null) { + if (loadingDialog!!.isShowing) { + loadingDialog!!.dismiss() + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt new file mode 100644 index 0000000..b93983b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/RSAUtils.kt @@ -0,0 +1,61 @@ +package com.casic.birmm.inspect.utils + +import android.util.Base64 +import java.security.* +import java.security.spec.InvalidKeySpecException +import java.security.spec.X509EncodedKeySpec +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException + +/** + * Created by W530 on 2019/9/26. + */ +object RSAUtils { + //构建Cipher实例时所传入的的字符串,默认为"RSA/NONE/PKCS1Padding" + private fun processData(srcData: ByteArray, key: Key, mode: Int): ByteArray? { //用来保存处理结果 + var resultBytes: ByteArray? = null + try { //获取Cipher实例 + val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") + //初始化Cipher,mode指定是加密还是解密,key为公钥或私钥 + cipher.init(mode, key) + //处理数据 + resultBytes = cipher.doFinal(srcData) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: NoSuchPaddingException) { + e.printStackTrace() + } catch (e: InvalidKeyException) { + e.printStackTrace() + } catch (e: BadPaddingException) { + e.printStackTrace() + } catch (e: IllegalBlockSizeException) { + e.printStackTrace() + } + return resultBytes + } + + fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey): String { + val resultBytes = + processData(srcData, publicKey, Cipher.ENCRYPT_MODE) + return Base64.encodeToString(resultBytes, Base64.DEFAULT) + } + + fun keyStrToPublicKey(publicKeyStr: String?): PublicKey? { + var publicKey: PublicKey? = null + val keyBytes = + Base64.decode(publicKeyStr, Base64.DEFAULT) + val keySpec = + X509EncodedKeySpec(keyBytes) + try { + val keyFactory = KeyFactory.getInstance("RSA") + publicKey = keyFactory.generatePublic(keySpec) + } catch (e: NoSuchAlgorithmException) { + e.printStackTrace() + } catch (e: InvalidKeySpecException) { + e.printStackTrace() + } + return publicKey + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt new file mode 100644 index 0000000..0dd341f --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitFactory.kt @@ -0,0 +1,48 @@ +package com.casic.birmm.inspect.utils.retrofit; + +import android.util.Log +import com.casic.birmm.inspect.utils.Constant +import com.casic.birmm.inspect.utils.SaveKeyValues +import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import org.jetbrains.annotations.NotNull +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +object RetrofitFactory { + private const val Tag = "RetrofitFactory" + + private val client: OkHttpClient by lazy { createOKHttpClient() } + + fun createRetrofit(clazz: Class): T { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + return Retrofit.Builder() + .baseUrl(defaultValue) + .addConverterFactory(ScalarsConverterFactory.create()) //字符串转换器 + .addConverterFactory(GsonConverterFactory.create()) //Gson转换器 + .addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) //协程请求适配器 + .client(client) //log拦截器 + .build().create(clazz) + } + + private fun createOKHttpClient(): OkHttpClient { //日志显示级别 + val interceptor = + HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger { + override fun log(@NotNull message: String) { + Log.d(Tag, "log: $message") + } + }) + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY) + val builder = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS)// 连接时间:30s超时 + .readTimeout(10, TimeUnit.SECONDS)// 读取时间:10s超时 + .writeTimeout(10, TimeUnit.SECONDS)// 写入时间:10s超时 + return builder.addInterceptor(interceptor).build() + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt new file mode 100644 index 0000000..41348ab --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitService.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean +import retrofit2.http.GET + +/** + * @JvmSuppressWildcards 用来注解类和方法,使得被标记元素的泛型参数不会被编译成通配符? + * */ +@JvmSuppressWildcards +interface RetrofitService { + /** + * PublicKey校验 + */ + @GET("/pan-tilt/config/baseConfig") + suspend fun obtainPublicKeyAsync(): PublicKeyBean +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt new file mode 100644 index 0000000..fe9869b --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/utils/retrofit/RetrofitServiceManager.kt @@ -0,0 +1,16 @@ +package com.casic.birmm.inspect.utils.retrofit + +import com.casic.birmm.inspect.model.PublicKeyBean + + +object RetrofitServiceManager { + + private val api by lazy { RetrofitFactory.createRetrofit(RetrofitService::class.java) } + + /** + * 验证PublicKey + */ + suspend fun authenticate(): PublicKeyBean { + return api.obtainPublicKeyAsync() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt new file mode 100644 index 0000000..a5578c3 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/GuideActivity.kt @@ -0,0 +1,56 @@ +package com.casic.birmm.inspect.view + +import android.annotation.SuppressLint +import android.content.Intent +import android.os.CountDownTimer +import android.view.ViewGroup.MarginLayoutParams +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity +import com.qmuiteam.qmui.util.QMUIDisplayHelper +import kotlinx.android.synthetic.main.activity_guide.* + +@SuppressLint("SetTextI18n") +class GuideActivity : BaseActivity() { + + private lateinit var countDownTimer: CountDownTimer + + override fun initLayoutView(): Int = R.layout.activity_guide + + override fun setupTopBarLayout() { + + } + + override fun initData() { + //根据不同设备状态栏高度设置"跳过"按钮的Margin值 + val statusBarHeight = QMUIDisplayHelper.getStatusBarHeight(this) + val rightMargin = QMUIDisplayHelper.dp2px(this, 10) + if (skipButton.layoutParams is MarginLayoutParams) { + val params = skipButton.layoutParams as MarginLayoutParams + params.setMargins(skipButton.width, statusBarHeight, rightMargin, 0) + skipButton.requestLayout() + } + + countDownTimer = object : CountDownTimer(4000, 1000) { + override fun onFinish() { + startLoginActivity() + } + + override fun onTick(millisUntilFinished: Long) { + skipButton.text = "跳过\u3000${(millisUntilFinished / 1000)}s" + } + } + } + + override fun initEvent() { + countDownTimer.start() + skipButton.setOnClickListener { + countDownTimer.cancel() + startLoginActivity() + } + } + + fun startLoginActivity() { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt new file mode 100644 index 0000000..bdb3f2d --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/LoginActivity.kt @@ -0,0 +1,110 @@ +package com.casic.birmm.inspect.view + +import android.text.Editable +import android.text.TextWatcher +import android.util.Log +import android.view.KeyEvent +import android.view.inputmethod.EditorInfo +import android.widget.EditText +import android.widget.TextView +import androidx.lifecycle.Observer +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseViewModelActivity +import com.casic.birmm.inspect.utils.* +import com.casic.birmm.inspect.vm.AuthenticateViewModel +import com.casic.birmm.inspect.widgets.InputDialog +import kotlinx.android.synthetic.main.activity_login.* + + +class LoginActivity : BaseViewModelActivity() { + + companion object { + private const val Tag = "LoginActivity" + } + + override fun createViewModelByClass(): Class? = + AuthenticateViewModel::class.java + + override fun initLayoutView(): Int = R.layout.activity_login + + override fun setupTopBarLayout() { + + } + + override fun initData() { + // 监听数据变化,从而显示数据在页面 + viewModel.keyBean.observe(this, Observer { + if (it.isSuccess) { + Log.d(Tag, "${it.data!!.publicKey}") + val keyString = it.data!!.publicKey + val publicKey = RSAUtils.keyStrToPublicKey(keyString) + val account = userNameView.text.toString() + val userPassword = userPasswordView.text.toString() + } else { + OtherUtils.showTipsDialog(this, it.message!!, loginButton) + } + }) + // 监听加载状态变化 从而改变页面 + viewModel.loadState.observe(this, Observer { + when (it) { + is LoadState.Loading -> { + OtherUtils.showLoadingDialog(this, "登录中,请稍后") + } + else -> OtherUtils.dismissLoadingDialog() + } + }) + } + + override fun initEvent() { + val editText: EditText? = inputLayout.editText + editText?.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + + } + + override fun afterTextChanged(s: Editable) { + if (s.toString().trim { it <= ' ' }.length > 12) { + inputLayout.error = "密码长度超出限制" + } else { + inputLayout.error = null + } + } + }) + //点击输入法键盘"完成" + editText?.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? -> + if (actionId == EditorInfo.IME_ACTION_DONE) { + viewModel.obtainPublicKey() + return@setOnEditorActionListener true + } + false + } + + loginButton.setChangeAlphaWhenPress(true) + loginButton.setOnClickListener { + viewModel.obtainPublicKey() + } + + //修改服务器配置 + changeServerConfigView.setOnClickListener { + val defaultValue = SaveKeyValues.getValue( + Constant.DEFAULT_SERVER_CONFIG, + "http://111.198.10.15:12204" + ) as String + InputDialog.Builder().setContext(this) + .setTitle("请输入后台服务器地址") + .setDefaultValue(defaultValue) + .setNegativeButton("取消") + .setPositiveButton("保存") + .setOnDialogClickListener(object : InputDialog.OnDialogButtonClickListener { + override fun onButtonClick(input: String) { + SaveKeyValues.putValue(Constant.DEFAULT_SERVER_CONFIG, input) + } + }) + .build().show() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt new file mode 100644 index 0000000..98b9e9a --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/MainActivity.kt @@ -0,0 +1,21 @@ +package com.casic.birmm.inspect.view + +import com.casic.birmm.inspect.R +import com.casic.birmm.inspect.base.BaseActivity + +class MainActivity : BaseActivity() { + + override fun initLayoutView(): Int = R.layout.activity_main + + override fun setupTopBarLayout() { + + } + + override fun initData() { + + } + + override fun initEvent() { + + } +} diff --git a/app/src/main/java/com/casic/birmm/inspect/view/PermissionActivity.kt b/app/src/main/java/com/casic/birmm/inspect/view/PermissionActivity.kt new file mode 100644 index 0000000..07f48a5 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/view/PermissionActivity.kt @@ -0,0 +1,53 @@ +package com.casic.birmm.inspect.view + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.casic.birmm.inspect.utils.Constant +import pub.devrel.easypermissions.EasyPermissions +import pub.devrel.easypermissions.EasyPermissions.PermissionCallbacks + +class PermissionActivity : AppCompatActivity(), PermissionCallbacks { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + //判断是否有权限,如果版本大于5.1才需要判断(即6.0以上),其他则不需要判断。 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (EasyPermissions.hasPermissions(this, *Constant.USER_PERMISSIONS)) { + startGuideActivity() + } else { + EasyPermissions.requestPermissions( + this, + "需要获取相关权限", + Constant.PERMISSIONS_CODE, + *Constant.USER_PERMISSIONS + ) + } + } else { + startGuideActivity() + } + } + + private fun startGuideActivity() { + startActivity(Intent(this, GuideActivity::class.java)) + finish() + } + + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) + } + + override fun onPermissionsGranted(requestCode: Int, perms: List) { + startGuideActivity() + } + + override fun onPermissionsDenied(requestCode: Int, perms: List) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/birmm/inspect/vm/AuthenticateViewModel.kt b/app/src/main/java/com/casic/birmm/inspect/vm/AuthenticateViewModel.kt new file mode 100644 index 0000000..7a64c46 --- /dev/null +++ b/app/src/main/java/com/casic/birmm/inspect/vm/AuthenticateViewModel.kt @@ -0,0 +1,20 @@ +package com.casic.birmm.inspect.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.birmm.inspect.base.BaseViewModel +import com.casic.birmm.inspect.model.PublicKeyBean +import com.casic.birmm.inspect.utils.LoadState +import com.casic.birmm.inspect.utils.launch +import com.casic.birmm.inspect.utils.retrofit.RetrofitServiceManager + +class AuthenticateViewModel : BaseViewModel() { + val keyBean = MutableLiveData() + + fun obtainPublicKey() = launch({ + loadState.value = LoadState.Loading() + keyBean.value = RetrofitServiceManager.authenticate() + loadState.value = LoadState.Success() + }, { + loadState.value = LoadState.Fail() + }) +} \ No newline at end of file